summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoss Lagerwall <rosslagerwall@gmail.com>2011-07-27 05:39:27 (GMT)
committerRoss Lagerwall <rosslagerwall@gmail.com>2011-07-27 05:39:27 (GMT)
commit114f0e499d1a9c4b63a827f23fdc87937d17fa10 (patch)
tree9fdecc99c4dd9b3f9ddc3f88291990ef817ccf86
parent4d90b19373a7ed83dee2291f82167842ed738bf2 (diff)
parentd98646e43096053ee13f5a9a6af38f8c723b1aaf (diff)
downloadcpython-114f0e499d1a9c4b63a827f23fdc87937d17fa10.zip
cpython-114f0e499d1a9c4b63a827f23fdc87937d17fa10.tar.gz
cpython-114f0e499d1a9c4b63a827f23fdc87937d17fa10.tar.bz2
Issue #12607: Merge with 3.2.
-rw-r--r--Lib/test/test_subprocess.py58
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_posixsubprocess.c7
3 files changed, 68 insertions, 0 deletions
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index 3921436..06664a8 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -1198,6 +1198,64 @@ class POSIXProcessTestCase(BaseTestCase):
for fd in temp_fds:
os.close(fd)
+ def check_swap_fds(self, stdin_no, stdout_no, stderr_no):
+ # open up some temporary files
+ temps = [mkstemp() for i in range(3)]
+ temp_fds = [fd for fd, fname in temps]
+ try:
+ # unlink the files -- we won't need to reopen them
+ for fd, fname in temps:
+ os.unlink(fname)
+
+ # save a copy of the standard file descriptors
+ saved_fds = [os.dup(fd) for fd in range(3)]
+ try:
+ # duplicate the temp files over the standard fd's 0, 1, 2
+ for fd, temp_fd in enumerate(temp_fds):
+ os.dup2(temp_fd, fd)
+
+ # write some data to what will become stdin, and rewind
+ os.write(stdin_no, b"STDIN")
+ os.lseek(stdin_no, 0, 0)
+
+ # now use those files in the given order, so that subprocess
+ # has to rearrange them in the child
+ p = subprocess.Popen([sys.executable, "-c",
+ 'import sys; got = sys.stdin.read();'
+ 'sys.stdout.write("got %s"%got); sys.stderr.write("err")'],
+ stdin=stdin_no,
+ stdout=stdout_no,
+ stderr=stderr_no)
+ p.wait()
+
+ for fd in temp_fds:
+ os.lseek(fd, 0, 0)
+
+ out = os.read(stdout_no, 1024)
+ err = support.strip_python_stderr(os.read(stderr_no, 1024))
+ finally:
+ for std, saved in enumerate(saved_fds):
+ os.dup2(saved, std)
+ os.close(saved)
+
+ self.assertEqual(out, b"got STDIN")
+ self.assertEqual(err, b"err")
+
+ finally:
+ for fd in temp_fds:
+ os.close(fd)
+
+ # When duping fds, if there arises a situation where one of the fds is
+ # either 0, 1 or 2, it is possible that it is overwritten (#12607).
+ # This tests all combinations of this.
+ def test_swap_fds(self):
+ self.check_swap_fds(0, 1, 2)
+ self.check_swap_fds(0, 2, 1)
+ self.check_swap_fds(1, 0, 2)
+ self.check_swap_fds(1, 2, 0)
+ self.check_swap_fds(2, 0, 1)
+ self.check_swap_fds(2, 1, 0)
+
def test_surrogates_error_message(self):
def prepare():
raise ValueError("surrogate:\uDCff")
diff --git a/Misc/NEWS b/Misc/NEWS
index 5d32c31..857acce 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -237,6 +237,9 @@ Core and Builtins
Library
-------
+- Issue #12607: In subprocess, fix issue where if stdin, stdout or stderr is
+ given as a low fd, it gets overwritten.
+
- Issue #12590: IDLE editor window now always displays the first line
when opening a long file. With Tk 8.5, the first line was hidden.
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c
index af0d971..bc2f72c 100644
--- a/Modules/_posixsubprocess.c
+++ b/Modules/_posixsubprocess.c
@@ -69,6 +69,13 @@ static void child_exec(char *const exec_array[],
}
POSIX_CALL(close(errpipe_read));
+ /* When duping fds, if there arises a situation where one of the fds is
+ either 0, 1 or 2, it is possible that it is overwritten (#12607). */
+ if (c2pwrite == 0)
+ POSIX_CALL(c2pwrite = dup(c2pwrite));
+ if (errwrite == 0 || errwrite == 1)
+ POSIX_CALL(errwrite = dup(errwrite));
+
/* Dup fds for child.
dup2() removes the CLOEXEC flag but we must do it ourselves if dup2()
would be a no-op (issue #10806). */