@@ -64,7 +64,7 @@ setup_std_handle_fork(int fd,
6464{
6565 switch (b -> behavior ) {
6666 case STD_HANDLE_CLOSE :
67- if (close (fd ) == -1 ) {
67+ if (close (fd ) == -1 && errno != EBADF ) {
6868 child_failed (pipe , "close" );
6969 }
7070 return 0 ;
@@ -101,6 +101,31 @@ 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+ int i = 0 ;
111+ int fds [3 ] = {0 };
112+ for (i = 0 ; fd < 3 && i < 3 ; ++ i ) {
113+ fds [i ] = fd ;
114+ fd = dup (fd );
115+ if (fd == -1 ) {
116+ * failed_doing = "dup(unshadow)" ;
117+ return -1 ;
118+ }
119+ }
120+ for (int j = 0 ; j < i ; ++ j ) {
121+ if (close (fds [j ]) == -1 ) {
122+ * failed_doing = "close(unshadow)" ;
123+ return -1 ;
124+ }
125+ }
126+ return fd ;
127+ }
128+
104129/* Try spawning with fork. */
105130ProcHandle
106131do_spawn_fork (char * const args [],
@@ -119,6 +144,16 @@ do_spawn_fork (char *const args[],
119144 return -1 ;
120145 }
121146
147+ // Ensure that the pipe fds don't shadow stdin/stdout/stderr
148+ forkCommunicationFds [0 ] = unshadow_pipe_fd (forkCommunicationFds [0 ], failed_doing );
149+ if (forkCommunicationFds [0 ] == -1 ) {
150+ return -1 ;
151+ }
152+ forkCommunicationFds [1 ] = unshadow_pipe_fd (forkCommunicationFds [1 ], failed_doing );
153+ if (forkCommunicationFds [1 ] == -1 ) {
154+ return -1 ;
155+ }
156+
122157 // Block signals with Haskell handlers. The danger here is that
123158 // with the threaded RTS, a signal arrives in the child process,
124159 // the RTS writes the signal information into the pipe (which is
0 commit comments