aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2021-10-18 18:06:39 -0400
committerMike Frysinger <vapier@gentoo.org>2021-11-02 20:05:25 -0400
commitba41b3b01c573a4f942605142a5a0d2f08b4c799 (patch)
treed1adbadd648af1039d5ecdff435220903bf91749 /libsandbox
parentbump to sandbox-3.0 (diff)
downloadsandbox-ba41b3b01c573a4f942605142a5a0d2f08b4c799.tar.gz
sandbox-ba41b3b01c573a4f942605142a5a0d2f08b4c799.tar.bz2
sandbox-ba41b3b01c573a4f942605142a5a0d2f08b4c799.zip
libsandbox: fix ptracing children
The ptrace logic was largely built around the assumption of execing a single static binary and that's it. But there's nothing stopping it from also forking & creating children. Today, that means children do not get tracked for problems. One major known issue is that the sandbox env is frozen upon launch. So once we switch to ptrace mode, it's not possible for traced code to disable sandboxing or otherwise reconfigure it. Currently that shouldn't be a big deal as we assume the main execution environment (i.e. bash) is dynamic, and that's where the env will be tweaked, but we'll have to address this before we can deploy ptrace more. Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'libsandbox')
-rw-r--r--libsandbox/trace.c73
1 files changed, 65 insertions, 8 deletions
diff --git a/libsandbox/trace.c b/libsandbox/trace.c
index 4ae58aa..0434f96 100644
--- a/libsandbox/trace.c
+++ b/libsandbox/trace.c
@@ -29,7 +29,7 @@ static long _do_ptrace(sb_ptrace_req_t request, const char *srequest, void *addr
# define SBDEBUG 0
#endif
#define __sb_debug(fmt, args...) do { if (SBDEBUG) sb_eraw(fmt, ## args); } while (0)
-#define _sb_debug(fmt, args...) do { if (SBDEBUG) sb_ewarn("TRACE (pid=%i):%s: " fmt, getpid(), __func__, ## args); } while (0)
+#define _sb_debug(fmt, args...) do { if (SBDEBUG) sb_ewarn("TRACE (pid=%i<%i):%s: " fmt, getpid(), trace_pid, __func__, ## args); } while (0)
#define sb_debug(fmt, args...) _sb_debug(fmt "\n", ## args)
#include "trace/os.c"
@@ -397,6 +397,19 @@ static bool trace_check_syscall(const struct syscall_entry *se, void *regs)
return ret;
}
+static void trace_init_tracee(void)
+{
+ do_ptrace(PTRACE_SETOPTIONS, NULL, (void *)(uintptr_t)(
+ PTRACE_O_EXITKILL |
+ PTRACE_O_TRACECLONE |
+ PTRACE_O_TRACEEXEC |
+ PTRACE_O_TRACEEXIT |
+ PTRACE_O_TRACEFORK |
+ PTRACE_O_TRACEVFORK |
+ PTRACE_O_TRACESYSGOOD
+ ));
+}
+
static void trace_loop(void)
{
trace_regs regs;
@@ -471,6 +484,56 @@ static void trace_loop(void)
__sb_debug(" exit event!\n");
continue;
+ case PTRACE_EVENT_CLONE:
+ case PTRACE_EVENT_FORK:
+ case PTRACE_EVENT_VFORK: {
+ /* The tracee is forking, so fork a new tracer to handle it. */
+ long newpid;
+ do_ptrace(PTRACE_GETEVENTMSG, NULL, &newpid);
+ sb_debug("following forking event %i; pid=%li %i\n",
+ event, newpid, before_syscall);
+
+ /* Pipe for synchronizing detach & attach events. */
+ int fds[2];
+ ret = pipe(fds);
+ sb_assert(ret == 0);
+ if (fork() == 0) {
+ /* New tracer needs to take control of new tracee. */
+ char ch;
+ close(fds[1]);
+ RETRY_EINTR(read(fds[0], &ch, 1));
+ close(fds[0]);
+ trace_pid = newpid;
+ retry_attach:
+ ret = do_ptrace(PTRACE_ATTACH, NULL, NULL);
+ if (ret) {
+ if (errno == EPERM)
+ goto retry_attach;
+ sb_ebort("ISE:PTRACE_ATTACH %s", strerror(errno));
+ }
+ trace_init_tracee();
+ before_syscall = true;
+ continue;
+ } else {
+ /* Existing tracer needs to release new tracee. */
+ retry_detach:
+ ret = ptrace(PTRACE_DETACH, newpid, NULL, (void *)SIGSTOP);
+ if (ret) {
+ if (errno == ESRCH) {
+ /* The kernel might not have the proc ready yet. */
+ struct timespec ts = {0, 500 * 1000 /* 0.5 millisec */};
+ nanosleep(&ts, NULL);
+ goto retry_detach;
+ }
+ sb_ebort("ISE:PTRACE_DETACH %s", strerror(errno));
+ }
+ close(fds[0]);
+ RETRY_EINTR(write(fds[1], "", 1));
+ close(fds[1]);
+ }
+ continue;
+ }
+
default:
sb_ebort("ISE: unhandle ptrace signal %s (%i) event %u\n",
strsig(sig), sig, event);
@@ -524,13 +587,7 @@ void trace_main(void)
} else if (trace_pid) {
sb_debug("parent waiting for child (pid=%i) to signal", trace_pid);
waitpid(trace_pid, NULL, 0);
- do_ptrace(PTRACE_SETOPTIONS, NULL,
- (void *)(uintptr_t)(
- PTRACE_O_EXITKILL |
- PTRACE_O_TRACEEXEC |
- PTRACE_O_TRACEEXIT |
- PTRACE_O_TRACESYSGOOD
- ));
+ trace_init_tracee();
sb_close_all_fds();
trace_loop();
sb_ebort("ISE: child should have quit, as should we\n");