Skip to content

Commit e8b810f

Browse files
bgamariblackgnezdo
authored andcommitted
cbits: Ensure that fork pipe doesn't shadow std fds
Previously we would assume that the pipe used to communicate errors from the child back to the parent did not shadow the standard descriptors (stdin, stdout, and stderr). Somewhat surprisingly, this appears to hold most platforms. However, OpenBSD appears to be a notable exception. This lead to the failure of the `processT251` test. Avoid relying on this assumption by `dup`ing the pipe fds until they end up out of the standard fd range. Closes #266.
1 parent 484286a commit e8b810f

File tree

1 file changed

+36
-0
lines changed

1 file changed

+36
-0
lines changed

cbits/posix/fork_exec.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,32 @@ setup_std_handle_fork(int fd,
101101
}
102102
}
103103

104+
/* We must ensure that the fork communications pipe does not inhabit fds 0
105+
* through 2 since we will need to manipulate these fds in
106+
* setup_std_handle_fork while keeping the pipe available so that it can report
107+
* errors. See #266.
108+
*/
109+
int unshadow_pipe_fd(int fd, char **failed_doing) {
110+
if (fd <= 2) {
111+
int fd2 = dup(fd);
112+
if (fd2 == -1) {
113+
*failed_doing = "dup(unshadow)";
114+
return -1;
115+
}
116+
117+
// This should recurse at most three times
118+
int fd3 = unshadow_pipe_fd(fd2, failed_doing);
119+
if (close(fd2) == -1) {
120+
*failed_doing = "close(unshadow)";
121+
return -1;
122+
}
123+
124+
return fd3;
125+
} else {
126+
return fd;
127+
}
128+
}
129+
104130
/* Try spawning with fork. */
105131
ProcHandle
106132
do_spawn_fork (char *const args[],
@@ -119,6 +145,16 @@ do_spawn_fork (char *const args[],
119145
return -1;
120146
}
121147

148+
// Ensure that the pipe fds don't shadow stdin/stdout/stderr
149+
forkCommunicationFds[0] = unshadow_pipe_fd(forkCommunicationFds[0], failed_doing);
150+
if (forkCommunicationFds[0] == -1) {
151+
return -1;
152+
}
153+
forkCommunicationFds[1] = unshadow_pipe_fd(forkCommunicationFds[1], failed_doing);
154+
if (forkCommunicationFds[1] == -1) {
155+
return -1;
156+
}
157+
122158
// Block signals with Haskell handlers. The danger here is that
123159
// with the threaded RTS, a signal arrives in the child process,
124160
// the RTS writes the signal information into the pipe (which is

0 commit comments

Comments
 (0)