From: Michal Sojka Date: Tue, 12 Sep 2017 11:12:38 +0000 (+0200) Subject: utrace: Trace processes across forks X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fprocd.git;a=commitdiff_plain;h=8b7d47af206e057437832d08116b497d38683daa;ds=sidebyside utrace: Trace processes across forks Without this change, utrace can trace only a single process. When the process forks, syscalls of its children do not appear in utrace output. This is a problem, because seccomp filters are inherited by children and therefore filters generated by utrace may lack legitimate syscalls. This commit enables utrace to trace processes across forks. The functionality can be demonstrated by the following examples: utrace /bin/touch /tmp/xxx produces: { "whitelist": [ "rt_sigaction", "rt_sigreturn", "exit", "getuid", "exit_group", "utimensat" ], "policy": 1 } The command: utrace /bin/sh -c 'touch /tmp/xxx' without this commit produces: { "whitelist": [ "stat", "rt_sigaction", "rt_sigprocmask", "rt_sigreturn", "getpid", "fork", "exit", "wait4", "uname", "getcwd", "getuid", "getppid", "exit_group" ], "policy": 1 } but with this commit, the output is the following: { "whitelist": [ "read", "open", "close", "stat", "fstat", "mmap", "mprotect", "rt_sigaction", "rt_sigprocmask", "rt_sigreturn", "getpid", "fork", "execve", "exit", "wait4", "uname", "fcntl", "getcwd", "getuid", "getppid", "arch_prctl", "gettid", "set_tid_address", "exit_group", "utimensat" ], "policy": 1 } Note that in addition to utimensat syscall from touch, this output contains more syscalls than what is in the union of single-process sh and touch traces. The reason is that single-process traces do not include syscalls from dynamic linker (due to preload trick), but the trace of forked processes includes the dynamic linker syscalls. This is unavoidable, because dynamic linker of the forked processes will be subject to seccomp filters of the parent process. Signed-off-by: Michal Sojka --- diff --git a/trace/trace.c b/trace/trace.c index 35bc548..f882c2e 100644 --- a/trace/trace.c +++ b/trace/trace.c @@ -57,11 +57,15 @@ fprintf(stderr, "utrace: "fmt, ## __VA_ARGS__); \ } while (0) -static struct uloop_process tracer; +struct tracee { + struct uloop_process proc; + int in_syscall; +}; + +static struct tracee tracer; static int *syscall_count; static struct blob_buf b; static int syscall_max; -static int in_syscall; static int debug; static int max_syscall = ARRAY_SIZE(syscall_names); @@ -143,25 +147,45 @@ static void print_syscalls(int policy, const char *json) static void tracer_cb(struct uloop_process *c, int ret) { - if (WIFSTOPPED(ret) && WSTOPSIG(ret) & 0x80) { - if (!in_syscall) { - int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr); - - if (syscall < syscall_max) { - syscall_count[syscall]++; - if (debug) - fprintf(stderr, "%s()\n", syscall_names[syscall]); - } else if (debug) { - fprintf(stderr, "syscal(%d)\n", syscall); + struct tracee *tracee = container_of(c, struct tracee, proc); + + if (WIFSTOPPED(ret)) { + if (WSTOPSIG(ret) & 0x80) { + if (!tracee->in_syscall) { + int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr); + + if (syscall < syscall_max) { + syscall_count[syscall]++; + if (debug) + fprintf(stderr, "%s()\n", syscall_names[syscall]); + } else if (debug) { + fprintf(stderr, "syscal(%d)\n", syscall); + } } + tracee->in_syscall = !tracee->in_syscall; + } else if ((ret >> 8) == (SIGTRAP | (PTRACE_EVENT_FORK << 8))) { + struct tracee *child = calloc(1, sizeof(struct tracee)); + + ptrace(PTRACE_GETEVENTMSG, c->pid, 0, &child->proc.pid); + child->proc.cb = tracer_cb; + ptrace(PTRACE_SYSCALL, child->proc.pid, 0, 0); + uloop_process_add(&child->proc); + if (debug) + fprintf(stderr, "Tracing new child %d\n", child->proc.pid); } - in_syscall = !in_syscall; } else if (WIFEXITED(ret)) { - uloop_end(); + if (tracee == &tracer) { + uloop_end(); /* Main process exit */ + } else { + if (debug) + fprintf(stderr, "Child %d exited\n", tracee->proc.pid); + free(tracee); + } return; } + ptrace(PTRACE_SYSCALL, c->pid, 0, 0); - uloop_process_add(&tracer); + uloop_process_add(c); } int main(int argc, char **argv, char **envp) @@ -228,13 +252,15 @@ int main(int argc, char **argv, char **envp) return -1; } - ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD); + ptrace(PTRACE_SETOPTIONS, child, 0, + PTRACE_O_TRACESYSGOOD | + PTRACE_O_TRACEFORK); ptrace(PTRACE_SYSCALL, child, 0, 0); uloop_init(); - tracer.pid = child; - tracer.cb = tracer_cb; - uloop_process_add(&tracer); + tracer.proc.pid = child; + tracer.proc.cb = tracer_cb; + uloop_process_add(&tracer.proc); uloop_run(); uloop_done();