0

I'm trying to follow the syscalls of a program using ptrace, but it does not work when the traced program has a fork() and just ignores it, supposedly you just need to set the line below and it should follow the process originating from fork() as well.

ptrace(PTRACE_SETOPTIONS, child, NULL, PTRACE_O_TRACEFORK); I'm posting my code below, for tracer.c:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <unistd.h>
#include <errno.h>
#include <sys/user.h>

int main(int argc, char* argv[]){
    if (argc < 2) {
        printf("Usage: %s <program> [args...]\n", argv[0]);
        return 1;
    }

    pid_t child;
    struct user_regs_struct regs;
    int status;
    int notPrinted = 1;

    child = fork();

    if (child == 0){
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0){
            perror("ptrace");
            return 1;
        }
        execvp(argv[1], &argv[1]);
    }
    else if (child > 0){
        wait(&status);

        ptrace(PTRACE_SETOPTIONS, child, NULL, PTRACE_SETOPTIONS | PTRACE_O_TRACEFORK | PTRACE_O_TRACESYSGOOD);
        while (WIFSTOPPED(status)){
            ptrace(PTRACE_GETREGS, child, NULL, &regs);

            if(notPrinted){
                if (regs.orig_rax != -1){
                    notPrinted = 0;
                    printf("FROM: %d, Syscall %ld: rdi=%ld, rsi=%ld, rdx=%ld, r10=%ld\n",
                        child, regs.orig_rax, regs.rbx, regs.rcx, regs.rdx, regs.r10);
                }
            }
            else{
                notPrinted = 1;
            }
            if (ptrace(PTRACE_SYSCALL, child, NULL, NULL) < 0){
                perror("ptrace");
                return 1;
            }
            wait(&status);
        }
    }
    else{
        perror("fork");
        return 1;
    }
    return 0;
}

As well as a simple program I've been using to test, random.c:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {

    if(fork() == 0){
        sleep(1);
    }
    srand(time(NULL));

    for (int i = 0; i < 30; i++) {
        printf("%d ", rand() & 0xf);
    }
    printf("\n");
    sleep(5);
    return 0;
}

Previously I had the following line with just set option to "PTRACE_O_TRACEFORK"; So i believe the issue is still a wrong configuration of PTRACE_SETOPTIONS; my goal is to have the program follow a program that uses fork() correctly, like strace -f option does

ptrace(PTRACE_SETOPTIONS, child, NULL, PTRACE_SETOPTIONS | PTRACE_O_TRACEFORK | PTRACE_O_TRACESYSGOOD);

EDIT:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char* argv[]){
    if (argc < 2){
        printf("Usage: %s <program> [args...]\n", argv[0]);
        return 1;
    }

    pid_t child;
    struct user_regs_struct regs;
    int status;

    child = fork();

    if (child == 0){
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
            perror("ptrace");
            return 1;
        }
        execvp(argv[1], &argv[1]);
    } else if (child > 0){
        child = wait(&status);
        ptrace(PTRACE_SETOPTIONS, child, NULL, PTRACE_SETOPTIONS | PTRACE_O_TRACEFORK | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE);

        while (WIFSTOPPED(status)){
            ptrace(PTRACE_GETREGS, child, NULL, &regs);

            if (regs.orig_rax != -1){
                printf("FROM: %d, Syscall %ld: rdi=%ld, rsi=%ld, rdx=%ld, r10=%ld\n",
                        child, regs.orig_rax, regs.rbx, regs.rcx, regs.rdx, regs.r10);
            }
            if (ptrace(PTRACE_SYSCALL, child, NULL, NULL) < 0){
                perror("ptrace");
                return 1;
            }

            child = wait(&status);
        }
    } else{
        perror("fork");
        return 1;
    }

    return 0;
}

This is the new code with PTRACE_O_TRACECLONE option included, removed notPrinted just for code readability, and with updated child by doing

child = wait(&status);
ptrace(PTRACE_GETREGS, child, NULL, &regs);

to get updated child, and then to read the registers from the process; But I'm still only getting one process as child, any idea why so?

frazz
  • 1
  • 2

2 Answers2

1

A few issues ...

  1. You need to [also] add PTRACE_O_TRACECLONE when you set options. You're doing fork but that's the libc function. (e.g. Under linux) it uses the clone syscall).

  2. You have to do ptrace(PTRACE_CONT, pid, 0, 0); to resume the child after the tracer processes the stop.

  3. If you're going to trace an app that calls fork and you want to also trace any children it created, you have to use the correct pid. You never change the value of child, so you never get the pid of any grandchildren.

  4. You have to do: child = wait(&status); When you're tracing multiple children, you have to know which pid caused the return from wait.


For some additional info, you could look at some of my ptrace answers:

  1. Ptrace options not working in parent process

UPDATE:

I have edited the code if you could please check again. I have added PTRACE_O_TRACECLONE, I did not add ptrace(PTRACE_CONT, pid, 0, 0); because I believe ptrace(PTRACE_SYSCALL, child, NULL, NULL) already does that and PTRACE_CONT is not required, and changed to record child_pid, but im not getting the grandchild, any ideas on what else I can do? – frazz

Yes, PTRACE_SYSCALL is sufficient.

I reworked your latest source. In the process, I discovered the bug.

Your PTRACE_SETOPTIONS call is incorrect. You are doing:

ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_SETOPTIONS |
    PTRACE_O_TRACEFORK | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE);

You are ORing in PTRACE_SETOPTIONS into the last argument. This argument should only have PTRACE_O_* options in it.

This [somehow] breaks things. The correct way is:

ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEFORK |
    PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE);

I added some extra code to have a per-process control struct to keep track of the state of each process being traced. While not strictly necessary for your example, it is a common thing to do.

I also added a log file for tracing.

And, I refactored your random a bit to be slightly more trace friendly (not necessary but helped spot the issue).

Here is the tracer:

#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <unistd.h>
#include <errno.h>
#include <syscall.h>

FILE *xflog;
int opt_bug;                                // 1=show bug
int opt_v;                                  // 1=verbose dump mode
char *opt_L;                                // logfile name
char *pgmname;

#define logf(_fmt...) \
    fprintf(xflog,_fmt)

#if 0
#define DREG(_reg) \
    logf(" " #_reg "=%16.16llX/%llu\n",regs._reg,regs._reg)
#else
#define DREG(_reg) \
    logf(" " #_reg "=%16.16llX\n",regs._reg)
#endif

struct pidctl {
    struct pidctl *ctl_next;                // linked list
    pid_t ctl_pid;                          // process id
    int ctl_xid;                            // incremental id (e.g. 1, 2, 3)
    unsigned int ctl_seqno;                 // event sequence number
    int ctl_status;                         // status from last wait call
    int ctl_signo;                          // signal number from last call
    int ctl_mode;                           // mode (0=enter, 1=exit) syscall
};

struct pidctl *pidlist;                     // list of active processes
int pidcount;                               // number of entries in pidlist
int pidxid;                                 // incremental id

#if 1
#define WAITFOR(_status)        wait(_status)
#else
#define WAITFOR(_status)        waitpid(-1,_status,0)
#endif

const char *sysname(int sysno);

// pidfind -- find control for given pid
struct pidctl *
pidfind(pid_t pid,int status)
{
    struct pidctl *prev = NULL;
    struct pidctl *cur = pidlist;

    // find existing entry
    for (;  cur != NULL;  cur = cur->ctl_next) {
        if (cur->ctl_pid == pid)
            break;
        prev = cur;
    }

    do {
        // already exists
        if (cur != NULL)
            break;

        // create new entry
        cur = calloc(1,sizeof(*cur));
        cur->ctl_pid = pid;
        cur->ctl_xid = pidxid++;
        logf("pidfind: NEWPID ctl_xid=%d ctl_pid=%8.8X/%d\n",
            cur->ctl_xid,cur->ctl_pid,cur->ctl_pid);

        // start in "AFT" mode
        cur->ctl_mode = 1;

        if (prev != NULL)
            prev->ctl_next = cur;
        else
            pidlist = cur;

        ++pidcount;
    } while (0);

    cur->ctl_status = status;

    if (WIFSTOPPED(status))
        cur->ctl_signo = WSTOPSIG(status);
    else
        cur->ctl_signo = 0;

    return cur;
}

void
pidsetup(pid_t pid)
{

// NOTE/BUG: PTRACE_SETOPTIONS must _not_ be or'ed in with PTRACE_O_*
    if (opt_bug)
        ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_SETOPTIONS |
            PTRACE_O_TRACEFORK | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE);
    else
        ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEFORK |
            PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE);
}

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

    pgmname = *argv;
    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 'b':
            opt_bug = ! opt_bug;
            break;
        case 'v':
            opt_v = ! opt_v;
            break;
        case 'L':
            opt_L = (*cp != 0) ? cp : NULL;
            break;
        }
    }

    if (argc < 1) {
        printf("Usage: %s <program> [args...]\n", pgmname);
        return 1;
    }

    pid_t child;
    struct user_regs_struct regs;
    int status;

    if (opt_L == NULL)
        opt_L = "LOG";
    xflog = fopen(opt_L,"w");
    setlinebuf(xflog);

    child = fork();
    if (child < 0) {
        perror("fork");
        return 1;
    }

    if (child == 0) {
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
            perror("ptrace");
            return 1;
        }
        execvp(argv[0], &argv[0]);
        perror("execvp");
        exit(9);
    }

    struct pidctl *ctl;

    child = WAITFOR(&status);

#if 1
    ctl = pidfind(child,status);
#endif

    pidsetup(child);

    unsigned int seqno = 0;
    int sysno;
    int exit_count = 0;

    while (pidcount > 0) {
        logf("\n");
        ctl = pidfind(child,status);

        if (WIFEXITED(status)) {
            logf("EXIT: %d status=%8.8X\n",ctl->ctl_xid,status);
            --pidcount;
            if (pidcount <= 0)
                break;
            continue;
        }

        ptrace(PTRACE_GETREGS, child, NULL, &regs);

        sysno = regs.orig_rax;

        do {
#if 0
            if (sysno == -1)
                break;
#endif

#if 0
            logf("FROM: %d, Syscall %ld: rdi=%ld, rsi=%ld, rdx=%ld, r10=%ld\n",
                child, regs.orig_rax, regs.rbx, regs.rcx, regs.rdx, regs.r10);
#else
            logf("%u: %d %s %d/%s\n",
                seqno,ctl->ctl_xid,
                ctl->ctl_mode ? "AFT" : "BEF",
                sysno,sysname(sysno));
            if (opt_v) {
                DREG(rdi);
                DREG(rsi);
                DREG(rdx);
                DREG(rcx);
                DREG(r10);
            }
            DREG(rax);
#endif
        } while (0);

        // wait for exit from all ones we're tracing
        if (sysno == __NR_exit_group) {
            if (++exit_count == pidcount)
                break;
        }

        if (ptrace(PTRACE_SYSCALL, child, NULL, NULL) < 0) {
            perror("ptrace");
            return 1;
        }

        ctl->ctl_seqno = seqno++;
        ctl->ctl_mode = ! ctl->ctl_mode;

        child = WAITFOR(&status);
    }

    fclose(xflog);

    return 0;
}

#define NRALL(_cmd) \
    _cmd(read) \
    _cmd(write) \
    _cmd(open) \
    _cmd(close) \
    _cmd(stat) \
    _cmd(fstat) \
    _cmd(lstat) \
    _cmd(poll) \
    _cmd(lseek) \
    _cmd(mmap) \
    _cmd(mprotect) \
    _cmd(munmap) \
    _cmd(brk) \
    _cmd(rt_sigaction) \
    _cmd(rt_sigprocmask) \
    _cmd(rt_sigreturn) \
    _cmd(ioctl) \
    _cmd(pread64) \
    _cmd(pwrite64) \
    _cmd(readv) \
    _cmd(writev) \
    _cmd(access) \
    _cmd(pipe) \
    _cmd(select) \
    _cmd(sched_yield) \
    _cmd(mremap) \
    _cmd(msync) \
    _cmd(mincore) \
    _cmd(madvise) \
    _cmd(shmget) \
    _cmd(shmat) \
    _cmd(shmctl) \
    _cmd(dup) \
    _cmd(dup2) \
    _cmd(pause) \
    _cmd(nanosleep) \
    _cmd(getitimer) \
    _cmd(alarm) \
    _cmd(setitimer) \
    _cmd(getpid) \
    _cmd(sendfile) \
    _cmd(socket) \
    _cmd(connect) \
    _cmd(accept) \
    _cmd(sendto) \
    _cmd(recvfrom) \
    _cmd(sendmsg) \
    _cmd(recvmsg) \
    _cmd(shutdown) \
    _cmd(bind) \
    _cmd(listen) \
    _cmd(getsockname) \
    _cmd(getpeername) \
    _cmd(socketpair) \
    _cmd(setsockopt) \
    _cmd(getsockopt) \
    _cmd(clone) \
    _cmd(fork) \
    _cmd(vfork) \
    _cmd(execve) \
    _cmd(exit) \
    _cmd(wait4) \
    _cmd(kill) \
    _cmd(uname) \
    _cmd(semget) \
    _cmd(semop) \
    _cmd(semctl) \
    _cmd(shmdt) \
    _cmd(msgget) \
    _cmd(msgsnd) \
    _cmd(msgrcv) \
    _cmd(msgctl) \
    _cmd(fcntl) \
    _cmd(flock) \
    _cmd(fsync) \
    _cmd(fdatasync) \
    _cmd(truncate) \
    _cmd(ftruncate) \
    _cmd(getdents) \
    _cmd(getcwd) \
    _cmd(chdir) \
    _cmd(fchdir) \
    _cmd(rename) \
    _cmd(mkdir) \
    _cmd(rmdir) \
    _cmd(creat) \
    _cmd(link) \
    _cmd(unlink) \
    _cmd(symlink) \
    _cmd(readlink) \
    _cmd(chmod) \
    _cmd(fchmod) \
    _cmd(chown) \
    _cmd(fchown) \
    _cmd(lchown) \
    _cmd(umask) \
    _cmd(gettimeofday) \
    _cmd(getrlimit) \
    _cmd(getrusage) \
    _cmd(sysinfo) \
    _cmd(times) \
    _cmd(ptrace) \
    _cmd(getuid) \
    _cmd(syslog) \
    _cmd(getgid) \
    _cmd(setuid) \
    _cmd(setgid) \
    _cmd(geteuid) \
    _cmd(getegid) \
    _cmd(setpgid) \
    _cmd(getppid) \
    _cmd(getpgrp) \
    _cmd(setsid) \
    _cmd(setreuid) \
    _cmd(setregid) \
    _cmd(getgroups) \
    _cmd(setgroups) \
    _cmd(setresuid) \
    _cmd(getresuid) \
    _cmd(setresgid) \
    _cmd(getresgid) \
    _cmd(getpgid) \
    _cmd(setfsuid) \
    _cmd(setfsgid) \
    _cmd(getsid) \
    _cmd(capget) \
    _cmd(capset) \
    _cmd(rt_sigpending) \
    _cmd(rt_sigtimedwait) \
    _cmd(rt_sigqueueinfo) \
    _cmd(rt_sigsuspend) \
    _cmd(sigaltstack) \
    _cmd(utime) \
    _cmd(mknod) \
    _cmd(uselib) \
    _cmd(personality) \
    _cmd(ustat) \
    _cmd(statfs) \
    _cmd(fstatfs) \
    _cmd(sysfs) \
    _cmd(getpriority) \
    _cmd(setpriority) \
    _cmd(sched_setparam) \
    _cmd(sched_getparam) \
    _cmd(sched_setscheduler) \
    _cmd(sched_getscheduler) \
    _cmd(sched_get_priority_max) \
    _cmd(sched_get_priority_min) \
    _cmd(sched_rr_get_interval) \
    _cmd(mlock) \
    _cmd(munlock) \
    _cmd(mlockall) \
    _cmd(munlockall) \
    _cmd(vhangup) \
    _cmd(modify_ldt) \
    _cmd(pivot_root) \
    _cmd(_sysctl) \
    _cmd(prctl) \
    _cmd(arch_prctl) \
    _cmd(adjtimex) \
    _cmd(setrlimit) \
    _cmd(chroot) \
    _cmd(sync) \
    _cmd(acct) \
    _cmd(settimeofday) \
    _cmd(mount) \
    _cmd(umount2) \
    _cmd(swapon) \
    _cmd(swapoff) \
    _cmd(reboot) \
    _cmd(sethostname) \
    _cmd(setdomainname) \
    _cmd(iopl) \
    _cmd(ioperm) \
    _cmd(create_module) \
    _cmd(init_module) \
    _cmd(delete_module) \
    _cmd(get_kernel_syms) \
    _cmd(query_module) \
    _cmd(quotactl) \
    _cmd(nfsservctl) \
    _cmd(getpmsg) \
    _cmd(putpmsg) \
    _cmd(afs_syscall) \
    _cmd(tuxcall) \
    _cmd(security) \
    _cmd(gettid) \
    _cmd(readahead) \
    _cmd(setxattr) \
    _cmd(lsetxattr) \
    _cmd(fsetxattr) \
    _cmd(getxattr) \
    _cmd(lgetxattr) \
    _cmd(fgetxattr) \
    _cmd(listxattr) \
    _cmd(llistxattr) \
    _cmd(flistxattr) \
    _cmd(removexattr) \
    _cmd(lremovexattr) \
    _cmd(fremovexattr) \
    _cmd(tkill) \
    _cmd(time) \
    _cmd(futex) \
    _cmd(sched_setaffinity) \
    _cmd(sched_getaffinity) \
    _cmd(set_thread_area) \
    _cmd(io_setup) \
    _cmd(io_destroy) \
    _cmd(io_getevents) \
    _cmd(io_submit) \
    _cmd(io_cancel) \
    _cmd(get_thread_area) \
    _cmd(lookup_dcookie) \
    _cmd(epoll_create) \
    _cmd(epoll_ctl_old) \
    _cmd(epoll_wait_old) \
    _cmd(remap_file_pages) \
    _cmd(getdents64) \
    _cmd(set_tid_address) \
    _cmd(restart_syscall) \
    _cmd(semtimedop) \
    _cmd(fadvise64) \
    _cmd(timer_create) \
    _cmd(timer_settime) \
    _cmd(timer_gettime) \
    _cmd(timer_getoverrun) \
    _cmd(timer_delete) \
    _cmd(clock_settime) \
    _cmd(clock_gettime) \
    _cmd(clock_getres) \
    _cmd(clock_nanosleep) \
    _cmd(exit_group) \
    _cmd(epoll_wait) \
    _cmd(epoll_ctl) \
    _cmd(tgkill) \
    _cmd(utimes) \
    _cmd(vserver) \
    _cmd(mbind) \
    _cmd(set_mempolicy) \
    _cmd(get_mempolicy) \
    _cmd(mq_open) \
    _cmd(mq_unlink) \
    _cmd(mq_timedsend) \
    _cmd(mq_timedreceive) \
    _cmd(mq_notify) \
    _cmd(mq_getsetattr) \
    _cmd(kexec_load) \
    _cmd(waitid) \
    _cmd(add_key) \
    _cmd(request_key) \
    _cmd(keyctl) \
    _cmd(ioprio_set) \
    _cmd(ioprio_get) \
    _cmd(inotify_init) \
    _cmd(inotify_add_watch) \
    _cmd(inotify_rm_watch) \
    _cmd(migrate_pages) \
    _cmd(openat) \
    _cmd(mkdirat) \
    _cmd(mknodat) \
    _cmd(fchownat) \
    _cmd(futimesat) \
    _cmd(newfstatat) \
    _cmd(unlinkat) \
    _cmd(renameat) \
    _cmd(linkat) \
    _cmd(symlinkat) \
    _cmd(readlinkat) \
    _cmd(fchmodat) \
    _cmd(faccessat) \
    _cmd(pselect6) \
    _cmd(ppoll) \
    _cmd(unshare) \
    _cmd(set_robust_list) \
    _cmd(get_robust_list) \
    _cmd(splice) \
    _cmd(tee) \
    _cmd(sync_file_range) \
    _cmd(vmsplice) \
    _cmd(move_pages) \
    _cmd(utimensat) \
    _cmd(epoll_pwait) \
    _cmd(signalfd) \
    _cmd(timerfd_create) \
    _cmd(eventfd) \
    _cmd(fallocate) \
    _cmd(timerfd_settime) \
    _cmd(timerfd_gettime) \
    _cmd(accept4) \
    _cmd(signalfd4) \
    _cmd(eventfd2) \
    _cmd(epoll_create1) \
    _cmd(dup3) \
    _cmd(pipe2) \
    _cmd(inotify_init1) \
    _cmd(preadv) \
    _cmd(pwritev) \
    _cmd(rt_tgsigqueueinfo) \
    _cmd(perf_event_open) \
    _cmd(recvmmsg) \
    _cmd(fanotify_init) \
    _cmd(fanotify_mark) \
    _cmd(prlimit64) \
    _cmd(name_to_handle_at) \
    _cmd(open_by_handle_at) \
    _cmd(clock_adjtime) \
    _cmd(syncfs) \
    _cmd(sendmmsg) \
    _cmd(setns) \
    _cmd(getcpu) \
    _cmd(process_vm_readv) \
    _cmd(process_vm_writev) \
    _cmd(kcmp) \
    _cmd(finit_module) \
    _cmd(sched_setattr) \
    _cmd(sched_getattr) \
    _cmd(renameat2) \
    _cmd(seccomp) \
    _cmd(getrandom) \
    _cmd(memfd_create) \
    _cmd(kexec_file_load) \
    _cmd(bpf) \
    _cmd(execveat) \
    _cmd(userfaultfd) \
    _cmd(membarrier) \
    _cmd(mlock2) \
    _cmd(copy_file_range) \
    _cmd(preadv2) \
    _cmd(pwritev2) \
    _cmd(pkey_mprotect) \
    _cmd(pkey_alloc) \
    _cmd(pkey_free) \
    _cmd(statx) \
    _cmd(io_pgetevents) \
    _cmd(rseq) \
    _cmd(pidfd_send_signal) \
    _cmd(io_uring_setup) \
    _cmd(io_uring_enter) \
    _cmd(io_uring_register) \
    _cmd(open_tree) \
    _cmd(move_mount) \
    _cmd(fsopen) \
    _cmd(fsconfig) \
    _cmd(fsmount) \
    _cmd(fspick) \
    _cmd(pidfd_open) \
    _cmd(clone3)

#define NRSTR(_sym) \
    [__NR_##_sym] = #_sym,
const char *NR_name[] = {
    NRALL(NRSTR)
};

const char *
sysname(int sysno)
{

    return NR_name[sysno];
}

Here is the test program:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/wait.h>

int opt_w;                              // 1=wait for child

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

    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 'w':
            opt_w = ! opt_w;
            break;
        }
    }

    int xid = 0;

    pid_t child = fork();

    if (child == 0) {
        sleep(1);
        ++xid;
    }
    srand(time(NULL));

    pid_t pid = getpid();

    setlinebuf(stdout);
    printf("%d: pid=%d\n",xid,pid);

    for (int i = 0; i < 5; i++) {
        printf("%d: %d\n", xid, rand() & 0xf);
    }

    sleep(5);

    if (opt_w && (child != 0))
        wait(NULL);

    return 0;
}

Here is the LOG file for the "buggy" case (e.g. -b). Notice that there is only one process traced and that there are no clone syscalls.

pidfind: NEWPID ctl_xid=0 ctl_pid=00150066/1376358

0: 0 AFT 59/execve
 rax=0000000000000000

1: 0 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

2: 0 AFT 12/brk
 rax=000000000159F000

3: 0 BEF 158/arch_prctl
 rax=FFFFFFFFFFFFFFDA

4: 0 AFT 158/arch_prctl
 rax=FFFFFFFFFFFFFFEA

5: 0 BEF 21/access
 rax=FFFFFFFFFFFFFFDA

6: 0 AFT 21/access
 rax=FFFFFFFFFFFFFFFE

7: 0 BEF 257/openat
 rax=FFFFFFFFFFFFFFDA

8: 0 AFT 257/openat
 rax=0000000000000004

9: 0 BEF 5/fstat
 rax=FFFFFFFFFFFFFFDA

10: 0 AFT 5/fstat
 rax=0000000000000000

11: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

12: 0 AFT 9/mmap
 rax=00007FC42DFE8000

13: 0 BEF 3/close
 rax=FFFFFFFFFFFFFFDA

14: 0 AFT 3/close
 rax=0000000000000000

15: 0 BEF 257/openat
 rax=FFFFFFFFFFFFFFDA

16: 0 AFT 257/openat
 rax=0000000000000004

17: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

18: 0 AFT 0/read
 rax=0000000000000340

19: 0 BEF 8/lseek
 rax=FFFFFFFFFFFFFFDA

20: 0 AFT 8/lseek
 rax=0000000000000318

21: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

22: 0 AFT 0/read
 rax=0000000000000044

23: 0 BEF 5/fstat
 rax=FFFFFFFFFFFFFFDA

24: 0 AFT 5/fstat
 rax=0000000000000000

25: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

26: 0 AFT 9/mmap
 rax=00007FC42DFE6000

27: 0 BEF 8/lseek
 rax=FFFFFFFFFFFFFFDA

28: 0 AFT 8/lseek
 rax=0000000000000318

29: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

30: 0 AFT 0/read
 rax=0000000000000044

31: 0 BEF 8/lseek
 rax=FFFFFFFFFFFFFFDA

32: 0 AFT 8/lseek
 rax=0000000000000360

33: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

34: 0 AFT 0/read
 rax=0000000000000020

35: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

36: 0 AFT 9/mmap
 rax=00007FC42DE20000

37: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

38: 0 AFT 10/mprotect
 rax=0000000000000000

39: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

40: 0 AFT 9/mmap
 rax=00007FC42DE42000

41: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

42: 0 AFT 9/mmap
 rax=00007FC42DF8F000

43: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

44: 0 AFT 9/mmap
 rax=00007FC42DFDC000

45: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

46: 0 AFT 9/mmap
 rax=00007FC42DFE2000

47: 0 BEF 3/close
 rax=FFFFFFFFFFFFFFDA

48: 0 AFT 3/close
 rax=0000000000000000

49: 0 BEF 158/arch_prctl
 rax=FFFFFFFFFFFFFFDA

50: 0 AFT 158/arch_prctl
 rax=0000000000000000

51: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

52: 0 AFT 10/mprotect
 rax=0000000000000000

53: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

54: 0 AFT 10/mprotect
 rax=0000000000000000

55: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

56: 0 AFT 10/mprotect
 rax=0000000000000000

57: 0 BEF 11/munmap
 rax=FFFFFFFFFFFFFFDA

58: 0 AFT 11/munmap
 rax=0000000000000000

59: 0 BEF 56/clone
 rax=FFFFFFFFFFFFFFDA

60: 0 AFT 56/clone
 rax=0000000000150067

61: 0 BEF 39/getpid
 rax=FFFFFFFFFFFFFFDA

62: 0 AFT 39/getpid
 rax=0000000000150066

63: 0 BEF 5/fstat
 rax=FFFFFFFFFFFFFFDA

64: 0 AFT 5/fstat
 rax=0000000000000000

65: 0 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

66: 0 AFT 12/brk
 rax=000000000159F000

67: 0 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

68: 0 AFT 12/brk
 rax=00000000015C0000

69: 0 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

70: 0 AFT 12/brk
 rax=00000000015C0000

71: 0 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

72: 0 AFT 1/write
 rax=000000000000000F

73: 0 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

74: 0 AFT 1/write
 rax=0000000000000005

75: 0 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

76: 0 AFT 1/write
 rax=0000000000000006

77: 0 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

78: 0 AFT 1/write
 rax=0000000000000005

79: 0 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

80: 0 AFT 1/write
 rax=0000000000000005

81: 0 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

82: 0 AFT 1/write
 rax=0000000000000005

83: 0 BEF 35/nanosleep
 rax=FFFFFFFFFFFFFFDA

84: 0 AFT 35/nanosleep
 rax=0000000000000000

85: 0 BEF 231/exit_group
 rax=FFFFFFFFFFFFFFDA

Here is the LOG for the "fixed" case:

pidfind: NEWPID ctl_xid=0 ctl_pid=0015006B/1376363

0: 0 AFT 59/execve
 rax=0000000000000000

1: 0 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

2: 0 AFT 12/brk
 rax=0000000000F4D000

3: 0 BEF 158/arch_prctl
 rax=FFFFFFFFFFFFFFDA

4: 0 AFT 158/arch_prctl
 rax=FFFFFFFFFFFFFFEA

5: 0 BEF 21/access
 rax=FFFFFFFFFFFFFFDA

6: 0 AFT 21/access
 rax=FFFFFFFFFFFFFFFE

7: 0 BEF 257/openat
 rax=FFFFFFFFFFFFFFDA

8: 0 AFT 257/openat
 rax=0000000000000004

9: 0 BEF 5/fstat
 rax=FFFFFFFFFFFFFFDA

10: 0 AFT 5/fstat
 rax=0000000000000000

11: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

12: 0 AFT 9/mmap
 rax=00007FEE5BF30000

13: 0 BEF 3/close
 rax=FFFFFFFFFFFFFFDA

14: 0 AFT 3/close
 rax=0000000000000000

15: 0 BEF 257/openat
 rax=FFFFFFFFFFFFFFDA

16: 0 AFT 257/openat
 rax=0000000000000004

17: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

18: 0 AFT 0/read
 rax=0000000000000340

19: 0 BEF 8/lseek
 rax=FFFFFFFFFFFFFFDA

20: 0 AFT 8/lseek
 rax=0000000000000318

21: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

22: 0 AFT 0/read
 rax=0000000000000044

23: 0 BEF 5/fstat
 rax=FFFFFFFFFFFFFFDA

24: 0 AFT 5/fstat
 rax=0000000000000000

25: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

26: 0 AFT 9/mmap
 rax=00007FEE5BF2E000

27: 0 BEF 8/lseek
 rax=FFFFFFFFFFFFFFDA

28: 0 AFT 8/lseek
 rax=0000000000000318

29: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

30: 0 AFT 0/read
 rax=0000000000000044

31: 0 BEF 8/lseek
 rax=FFFFFFFFFFFFFFDA

32: 0 AFT 8/lseek
 rax=0000000000000360

33: 0 BEF 0/read
 rax=FFFFFFFFFFFFFFDA

34: 0 AFT 0/read
 rax=0000000000000020

35: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

36: 0 AFT 9/mmap
 rax=00007FEE5BD68000

37: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

38: 0 AFT 10/mprotect
 rax=0000000000000000

39: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

40: 0 AFT 9/mmap
 rax=00007FEE5BD8A000

41: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

42: 0 AFT 9/mmap
 rax=00007FEE5BED7000

43: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

44: 0 AFT 9/mmap
 rax=00007FEE5BF24000

45: 0 BEF 9/mmap
 rax=FFFFFFFFFFFFFFDA

46: 0 AFT 9/mmap
 rax=00007FEE5BF2A000

47: 0 BEF 3/close
 rax=FFFFFFFFFFFFFFDA

48: 0 AFT 3/close
 rax=0000000000000000

49: 0 BEF 158/arch_prctl
 rax=FFFFFFFFFFFFFFDA

50: 0 AFT 158/arch_prctl
 rax=0000000000000000

51: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

52: 0 AFT 10/mprotect
 rax=0000000000000000

53: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

54: 0 AFT 10/mprotect
 rax=0000000000000000

55: 0 BEF 10/mprotect
 rax=FFFFFFFFFFFFFFDA

56: 0 AFT 10/mprotect
 rax=0000000000000000

57: 0 BEF 11/munmap
 rax=FFFFFFFFFFFFFFDA

58: 0 AFT 11/munmap
 rax=0000000000000000

59: 0 BEF 56/clone
 rax=FFFFFFFFFFFFFFDA

60: 0 AFT 56/clone
 rax=FFFFFFFFFFFFFFDA

pidfind: NEWPID ctl_xid=1 ctl_pid=0015006C/1376364
61: 1 AFT 56/clone
 rax=0000000000000000

62: 0 BEF 56/clone
 rax=000000000015006C

63: 0 AFT 39/getpid
 rax=FFFFFFFFFFFFFFDA

64: 1 BEF 35/nanosleep
 rax=FFFFFFFFFFFFFFDA

65: 0 BEF 39/getpid
 rax=000000000015006B

66: 0 AFT 5/fstat
 rax=FFFFFFFFFFFFFFDA

67: 0 BEF 5/fstat
 rax=0000000000000000

68: 0 AFT 12/brk
 rax=FFFFFFFFFFFFFFDA

69: 0 BEF 12/brk
 rax=0000000000F4D000

70: 0 AFT 12/brk
 rax=FFFFFFFFFFFFFFDA

71: 0 BEF 12/brk
 rax=0000000000F6E000

72: 0 AFT 12/brk
 rax=FFFFFFFFFFFFFFDA

73: 0 BEF 12/brk
 rax=0000000000F6E000

74: 0 AFT 1/write
 rax=FFFFFFFFFFFFFFDA

75: 0 BEF 1/write
 rax=000000000000000F

76: 0 AFT 1/write
 rax=FFFFFFFFFFFFFFDA

77: 0 BEF 1/write
 rax=0000000000000005

78: 0 AFT 1/write
 rax=FFFFFFFFFFFFFFDA

79: 0 BEF 1/write
 rax=0000000000000006

80: 0 AFT 1/write
 rax=FFFFFFFFFFFFFFDA

81: 0 BEF 1/write
 rax=0000000000000005

82: 0 AFT 1/write
 rax=FFFFFFFFFFFFFFDA

83: 0 BEF 1/write
 rax=0000000000000006

84: 0 AFT 1/write
 rax=FFFFFFFFFFFFFFDA

85: 0 BEF 1/write
 rax=0000000000000005

86: 0 AFT 35/nanosleep
 rax=FFFFFFFFFFFFFFDA

87: 1 AFT 35/nanosleep
 rax=0000000000000000

88: 1 BEF 39/getpid
 rax=FFFFFFFFFFFFFFDA

89: 1 AFT 39/getpid
 rax=000000000015006C

90: 1 BEF 5/fstat
 rax=FFFFFFFFFFFFFFDA

91: 1 AFT 5/fstat
 rax=0000000000000000

92: 1 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

93: 1 AFT 12/brk
 rax=0000000000F4D000

94: 1 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

95: 1 AFT 12/brk
 rax=0000000000F6E000

96: 1 BEF 12/brk
 rax=FFFFFFFFFFFFFFDA

97: 1 AFT 12/brk
 rax=0000000000F6E000

98: 1 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

99: 1 AFT 1/write
 rax=000000000000000F

100: 1 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

101: 1 AFT 1/write
 rax=0000000000000005

102: 1 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

103: 1 AFT 1/write
 rax=0000000000000005

104: 1 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

105: 1 AFT 1/write
 rax=0000000000000006

106: 1 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

107: 1 AFT 1/write
 rax=0000000000000005

108: 1 BEF 1/write
 rax=FFFFFFFFFFFFFFDA

109: 1 AFT 1/write
 rax=0000000000000006

110: 1 BEF 35/nanosleep
 rax=FFFFFFFFFFFFFFDA

111: 0 BEF 35/nanosleep
 rax=0000000000000000

112: 0 AFT 61/wait4
 rax=FFFFFFFFFFFFFFDA

113: 1 AFT 35/nanosleep
 rax=0000000000000000

114: 1 BEF 231/exit_group
 rax=FFFFFFFFFFFFFFDA

EXIT: 1 status=00000000

EXIT: 1 status=00000000
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • I have edited the code if you could please check again. I have added PTRACE_O_TRACECLONE, I did not add ptrace(PTRACE_CONT, pid, 0, 0); because I believe ptrace(PTRACE_SYSCALL, child, NULL, NULL) already does that and PTRACE_CONT is not required, and changed to record child_pid, but im not getting the grandchild, any ideas on what else I can do? – frazz Aug 16 '23 at 14:46
  • @frazz I've updated my answer with the fix and additional code. – Craig Estey Aug 17 '23 at 00:01
0
  • Use ptrace(PTRACE_TRACEME, 0, 0, 0); in your parent program.
  • To let the parent trace its child, use ptrace(PTRACE_CONT, pid, 0, 0); within the child you are spawning.

(See listing_2 and listing_3 in this link for a simplified example.)

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • That works for a program that does not fork() itself, but my issue is a problem with fork() not being traced by the parent process – frazz Aug 14 '23 at 22:16