Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 87602) +++ Misc/NEWS (working copy) @@ -20,6 +20,9 @@ Library ------- +- Issue XXXXX: Fix issue in subprocess where performing dup() on a file + descriptor does not reset the close-on-exec flag. Patch by Ross Lagerwall. + - Issue 6285: IDLE no longer crashes on missing help file; patch by Scott David Daniels. Index: Lib/test/test_subprocess.py =================================================================== --- Lib/test/test_subprocess.py (revision 87602) +++ Lib/test/test_subprocess.py (working copy) @@ -903,6 +903,33 @@ self.assertStderrEqual(stderr, b'') self.assertEqual(p.wait(), -signal.SIGTERM) + def test_close_std_fds(self): + o0 = os.dup(0) + o1 = os.dup(1) + o2 = os.dup(2) + try: + os.close(0) + os.close(1) + os.close(2) + + out, err = subprocess.Popen([sys.executable, "-c", + 'import sys;' + 'sys.stdout.write("apple");' + 'sys.stdout.flush();' + 'sys.stderr.write("orange")'], + stdin=o0, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + err = support.strip_python_stderr(err) + self.assertEqual((out, err), (b'apple', b'orange')) + finally: + os.dup2(o0, 0) + os.dup2(o1, 1) + os.dup2(o2, 2) + os.close(o0) + os.close(o1) + os.close(o2) + def test_surrogates_error_message(self): def prepare(): raise ValueError("surrogate:\uDCff") Index: Lib/subprocess.py =================================================================== --- Lib/subprocess.py (revision 87602) +++ Lib/subprocess.py (working copy) @@ -1194,11 +1194,25 @@ os.close(errpipe_read) # Dup fds for child - if p2cread != -1: + try: + cloexec_flag = fcntl.FD_CLOEXEC + except AttributeError: + cloexec_flag = 1 + + if p2cread == 0: + old = fcntl.fcntl(p2cread, fcntl.F_GETFD) + fcntl.fcntl(p2cread, fcntl.F_SETFD, old & ~cloexec_flag) + elif p2cread != -1: os.dup2(p2cread, 0) - if c2pwrite != -1: + if c2pwrite == 1: + old = fcntl.fcntl(c2pwrite, fcntl.F_GETFD) + fcntl.fcntl(c2pwrite, fcntl.F_SETFD, old & ~cloexec_flag) + elif c2pwrite != -1: os.dup2(c2pwrite, 1) - if errwrite != -1: + if errwrite == 2: + old = fcntl.fcntl(errwrite, fcntl.F_GETFD) + fcntl.fcntl(errwrite, fcntl.F_SETFD, old & ~cloexec_flag) + elif errwrite != -1: os.dup2(errwrite, 2) # Close pipe fds. Make sure we don't close the Index: Modules/_posixsubprocess.c =================================================================== --- Modules/_posixsubprocess.c (revision 87602) +++ Modules/_posixsubprocess.c (working copy) @@ -70,13 +70,25 @@ POSIX_CALL(close(errpipe_read)); /* Dup fds for child. */ - if (p2cread != -1) { + if (p2cread == 0) { + int old = fcntl(p2cread, F_GETFD); + if (old != -1) + fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC); + } else if (p2cread != -1) { POSIX_CALL(dup2(p2cread, 0)); /* stdin */ } - if (c2pwrite != -1) { + if (c2pwrite == 1) { + int old = fcntl(c2pwrite, F_GETFD); + if (old != -1) + fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); + } else if (c2pwrite != -1) { POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ } - if (errwrite != -1) { + if (errwrite == 2) { + int old = fcntl(errwrite, F_GETFD); + if (old != -1) + fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); + } else if (errwrite != -1) { POSIX_CALL(dup2(errwrite, 2)); /* stderr */ }