11

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

osgx
  • 90,338
  • 53
  • 357
  • 513

2 Answers2

10

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
  • 112,946
  • 110
  • 377
  • 526
  • 1
    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 Feb 07 '10 at 14:45
  • This code seems to work... On linux/x86 and on my linux (embedded) – osgx Feb 08 '10 at 14:59
  • 1
    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 Feb 08 '10 at 15:02
  • Matt, please take look at this q: http://stackoverflow.com/questions/958369/low-overhead-way-to-access-the-memory-space-of-a-traced-process What do you think? – osgx Feb 08 '10 at 15:04
  • the GETREGS is disabled on my embedded linux... :( see update of this question. – osgx Feb 08 '10 at 15:05
  • @osgx: What am I looking for? – Matt Joiner Feb 14 '10 at 05:31
1

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
  • 90,338
  • 53
  • 357
  • 513
  • Yes, but GETREGS is added. I have problems with it. – osgx Feb 08 '10 at 15:15
  • osgx, try creating another question for the getregs problem – Matt Joiner Feb 08 '10 at 15:39
  • 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 Feb 08 '10 at 23:00