views:

274

answers:

3

Hello

Can child process use the ptrace system call to trace its parent?

Os is linux 2.6

Thanks.

upd1: I want to trace process1 from "itself". It is impossible, so I do fork and try to do ptrace(process1_pid, PTRACE_ATTACH) from child process. But I can't, there is a strange error, like kernel prohibits child from tracing their parent processes

UPD2: such tracing can be prohibited by security policies. Which polices do this? Where is the checking code in the kernel?

UPD3: on my embedded linux I have no errors with PEEKDATA, but not with GETREGS:

child: getregs parent: -1
errno is 1, strerror is Operation not permitted 

errno = EPERM

+4  A: 

This question really interested me. So I wrote some code to try it out.

Firstly keep in mind, that when tracing a process, the tracing process becomes a parent for most purposes, except in name (i.e. getppid()). Firstly, a snippet of the PTRACE_ATTACH section of the manual is helpful:

   PTRACE_ATTACH
          Attaches to the process specified in pid,  making  it  a  traced
          "child"  of the calling process; the behavior of the child is as
          if it had done a PTRACE_TRACEME.  The calling  process  actually
          becomes the parent of the child process for most purposes (e.g.,
          it will receive notification of  child  events  and  appears  in
          ps(1)  output  as  the  child's parent), but a getppid(2) by the
          child will still return the PID of  the  original  parent.   The
          child  is  sent a SIGSTOP, but will not necessarily have stopped
          by the completion of this call; use  wait(2)  to  wait  for  the
          child to stop.  (addr and data are ignored.)

Now here is the code I wrote to test and verify that you can in fact ptrace() your parent (you can build this by dumping it in a file named blah.c and running make blah:

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>

int main()
{
    pid_t pid = fork();
    assert(pid != -1);
    int status;
    long readme = 0;
    if (pid)
    {
        readme = 42;
        printf("parent: child pid is %d\n", pid);
        assert(pid == wait(&status));
        printf("parent: child terminated?\n");
        assert(0 == status);
    }
    else
    {
        pid_t tracee = getppid();
        printf("child: parent pid is %d\n", tracee);
        sleep(1); // give parent time to set readme
        assert(0 == ptrace(PTRACE_ATTACH, tracee));
        assert(tracee == waitpid(tracee, &status, 0));
        printf("child: parent should be stopped\n");
        printf("child: peeking at parent: %ld\n", ptrace(PTRACE_PEEKDATA, tracee, &readme));
    }
    return 0;
}

Note that I'm exploiting the replication of the parent's virtual address space to know where to look. Also note that when the child then terminates, I suspect there's an implicit detach which must allow the parent to continue, I didn't investigate further.

Matt Joiner
Does this code works on your machine?
osgx
yes it certainly does. maybe you're running a distro that ships with some default process security policies, i think fedora does this for example. did it work for you? you should see the child peek, and print the value from the parent.
Matt Joiner
This code seems to work... On linux/x86 and on my linux (embedded)
osgx
naturally... i did actually test it :P it was very fun playing with this stuff. it may be possible you're running into the "defunct" process problem i had early on in testing this. you must carefully manage the trace childs state. it needs to be sent SIGCONT when it stops, and you must wait on the initial SIGSTOP when the attach completes..
Matt Joiner
Matt, please take look at this q: http://stackoverflow.com/questions/958369/low-overhead-way-to-access-the-memory-space-of-a-traced-processWhat do you think?
osgx
the GETREGS is disabled on my embedded linux... :( see update of this question.
osgx
@osgx: What am I looking for?
Matt Joiner
+1  A: 

Yes it is possible... Even GETREGS works. Checked on x86 (based on Matt Joiner code, thanks him)

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/user.h>

int main()
{
    pid_t pid = fork();
//    assert(pid != -1);
    int status;
    long readme = 0;
    struct user_regs_struct regs;
    if (pid)
    {
        readme = 42;
        printf("parent: child pid is %d\n", pid);
        assert(pid == wait(&status));
        printf("parent: child terminated?\n");
        assert(0 == status);
    }
    else
    {
        pid_t tracee = getppid();
        printf("child: parent pid is %d\n", tracee);
        sleep(1); // give parent time to set readme
        assert(0 == ptrace(PTRACE_ATTACH, tracee));
        assert(tracee == waitpid(tracee, &status, 0));
        printf("child: parent should be stopped\n");
        printf("child: peeking at parent: %ld\n", ptrace(PTRACE_PEEKDATA, tracee, &readme, NULL));
        printf("Regs was %p, %p, %p, %p; &status is %p \n", regs.eax, regs.ebx, regs.ecx, regs.edx, &status);
        printf("child: getregs parent: %ld\n", ptrace(PTRACE_GETREGS, tracee, NULL, &regs));
        printf("Regs is %p, %p, %p, %p; &status is %p \n", regs.eax, regs.ebx, regs.ecx, regs.edx, &status);
    }
    return 0;
}

result:

child: parent pid is 1188
parent: child pid is 1189
child: parent should be stopped
child: peeking at parent: 42
Regs was (nil), (nil), (nil), (nil); &status is 0xbfffea50
child: getregs parent: 0
Regs is 0xfffffe00, 0xffffffff, 0xbfffea50, (nil); &status is 0xbfffea50
parent: child terminated?
osgx
shameless copy of my code? :P
Matt Joiner
Yes, but GETREGS is added. I have problems with it.
osgx
osgx, try creating another question for the getregs problem
Matt Joiner
sorry, but this embedded system uses specific and not-so-wide distributed kernel. So I will examine kernel by myself. I thought that tracing parent is not permitted on all linux kernels, but there are problems only in this modified kernel and only in GETREGS.
osgx
A: 

hi, i have a doubt here ...

what does PTRACE_PEEKDATA gives us about the callstack of the process? i think it gives back address of the stack.

how do i retrieve function function signatures from there?

could you please guide?

Thanks, Sandeep

Sandeep P
Sandeep, please open new question ("Ask question" button) and mark it with ptrace tag. Secondly, take a look on `libunwind`
osgx
peekdata doesnt return address of the stack. It is returned in GETREGS
osgx
i could use ptrace successfully to trace my parent process. this is possible.
Sandeep P