diff -r 4d431e719646 Lib/subprocess.py --- a/Lib/subprocess.py Tue Aug 28 17:55:35 2012 -0400 +++ b/Lib/subprocess.py Wed Aug 29 11:15:00 2012 -0700 @@ -406,10 +406,17 @@ _create_pipe = _posixsubprocess.cloexec_pipe else: def _create_pipe(): - fds = os.pipe() - _set_cloexec(fds[0], True) - _set_cloexec(fds[1], True) - return fds + # An os.pipe() with the cloexec flag set. write_end >= 3. + read_fd, write_fd = os.pipe() + temp_fds = [] + while write_fd < 3: + temp_fds.append(write_fd) + write_fd = os.dup(write_fd) + for low_fd in temp_fds: + os.close(low_fd) + _set_cloexec(read_fd, True) + _set_cloexec(write_fd, True) + return read_fd, write_fd __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", "getoutput", "check_output", "CalledProcessError"] @@ -1174,6 +1181,12 @@ # Data format: "exception name:hex errno:description" # Pickle is not used; it is complex and involves memory allocation. errpipe_read, errpipe_write = _create_pipe() + wrong_errpipe_fds = [] + while 0 <= errpipe_write < 3: + wrong_errpipe_fds.append(errpipe_write) + errpipe_write = os.dup(errpipe_write) + for fd in wrong_errpipe_fds: + os.close(fd) try: try: diff -r 4d431e719646 Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Tue Aug 28 17:55:35 2012 -0400 +++ b/Lib/test/test_subprocess.py Wed Aug 29 11:15:00 2012 -0700 @@ -1139,6 +1139,22 @@ # all standard fds closed. self.check_close_std_fds([0, 1, 2]) + def test_small_errpipe_write_fd(self): + """Issue #15798: Popen should work when stdio fds are available.""" + new_stdin = os.dup(0) + new_stdout = os.dup(1) + try: + os.close(0) + os.close(1) + + subprocess.Popen([sys.executable, "-c", "pass"]).wait() + finally: + # Restore original stdin and stdout + os.dup2(new_stdin, 0) + os.dup2(new_stdout, 1) + os.close(new_stdin) + os.close(new_stdout) + def test_remapping_std_fds(self): # open up some temporary files temps = [mkstemp() for i in range(3)] diff -r 4d431e719646 Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c Tue Aug 28 17:55:35 2012 -0400 +++ b/Modules/_posixsubprocess.c Wed Aug 29 11:15:00 2012 -0700 @@ -721,13 +721,13 @@ PyDoc_STRVAR(subprocess_cloexec_pipe_doc, "cloexec_pipe() -> (read_end, write_end)\n\n\ -Create a pipe whose ends have the cloexec flag set."); +Create a pipe whose ends have the cloexec flag set; write_end will be >= 3."); static PyObject * subprocess_cloexec_pipe(PyObject *self, PyObject *noargs) { int fds[2]; - int res; + int res, saved_errno; #ifdef HAVE_PIPE2 Py_BEGIN_ALLOW_THREADS res = pipe2(fds, O_CLOEXEC); @@ -760,6 +760,45 @@ } } #endif + if (res == 0 && fd[1] < 3) { + /* We always want the write end of the pipe to avoid fds 0, 1 and 2 + * as our child may claim those for stdio connections. */ + int write_fd = fd[1]; + int fds_to_close[3] = {-1, -1, -1}; + int fds_to_close_idx = 0; +#ifdef F_DUPFD_CLOEXEC + fds_to_close[fds_to_close_idx++] = write_fd; + write_fd = fcntl(write_fd, F_DUPFD_CLOEXEC, 3); + if (write_fd < 0) /* We don't support F_DUPFD_CLOEXEC / other error */ +#endif + { + long oldflags; + /* Use dup a few times until we get a desirable fd. */ + for (; fds_to_close_idx < 3; ++fds_to_close_idx) { + fds_to_close[fds_to_close_idx] = write_fd; + write_fd = dup(write_fd); + if (write_fd >= 3) + break; + /* We may dup a few extra times if it returns an error but + * that is okay. Repeat calls should return the same error. */ + } + if (write_fd < 0) res = write_fd; + if (res == 0) { + oldflags = fcntl(write_fd, F_GETFD, 0); + if (oldflags < 0) res = oldflags; + if (res == 0) + res = fcntl(write_fd, F_SETFD, oldflags | FD_CLOEXEC); + } + } + saved_errno = errno; + /* Close fds we tried for the write end that were too low. */ + for (fds_to_close_idx=0; fds_to_close_idx < 3; ++fds_to_close_idx) { + int temp_fd = fds_to_close[fds_to_close_idx]; + while (temp_fd >= 0 && close(temp_fd) < 0 && errno == EINTR); + } + errno = saved_errno; /* report dup or fcntl errors, not close. */ + } /* end if write fd was too small */ + if (res != 0) return PyErr_SetFromErrno(PyExc_OSError); return Py_BuildValue("(ii)", fds[0], fds[1]);