0

Basically the title, i can't get it to work, nor can i find any reason why it shouldn't work.

ptrace(2)'s manual states that Linux 5.3+ is required and i am running Linux 5.17.4, the following simplified code compiles without any warnings, runs without any errors while detecting every single system call and yet syscall_info.op always has value PTRACE_SYSCALL_INFO_NONE.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <sys/user.h>

#include <syscall.h>
#include <sys/ptrace.h>
#include <linux/ptrace.h>

#include <sys/errno.h>

int main(int argc, char **argv) {
    pid_t pid = fork();

    switch(pid) {
        case -1:
            //error...
        case 0:
            ptrace(PTRACE_TRACEME, 0, NULL, NULL);
            execvp(argv[1], argv + 1);
            //error...
    }

    waitpid(pid, NULL, 0);
    ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_EXITKILL);

    struct ptrace_syscall_info syscall_info;
    size_t size = sizeof(syscall_info);
    while(1) {
        if(ptrace(PTRACE_SYSCALL, pid, NULL, NULL) == -1) {
            //error...
        }
        if(waitpid(pid, NULL, 0) == -1) {
            //error...
        }
        
        // syscall entry
        if(ptrace(PTRACE_GET_SYSCALL_INFO, pid, (void *)size, &syscall_info) == -1) {
            //error...
        }

        /* syscall_info.op is always PTRACE_SYSCALL_INFO_NONE
           instead of PTRACE_SYSCALL_INFO_ENTRY */

        if(ptrace(PTRACE_SYSCALL, pid, NULL, NULL) == -1) {
            //error...
        }
        if(waitpid(pid, NULL, 0) == -1) {
            //error...
        }

        // syscall exit
        if(ptrace(PTRACE_GET_SYSCALL_INFO, pid, (void *)size, &syscall_info) == -1) {
            if(errno == ESRCH) {
                exit(syscall_info.exit.rval);
            }
            //error...
        }

        /* Same here, should be PTRACE_SYSCALL_INFO_EXIT but still
           is PTRACE_SYSCALL_INFO_NONE */
    }
}

Have been googling for the past 3 days and i wasn't able to find much to validate if i am using it correctly or any posts explaining why this shouldn't work. I went as far as to study strace's source to see how they do it and all i could find was a couple of macros testing whether PTRACE_GET_SYSCALL_INFO is supported in the first place but no idea as to what those tests were. If there are certain criteria for PTRACE_GET_SYSCALL_INFO other than kernel version why aren't they listed in the manual?

fvalasiad
  • 173
  • 1
  • 10
  • Not sure if it's the issue, but `PTRACE_TRACEME` doesn't stop the child. Are you missing a `raise(SIGSTOP)` in the child before the exec? – Nate Eldredge May 28 '22 at 01:50
  • Otherwise, can you post a more complete example that people can actually test? – Nate Eldredge May 28 '22 at 01:58
  • I'm starting to think this may be a kernel bug. The struct is supposed to be populated at [`kernel/ptrace.c` line 1018](https://github.com/torvalds/linux/blob/8291eaafed36f575f23951f3ce18407f480e9ecf/kernel/ptrace.c#L1018), if `child->last_siginfo->si_code` equals either `SIGTRAP | 0x80` (`0x85`) or `SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)` (`0x705`). But I put in a printk and every time it's called, the value is just `0x5`, which equals `SIGTRAP`. So none of the `ptrace_get_syscall_info_*` helpers get called. That doesn't seem right. – Nate Eldredge May 28 '22 at 04:45
  • Aha, it looks like the 0x80 bit gets set if you have enabled the `PTRACE_O_TRACESYSGOOD` option. – Nate Eldredge May 28 '22 at 04:59
  • @NateEldredge, indeed `PTRACE_TRACEME` doesn't stop the child. It's `execvp` that does as the manual states. – fvalasiad May 28 '22 at 09:56
  • @NateEldredge, it now works, damn. Bug or not this should be reported to the kernel developers either by adding this to the manual or by patching it in case it's a bug. – fvalasiad May 28 '22 at 10:04

1 Answers1

1

After some digging, it appears that you have to enable the PTRACE_O_TRACESYSGOOD option of PTRACE_SETOPTIONS in order to get full info from PTRACE_GET_SYSCALL_INFO.

This requirement doesn't seem to be documented. It's unclear if it is intended or if it's a kernel bug.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82