From 4fd02c829ae23cc5c581acf06e3012578fa07db7 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Mon, 11 Apr 2016 08:59:15 -0700 Subject: [PATCH] proc: Set close-on-exec flag on fork pipes --- proc/exec_darwin.c | 47 +++++++++++++++++++++++++++++----------------- proc/exec_darwin.h | 1 + 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/proc/exec_darwin.c b/proc/exec_darwin.c index 3c6dd394..f46dccae 100644 --- a/proc/exec_darwin.c +++ b/proc/exec_darwin.c @@ -2,6 +2,14 @@ extern char** environ; +int +close_exec_pipe(int fd[2]) { + if (pipe(fd) < 0) return -1; + if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1; + if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1; + return 0; +} + int fork_exec(char *argv0, char **argv, int size, mach_port_name_t *task, @@ -9,23 +17,23 @@ fork_exec(char *argv0, char **argv, int size, mach_port_t *exception_port, mach_port_t *notification_port) { - // In order to call PT_SIGEXC below, we must ensure that we have acquired the mach task first. - // We facilitate this by creating a pipe and using it to let the forked process know that we've - // finishing acquiring the mach task, and it can go ahead with the calls to PT_TRACE_ME and PT_SIGEXC. + // Since we're using mach exceptions instead of signals, + // we need to coordinate between parent and child via pipes + // to ensure that the parent has set the exception ports on + // the child task before it execs. int fd[2]; - if (pipe(fd) < 0) return -1; + if (close_exec_pipe(fd) < 0) return -1; - // Create another pipe so that we know when we're about to exec. This ensures that control only returns - // back to Go-land when we call exec, effectively eliminating a race condition between launching the new - // process and trying to read its memory. - int wfd[2]; - if (pipe(wfd) < 0) return -1; + // Create another pipe to signal the parent on exec. + int efd[2]; + if (close_exec_pipe(efd) < 0) return -1; kern_return_t kret; pid_t pid = fork(); if (pid > 0) { // In parent. close(fd[0]); + close(efd[1]); kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port); if (kret != KERN_SUCCESS) return -1; @@ -33,10 +41,14 @@ fork_exec(char *argv0, char **argv, int size, write(fd[1], &msg, 1); close(fd[1]); - char w; - read(wfd[0], &w, 1); - close(wfd[0]); - + char w; + size_t n = read(efd[0], &w, 1); + close(efd[0]); + if (n != 0) { + // Child died, reap it. + waitpid(pid, NULL, 0); + return -1; + } return pid; } @@ -64,13 +76,14 @@ fork_exec(char *argv0, char **argv, int size, pret = ptrace(PT_SIGEXC, 0, 0, 0); if (pret != 0 && errno != 0) return -errno; - char msg = 'd'; - write(wfd[1], &msg, 1); - close(wfd[1]); - // Create the child process. execve(argv0, argv, environ); // We should never reach here, but if we did something went wrong. + // Write a message to parent to alert that exec failed. + char msg = 'd'; + write(efd[1], &msg, 1); + close(efd[1]); + exit(1); } diff --git a/proc/exec_darwin.h b/proc/exec_darwin.h index da454ab2..395cad43 100644 --- a/proc/exec_darwin.h +++ b/proc/exec_darwin.h @@ -4,6 +4,7 @@ #include #include #include +#include int fork_exec(char *, char **, int, mach_port_name_t*, mach_port_t*, mach_port_t*, mach_port_t*);