вызов ptrace внутри процесса ptraced Linux - PullRequest
6 голосов
/ 02 марта 2010

Кто-то добавил в статью в Википедии "ptrace" , утверждая, что в Linux процесс ptraced сам по себе не может отслеживать другой процесс. Я пытаюсь определить, если (и если да, то почему) это так. Ниже приведена простая программа, которую я разработал, чтобы проверить это. Моя программа не работает (подпроцесс не работает должным образом), но я вполне уверен, что это моя ошибка, а не что-то фундаментальное.

По сути, первоначальный процесс A разветвляется процесс B , который в свою очередь разветвляется C . A отслеживает своего потомка B , B отслеживает своего потомка C . Как только они настроены, все три процесса записываются так, чтобы просто печатать A, B или C в стандартный вывод раз в секунду.

На практике получается, что A и B работают нормально, но C печатает только один раз, а затем застревает. Проверка с помощью ps -eo pid,cmd,wchan показывает C , застрявшую в функции ядра ptrace_stop, а остальные находятся в hrtimer_nanosleep, где я ожидаю, что все три будут.

Очень редко все трое работают (поэтому программа печатает Cs, а также As и Bs), что наводит меня на мысль, что в начальной настройке есть какое-то состояние гонки.

Мои догадываются о том, что может быть не так:

  • что-то делать с A , видя SIGCHLD, относящееся к B , видя SIGCHLD, имеющего отношение к сигналу C , и ждать ( 2) сообщать обоим как о B (но хакерский вызов PTRACE_CONT для обоих пидов не исправляет ситуацию)?
  • C должен быть выведен как B - имеет C унаследовал ptrace вместо A B вызов ptrace не ошибся и не переписал это)?

Кто-нибудь может понять, что я делаю не так? Спасибо.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

static void a(){
  while(1){
    printf ("A\n");
    fflush(stdout);
    sleep(1);
  }
}

static void b(){
  while(1){
    printf ("B\n");
    fflush(stdout);
    sleep(1);
  }
}

static void c(){
  while(1){
    printf ("C\n");
    fflush(stdout);
    sleep(1);
  }
}

static void sigchld_handler(int sig){
  int result;
  pid_t child_pid = wait(NULL); // find who send us this SIGCHLD

  printf("SIGCHLD on %d\n", child_pid);
  result=ptrace(PTRACE_CONT, child_pid, sig, NULL);
  if(result) {
    perror("continuing after SIGCHLD");
  }
}

int main(int  argc,
         char **argv){

  pid_t mychild_pid;
  int   result;

  printf("pidA = %d\n", getpid());

  signal(SIGCHLD, sigchld_handler);

  mychild_pid = fork();

  if (mychild_pid) {
    printf("pidB = %d\n", mychild_pid);
    result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
    if(result==-1){
      perror("outer ptrace");
    }
    a();
  }
  else {
    mychild_pid = fork();

    if (mychild_pid) {
      printf("pidC = %d\n", mychild_pid);

      result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
      if(result==-1){
        perror("inner ptrace");
      }
      b();
    }
    else {
      c();
    }
  }

  return 0;
}

Ответы [ 2 ]

5 голосов
/ 02 марта 2010

Вы действительно видите состояние гонки. Вы можете сделать это многократно, поставив sleep(1); непосредственно перед вызовом second fork().

Состояние гонки вызвано тем, что процесс A неправильно передает сигналы процессу B. Это означает, что если процесс B начинает процесс отслеживания C после того, как процесс A начал процесс B, процесс B никогда не получает сигнал SIGCHLD, указывающий этот процесс C остановлен, поэтому он никогда не сможет его продолжить.

Чтобы устранить проблему, вам просто нужно исправить обработчик SIGCHLD:

static void sigchld_handler(int sig){
    int result, status;
    pid_t child_pid = wait(&status); // find who send us this SIGCHLD

    printf("%d received SIGCHLD on %d\n", getpid(), child_pid);
    if (WIFSTOPPED(status))
    {
        result=ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
        if(result) {
            perror("continuing after SIGCHLD");
        }
    }
}
0 голосов
/ 29 ноября 2011

«Возможно» выполнить некоторые функции ptrace для дочернего процесса, который вызывает сам ptrace. Реальная трудность состоит в том, что процесс трассировки становится родителем трассы, когда присоединен к ней. И если ваш трассировочный процесс хочет отследить все поведения от всех (прямых и косвенных) дочерних процессов (то есть, например, когда программе отладчика требуется отладка многопоточной программы), он естественным образом нарушает исходную иерархию процессов и все межпроцессные / межпотоковая связь (т. е. синхронизация потоков, отправка / прием сигналов, ...) между всеми дочерними процессами должна быть эмулирована / мультиплексирована процессом трассировки. Это все еще «возможно», но гораздо сложнее и неэффективно.

...