diff --git a/Lib/subprocess.py b/Lib/subprocess.py --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1164,6 +1164,17 @@ # Data format: "exception name:hex errno:description" # Pickle is not used; it is complex and involves memory allocation. errpipe_read, errpipe_write = _create_pipe() + # Issue 15798: Ensure that errpipe_read & errpipe_write >= 3 so that + # they are not clobbered by dup2() calls in child_exec. + old_fds = [] + while errpipe_read < 3: + old_fds.append(errpipe_read) + errpipe_read = os.dup(errpipe_read) + while errpipe_write < 3: + old_fds.append(errpipe_write) + errpipe_write = os.dup(errpipe_write) + for fd in old_fds: + os.close(fd) try: try: diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1059,6 +1059,23 @@ # all standard fds closed. self.check_close_std_fds([0, 1, 2]) + def test_small_errpipe_write(self): + # Issue #15798: test that subprocess pipes still work properly with + # fd 0 and 1 closed such that errpipe_read & errpipe_write < 3. + # Save stdin & stdout + new_stdin = os.dup(0) + new_stdout = os.dup(1) + os.close(0) + os.close(1) + + subprocess.Popen([sys.executable, "-c", 'pass']).wait() + + # Restore old stdin & 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)]