cmultiprocessingforkptrace

Ptrace options not working in parent process


Ptrace options dont set the correct status when the desired systemcalls resume in the parent process. I can only use whats seen here no PEEKUSER, SYSGOOD or SYSCALL. Ive read the ptrace man and looked for examples for the past few days, Im mentally exhausted.

Any ideas/tips, no matter how small, are welcome. thanks.

argument: /bin/bash -c "echo 'first test' | wc -c"

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ptrace.h>

int wait_for_syscall(pid_t child) {
    int status;
    while (1) {
        ptrace(PTRACE_CONT, child, 0, 0);
        waitpid(child, &status, 0);
        if (WIFSTOPPED(status) && WSTOPSIG(status) | 0x80)
            return 0;
        if (WIFEXITED(status))
            return 1;
    }
}

int main(int argc, char *argv[]) {
    int status;
    int counter = 0;
    pid_t pid = fork();
    if (pid < 0)
        exit(1);
    else if (pid == 0) {
        ptrace(PTRACE_TRACEME, pid, NULL, NULL);
        raise(SIGSTOP);
        return execve(argv[1], &argv[1], NULL);
    } else {
        wait(&status);
        ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACESECCOMP);

        while (1) {
            if (wait_for_syscall(pid) != 0) break;
            
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8)))
                counter++;

            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)))
                counter++;

            if (wait_for_syscall(pid) != 0) break;
        }
    }
    return 0;
}

Solution

  • In your primary loop, you use a status that is scoped to main.

    This is not the same status you are setting in wait_for_syscall (which has its own private copy of status).

    So, the status in main is never updated.


    To fix, you can pass a pointer to wait_for_syscall

    I've left it as is, but I believe that the second call to wait_for_syscall in the loop is extraneous and may actually silently absorb a syscall.

    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/ptrace.h>
    
    int
    wait_for_syscall(pid_t child,int *stp)
    {
        int status;
    
        while (1) {
            ptrace(PTRACE_CONT, child, 0, 0);
    
            waitpid(child, &status, 0);
            *stp = status;
    
            if (WIFSTOPPED(status) && WSTOPSIG(status) | 0x80)
                return 0;
            if (WIFEXITED(status))
                return 1;
        }
    }
    
    int
    main(int argc, char *argv[])
    {
        int status;
        int counter = 0;
    
        pid_t pid = fork();
        if (pid < 0)
            exit(1);
    
        if (pid == 0) {
            ptrace(PTRACE_TRACEME, pid, NULL, NULL);
            raise(SIGSTOP);
            return execve(argv[1], &argv[1], NULL);
        }
    
        wait(&status);
        ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACESECCOMP | PTRACE_O_TRACECLONE);
    
        while (1) {
            if (wait_for_syscall(pid,&status) != 0)
                break;
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8)))
                counter++;
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)))
                counter++;
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8)))
                counter++;
    
            if (wait_for_syscall(pid,&status) != 0)
                break;
        }
    
        return 0;
    }
    

    UPDATE:

    I see. Its close bc now it only does one iteration in the while then it stops. So it doesnt continue/go to the next systemcall. :( – Olivia22

    Okay, I've played with some variations a bit. Here's some changes:

    1. PTRACE_O_TRACEFORK is needed in addition to PTRACE_O_TRACECLONE (this is a big deal ;-)

    2. Use execvp to preserve environment variables (e.g. PATH)

    3. waitpid should get -1 for pid to catch subchildren, etc.

    4. I added debug printing (dbgprt)

    5. Added signo to the ptrace(PTRACE_CONT,...)

    Here's the refactored code (with lots of debug):

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/wait.h>
    #include <sys/ptrace.h>
    
    #define SHOW(_sym) \
        dbgprt(#_sym ": %8.8X %8.8X\n",_sym,SIGTRAP | (_sym << 8))
    
    #define dbgprt(_fmt...) \
        do { \
            int sverr = errno; \
            printf(_fmt); \
            errno = sverr; \
        } while (0)
    
    #define ONERR(_expr) \
        do { \
            if (! (_expr)) \
                break; \
            dbgprt("ONERR " #_expr " line %d -- %s\n",__LINE__,strerror(errno)); \
            exit(1); \
        } while (0)
    
    int
    main(int argc, char *argv[])
    {
        int status;
        int counter = 0;
        long err;
    
        pid_t pidm = getpid();
    
        pid_t pid0 = fork();
        if (pid0 < 0)
            exit(1);
    
        if (pid0 == 0) {
            ptrace(PTRACE_TRACEME, pid0, NULL, NULL);
            //raise(SIGSTOP);
    #if 0
            execve(argv[1], &argv[1], NULL);
    #else
            execvp(argv[1], &argv[1]);
    #endif
            exit(97);
        }
    
        dbgprt("main: pid0=%d/%d\n",pid0,pid0 - pidm);
        dbgprt("main: SIGSTOP %d\n",SIGSTOP);
        dbgprt("main: SIGTRAP %d\n",SIGTRAP);
    
        SHOW(PTRACE_EVENT_EXEC);
        SHOW(PTRACE_EVENT_SECCOMP);
        SHOW(PTRACE_EVENT_CLONE);
    
    #if 0
        err = ptrace(PTRACE_ATTACH,pid0,0,0);
        ONERR(err < 0);
    #endif
    
        err = waitpid(-1,&status,0);
        ONERR(err < 0);
    
    #if 1
        unsigned int opt = 0;
        opt |= PTRACE_O_EXITKILL;
        opt |= PTRACE_O_TRACEEXEC;
        opt |= PTRACE_O_TRACESECCOMP;
        opt |= PTRACE_O_TRACECLONE;
        opt |= PTRACE_O_TRACEFORK;
        err = ptrace(PTRACE_SETOPTIONS, pid0, 0, opt);
        ONERR(err < 0);
    #endif
    
        err = ptrace(PTRACE_CONT,pid0,0,0);
        ONERR(err < 0);
    
        while (1) {
    #if 1
            pid_t pid = waitpid(-1, &status, 0);
    #else
            pid_t pid = wait(&status);
    #endif
    
            unsigned int evmsk = status >> 16;
    
            dbgprt("waitpid: pid=%d/%d status=%8.8X\n",pid,pid - pidm,status);
            dbgprt("waitpid: status %8.8X %8.8X evmsk=%8.8X\n",
                status >> 8,status & 0x7F,evmsk);
    
            if (pid < 0) {
                dbgprt("waitpid: errno=%d -- %s\n",errno,strerror(errno));
                break;
            }
    
            if (WIFEXITED(status)) {
                dbgprt("waitpid: WIFEXITED code=%d\n",WEXITSTATUS(status));
                continue;
            }
    
            int signo = 0;
    
            do {
                if (WIFSTOPPED(status)) {
                    signo = WSTOPSIG(status);
                    dbgprt("waitpid: WIFSTOPPED signo=%d\n",signo);
                }
    
                if (WIFSIGNALED(status)) {
                    signo = WTERMSIG(status);
                    dbgprt("waitpid: WIFSIGNALED signo=%d\n",signo);
                }
            } while (0);
    
    #if 0
            if (WIFSTOPPED(status) && WSTOPSIG(status) | 0x80)
                return 0;
            if (WIFEXITED(status))
                return 1;
    #endif
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
                dbgprt("EXEC\n");
                counter++;
            }
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
                dbgprt("SECCOMP\n");
                counter++;
            }
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
                dbgprt("CLONE\n");
                counter++;
            }
    
            dbgprt("main: counter=%d\n",counter);
    
            err = ptrace(PTRACE_CONT, pid, 0, signo);
            ONERR(err < 0);
        }
    
        return 0;
    }
    

    Here's the program invocation and output:

    + ./fix3 /bin/bash -c 'cat /bin/bash | wc -c'
    main: pid0=1905216/1
    main: SIGSTOP 19
    main: SIGTRAP 5
    PTRACE_EVENT_EXEC: 00000004 00000405
    PTRACE_EVENT_SECCOMP: 00000007 00000705
    PTRACE_EVENT_CLONE: 00000003 00000305
    waitpid: pid=1905216/1 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=0
    waitpid: pid=1905217/2 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905217/2 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=0
    waitpid: pid=1905217/2 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=0
    waitpid: pid=1905216/1 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=0
    waitpid: pid=1905218/3 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905218/3 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=0
    waitpid: pid=1905218/3 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=0
    waitpid: pid=1905216/1 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=0
    waitpid: pid=1905219/4 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905219/4 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=0
    waitpid: pid=1905220/5 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905219/4 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=0
    waitpid: pid=1905220/5 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=0
    waitpid: pid=1905219/4 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=0
    waitpid: pid=1905220/5 status=0004057F
    waitpid: status 00000405 0000007F evmsk=00000004
    waitpid: WIFSTOPPED signo=5
    EXEC
    main: counter=1
    waitpid: pid=1905220/5 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905219/4 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=1
    waitpid: pid=1905219/4 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=1
    waitpid: pid=1905216/1 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=1
    waitpid: pid=1905221/6 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=1
    waitpid: pid=1905221/6 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=1
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=1
    waitpid: pid=1905221/6 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=1
    waitpid: pid=191190216
    05222/7 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=1
    waitpid: pid=1905222/7 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=1
    waitpid: pid=1905221/6 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=1
    waitpid: pid=1905222/7 status=0004057F
    waitpid: status 00000405 0000007F evmsk=00000004
    waitpid: WIFSTOPPED signo=5
    EXEC
    main: counter=2
    waitpid: pid=1905222/7 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905221/6 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=2
    waitpid: pid=1905221/6 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=2
    waitpid: pid=1905216/1 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=2
    waitpid: pid=1905223/8 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=2
    waitpid: pid=1905223/8 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=2
    waitpid: pid=1905216/1 status=0001057F
    waitpid: status 00000105 0000007F evmsk=00000001
    waitpid: WIFSTOPPED signo=5
    main: counter=2
    waitpid: pid=1905224/9 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=2
    waitpid: pid=1905224/9 status=0000137F
    waitpid: status 00000013 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=19
    main: counter=2
    waitpid: pid=1905223/8 status=0004057F
    waitpid: status 00000405 0000007F evmsk=00000004
    waitpid: WIFSTOPPED signo=5
    EXEC
    main: counter=3
    waitpid: pid=1905224/9 status=0004057F
    waitpid: status 00000405 0000007F evmsk=00000004
    waitpid: WIFSTOPPED signo=5
    EXEC
    main: counter=4
    waitpid: pid=1905223/8 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905224/9 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=1905216/1 status=0000117F
    waitpid: status 00000011 0000007F evmsk=00000000
    waitpid: WIFSTOPPED signo=17
    main: counter=4
    waitpid: pid=1905216/1 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: WIFEXITED code=0
    waitpid: pid=-1/-1905216 status=00000000
    waitpid: status 00000000 00000000 evmsk=00000000
    waitpid: errno=10 -- No child processes
    

    UPDATE #2:

    Thanks alot, Ill go study it and see how it works. Just one thing what does if 0 and if 1 mean here? – Olivia22

    For illustration here, I use cpp conditionals to denote old vs. new code:

    #if 0
    // old code
    #else
    // new code
    #endif
    
    #if 1
    // new code
    #endif
    

    Sometimes, I'll put in some experimental code, but it will turn out to be "not so good". So, I'll wrap it in a #if 0. I'll keep it for a while to remind myself that I did it and it didn't work (e.g. the PTRACE_ATTACH block).

    The conditionals are quick way to "comment out" code that is cleaner than using (e.g.) a /* and */ pair.

    When I'm done and I think the code is final, I'll remove the conditionals as appropriate.

    An easy way to do this is to run the code through unifdef -k (and we get):

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/wait.h>
    #include <sys/ptrace.h>
    
    #define SHOW(_sym) \
        dbgprt(#_sym ": %8.8X %8.8X\n",_sym,SIGTRAP | (_sym << 8))
    
    #define dbgprt(_fmt...) \
        do { \
            int sverr = errno; \
            printf(_fmt); \
            errno = sverr; \
        } while (0)
    
    #define ONERR(_expr) \
        do { \
            if (! (_expr)) \
                break; \
            dbgprt("ONERR " #_expr " line %d -- %s\n",__LINE__,strerror(errno)); \
            exit(1); \
        } while (0)
    
    int
    main(int argc, char *argv[])
    {
        int status;
        int counter = 0;
        long err;
    
        pid_t pidm = getpid();
    
        pid_t pid0 = fork();
        if (pid0 < 0)
            exit(1);
    
        if (pid0 == 0) {
            ptrace(PTRACE_TRACEME, pid0, NULL, NULL);
            //raise(SIGSTOP);
            execvp(argv[1], &argv[1]);
            exit(97);
        }
    
        dbgprt("main: pid0=%d/%d\n",pid0,pid0 - pidm);
        dbgprt("main: SIGSTOP %d\n",SIGSTOP);
        dbgprt("main: SIGTRAP %d\n",SIGTRAP);
    
        SHOW(PTRACE_EVENT_EXEC);
        SHOW(PTRACE_EVENT_SECCOMP);
        SHOW(PTRACE_EVENT_CLONE);
    
        err = waitpid(-1,&status,0);
        ONERR(err < 0);
    
        unsigned int opt = 0;
        opt |= PTRACE_O_EXITKILL;
        opt |= PTRACE_O_TRACEEXEC;
        opt |= PTRACE_O_TRACESECCOMP;
        opt |= PTRACE_O_TRACECLONE;
        opt |= PTRACE_O_TRACEFORK;
        err = ptrace(PTRACE_SETOPTIONS, pid0, 0, opt);
        ONERR(err < 0);
    
        err = ptrace(PTRACE_CONT,pid0,0,0);
        ONERR(err < 0);
    
        while (1) {
            pid_t pid = waitpid(-1, &status, 0);
    
            unsigned int evmsk = status >> 16;
    
            dbgprt("waitpid: pid=%d/%d status=%8.8X\n",pid,pid - pidm,status);
            dbgprt("waitpid: status %8.8X %8.8X evmsk=%8.8X\n",
                status >> 8,status & 0x7F,evmsk);
    
            if (pid < 0) {
                dbgprt("waitpid: errno=%d -- %s\n",errno,strerror(errno));
                break;
            }
    
            if (WIFEXITED(status)) {
                dbgprt("waitpid: WIFEXITED code=%d\n",WEXITSTATUS(status));
                continue;
            }
    
            int signo = 0;
    
            do {
                if (WIFSTOPPED(status)) {
                    signo = WSTOPSIG(status);
                    dbgprt("waitpid: WIFSTOPPED signo=%d\n",signo);
                }
    
                if (WIFSIGNALED(status)) {
                    signo = WTERMSIG(status);
                    dbgprt("waitpid: WIFSIGNALED signo=%d\n",signo);
                }
            } while (0);
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_EXEC << 8))) {
                dbgprt("EXEC\n");
                counter++;
            }
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
                dbgprt("SECCOMP\n");
                counter++;
            }
    
            if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {
                dbgprt("CLONE\n");
                counter++;
            }
    
            dbgprt("main: counter=%d\n",counter);
    
            err = ptrace(PTRACE_CONT, pid, 0, signo);
            ONERR(err < 0);
        }
    
        return 0;
    }