diff --git a/cbits/posix/fork_exec.c b/cbits/posix/fork_exec.c index dd6e58f1eecf4fcfeb787fb579816201b1386da2..3822955777ffdbb15e1f5fe0d47bef9e25a45ca4 100644 --- a/cbits/posix/fork_exec.c +++ b/cbits/posix/fork_exec.c @@ -101,6 +101,32 @@ setup_std_handle_fork(int fd, } } +/* We must ensure that the fork communications pipe does not inhabit fds 0 + * through 2 since we will need to manipulate these fds in + * setup_std_handle_fork while keeping the pipe available so that it can report + * errors. See #266. + */ +int unshadow_pipe_fd(int fd, char **failed_doing) { + if (fd <= 2) { + int fd2 = dup(fd); + if (fd2 == -1) { + *failed_doing = "dup(unshadow)"; + return -1; + } + + // This should recurse at most three times + int fd3 = unshadow_pipe_fd(fd2, failed_doing); + if (close(fd2) == -1) { + *failed_doing = "close(unshadow)"; + return -1; + } + + return fd3; + } else { + return fd; + } +} + /* Try spawning with fork. */ ProcHandle do_spawn_fork (char *const args[], @@ -119,6 +145,16 @@ do_spawn_fork (char *const args[], return -1; } + // Ensure that the pipe fds don't shadow stdin/stdout/stderr + forkCommunicationFds[0] = unshadow_pipe_fd(forkCommunicationFds[0], failed_doing); + if (forkCommunicationFds[0] == -1) { + return -1; + } + forkCommunicationFds[1] = unshadow_pipe_fd(forkCommunicationFds[1], failed_doing); + if (forkCommunicationFds[1] == -1) { + return -1; + } + // Block signals with Haskell handlers. The danger here is that // with the threaded RTS, a signal arrives in the child process, // the RTS writes the signal information into the pipe (which is