Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ptrace from multithreaded applications

I want to use ptrace to check what system calls a program spawned by my program makes. I started out from this tutorial as it was explained in an answer to my previous question. I modified the code by adapting it to the platform I'm using (SLES 11 64 bit), and put together the following test code that prints out every system call the spawned process makes:

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/reg.h>
#include <sys/syscall.h>   /* For SYS_write etc */

pid_t child;

void run()
{
    long orig_eax;
    int status;

     while(1) {
          int pid = wait(&status);
          if (pid == -1) {
              perror("wait");
              kill(child, SIGKILL);
              return;
          }
          printf("Got event from %d.\n", pid);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER,
                     pid, 8 * ORIG_RAX, NULL);
          if (orig_eax == -1) {
              perror("ptrace");
              kill(child, SIGKILL);
              return;
          } else {
              printf("Syscall %ld called.\n", orig_eax);
          }
            ptrace(PTRACE_SYSCALL,
                   pid, NULL, NULL);
    }
}

int main(int /*argc*/, char* argv[])
{

    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl(argv[1], argv[1], NULL);
    }
    else {
        printf("Child process id = %d.\n", child);
        run();

    }
    return 0;
}

It works pretty well: it prints the id of the system calls made by the program (actually it prints each one twice, once at entry and once for exit, but that doesn't matter now). However, my program needs to do other things to do other than checking the system calls, so I decided to move the checking to a separate thread (I'm more comfortable with C++ than C, so I did it the C++ way, but I don't think that matters). Of course in this thest program, I only start the thread and then join it.

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/reg.h>
#include <sys/syscall.h>   /* For SYS_write etc */

#include <boost/thread.hpp>


pid_t child;

void run()
{
    long orig_eax;
    int status;

     while(1) {
          int pid = wait(&status);
          if (pid == -1) {
              perror("wait");
              kill(child, SIGKILL);
              return;
          }
          printf("Got event from %d.\n", pid);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER,
                     pid, 8 * ORIG_RAX, NULL);
          if (orig_eax == -1) {
              perror("ptrace");
              kill(child, SIGKILL);
              return;
          } else {
              printf("Syscall %ld called.\n", orig_eax);
          }
            ptrace(PTRACE_SYSCALL,
                   pid, NULL, NULL);
    }
}

int main(int /*argc*/, char* argv[])
{

    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl(argv[1], argv[1], NULL);
    }
    else {
        printf("Child process id = %d.\n", child);
        boost::thread t(run);
        t.join();
    }
    return 0;
}

This time I get an error message:

Child process id = 24682.
Got event from 24682.
ptrace: No such process

Why is this? I tried searching for an answer but found nothing like this. I found that ptrace won't trace threads started by the child process, but that's another thing needs to be dealed with later. Is that even possible to check the child process from a different therad?

The other strange thing is that in my real application I do basically the same thing (but from a much more complicated context: classes, mutexes etc.), and I get a different kind of error. Instead of ptrace returning with an error, wait doesn't even return for system calls on the child process (and the child process doesn't even stop). On the other hand, wait works as expected when the child process exits.

like image 457
petersohn Avatar asked Nov 13 '22 17:11

petersohn


1 Answers

As far as I can tell, ptrace allows just one tracer per process. This means that if you try to attach, which you can try and force it with PTRACE_ATTACH, you will receive an error, telling that ptrace was not able to attach to the specified process.

Thus, your error appears because your thread is not attached to the child process, and this way, when you try to ptrace it, it fails, sending a -ESRCH code.

Furthermore, you can have a look at this post here, it might answer some other questions you might have apart from this one.

like image 124
kosklain Avatar answered Nov 15 '22 07:11

kosklain