From 13b45f7910d6039e3a3a0971c786a5750f80cd9b Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sat, 19 Dec 2015 19:55:14 -0500 Subject: libsandbox: switch to PTRACE_O_TRACEEXEC Rather than try to deal with the inconsistent cross-arch behavior when it comes to tracking exec behavior, use the PTRACE_O_TRACEEXEC option. This means we only support ptrace on linux-2.6+ systems, but that's fine as we have been requiring that for a long time now. It also means the code is much simpler and stable across arches. Signed-off-by: Mike Frysinger --- libsandbox/trace.c | 68 +++++++++++++++---------------------------- libsandbox/trace/linux/arch.c | 8 +++-- 2 files changed, 29 insertions(+), 47 deletions(-) (limited to 'libsandbox') diff --git a/libsandbox/trace.c b/libsandbox/trace.c index f9194fe..d424389 100644 --- a/libsandbox/trace.c +++ b/libsandbox/trace.c @@ -430,41 +430,34 @@ static bool trace_check_syscall(const struct syscall_entry *se, void *regs) static void trace_loop(void) { trace_regs regs; - bool before_syscall, fake_syscall_ret; + bool before_exec, before_syscall, fake_syscall_ret; long ret; - int nr, exec_state; - const struct syscall_entry *se, *tbl_at_fork, *tbl_after_fork; + int nr, status; + const struct syscall_entry *se, *tbl_after_fork; - exec_state = 0; - before_syscall = true; + before_exec = true; + before_syscall = false; fake_syscall_ret = false; - tbl_at_fork = tbl_after_fork = NULL; + tbl_after_fork = NULL; do { ret = do_ptrace(PTRACE_SYSCALL, NULL, NULL); - waitpid(trace_pid, NULL, 0); - ret = trace_get_regs(®s); - nr = trace_get_sysnum(®s); + waitpid(trace_pid, &status, 0); - if (!exec_state) { - if (!tbl_at_fork) - tbl_at_fork = trace_check_personality(®s); - se = lookup_syscall_in_tbl(tbl_at_fork, nr); - if (!before_syscall || !se || se->sys != SB_NR_EXECVE) { - if (before_syscall) - _sb_debug(">%s:%i", se ? se->name : "IDK", nr); - else - __sb_debug("(...pre-exec...) = ...\n"); - goto loop_again; - } - ++exec_state; - } else if (exec_state == 1) { - /* Don't bother poking exec return */ - ++exec_state; - goto loop_again; + if (before_exec) { + unsigned event = ((unsigned)status >> 16); + if (event == PTRACE_EVENT_EXEC) { + _sb_debug("hit exec!"); + before_exec = false; + } else + _sb_debug("waiting for exec; status: %#x", status); + ret = trace_get_regs(®s); + tbl_after_fork = trace_check_personality(®s); + continue; } - if (!tbl_after_fork) - tbl_after_fork = trace_check_personality(®s); + ret = trace_get_regs(®s); + nr = trace_get_sysnum(®s); + se = lookup_syscall_in_tbl(tbl_after_fork, nr); ret = trace_get_regs(®s); if (before_syscall) { @@ -486,24 +479,11 @@ static void trace_loop(void) ret = trace_result(®s, &err); __sb_debug(" = %li", ret); - if (err) { + if (err) __sb_debug(" (errno: %i: %s)", err, strerror(err)); - - /* If the exec() failed for whatever reason, kill the - * child and have the parent resume like normal - */ - if (exec_state == 1) { - do_ptrace(PTRACE_KILL, NULL, NULL); - trace_pid = 0; - return; - } - } __sb_debug("\n"); - - exec_state = 2; } - loop_again: before_syscall = !before_syscall; } while (1); } @@ -527,10 +507,8 @@ void trace_main(const char *filename, char *const argv[]) } else if (trace_pid) { sb_debug("parent waiting for child (pid=%i) to signal", trace_pid); waitpid(trace_pid, NULL, 0); -#if defined(PTRACE_SETOPTIONS) && defined(PTRACE_O_TRACESYSGOOD) - /* Not all kernel versions support this, so ignore return */ - ptrace(PTRACE_SETOPTIONS, trace_pid, NULL, (void *)PTRACE_O_TRACESYSGOOD); -#endif + do_ptrace(PTRACE_SETOPTIONS, NULL, + (void *)(PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC)); sb_close_all_fds(); trace_loop(); sb_ebort("ISE: child should have quit, as should we\n"); diff --git a/libsandbox/trace/linux/arch.c b/libsandbox/trace/linux/arch.c index 5ca1acb..957db3b 100644 --- a/libsandbox/trace/linux/arch.c +++ b/libsandbox/trace/linux/arch.c @@ -1,7 +1,11 @@ #include "common.c" -/* Linux uses ptrace() */ -#if !defined(HAVE_PTRACE) || !defined(HAVE_SYS_PTRACE_H) || !defined(HAVE_SYS_USER_H) +/* Linux uses ptrace(). We require PTRACE_SETOPTIONS so the exec tracing logic + * is sane. Otherwise we need a lot of arch-specific hacks to make it work. + * This should be fine for linux-2.6+ versions. + */ +#if !defined(HAVE_PTRACE) || !defined(HAVE_SYS_PTRACE_H) || \ + !defined(HAVE_SYS_USER_H) || !defined(PTRACE_SETOPTIONS) # define SB_NO_TRACE_ARCH #elif defined(__bfin__) # include "bfin.c" -- cgit v1.2.3-18-g5258