Call ptrace inside a ptraced Linux process

Someone added “ptrace” to the Wikipedia article , which states that on Linux, the ptraced process cannot execute another process itself. I am trying to determine if (and if so, why) this is the case. Below is a simple program that I tried to test. My program does not work (the sub-process does not work properly), but I am pretty sure that this is my mistake, and not something fundamental.

Essentially, the initial process A processes process B , which in turn forks C. A ptraces its child B , B ptraces its child C. After they are configured, all three processes are only written to print A , B or C in stdout once per second.

In practice, what happens is that A and B work fine, but C prints only once and then gets stuck. When checking with ps -eo pid,cmd,wchan , C is displayed, stuck in the ptrace_stop kernel ptrace_stop , and the rest are in hrtimer_nanosleep , where I expect all three to be.

Very often, all three work (therefore, the program prints Cs, as well as As and Bs), which makes me believe in some conditions of the race in the initial setup.

My guesses about what might be wrong are as follows:

  • do something with A , seeing <<26> associated with B , seeing <<26> on signal C and wait (2), saying that they are like from B (but the hacker call PTRACE_CONT for both pids does not fix things) ?
  • C must be bound to B - has C inherit ptrace instead of A (and B call ptrace neither an error nor a rewrite)?

Can anyone understand what I'm doing wrong? Thanks.

 #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; } 
+6
c linux ptrace
source share
2 answers

You really see the race condition. You can cause it to repeat by setting sleep(1); just before the second fork() call.

The race condition is because process A is not signaling correctly to process B. This means that if process B starts tracking process C after process A starts trace process B, process B never receives a SIGCHLD signal indicating that process C has stopped so he can never continue it.

To fix the problem, you just need to fix your SIGCHLD handler:

 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"); } } } 
+4
source share

It is "possible" to execute some ptrace functions in a child process that ptrace itself calls. The real difficulty is that the trace process becomes the parent element of the trace when attached to the latter. And if your trace process wants to track all the behavior from all (direct and indirect) child processes (for example, when the debugger program needs to debug a multi-threaded program), it naturally destroys the original hierarchy of processes, and all interprocess / inter-threaded communications (for example, thread synchronization, signal transmission / reception ...) among all child processes must be emulated / multiplexed using the trace process. It is still "possible", but much more complex and inefficient.

0
source share

All Articles