]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-35182: fix communicate() crash after child closes its pipes (GH-17020) (GH-18117)
authorAlex Rebert <alex@forallsecure.com>
Wed, 22 Jan 2020 23:28:31 +0000 (18:28 -0500)
committerGregory P. Smith <greg@krypto.org>
Wed, 22 Jan 2020 23:28:31 +0000 (15:28 -0800)
When communicate() is called in a loop, it crashes when the child process
has already closed any piped standard stream, but still continues to be running

Co-authored-by: Andriy Maletsky <andriy.maletsky@gmail.com>
Lib/subprocess.py
Lib/test/test_subprocess.py
Misc/NEWS.d/next/Library/2019-10-31-19-23-25.bpo-35182.hzeNl9.rst [new file with mode: 0644]

index 79dffd349a30e9af96415dd1089275b1371217ab..26a1e69bd38c25ea0d83811f0632fa2cf6b87a6c 100644 (file)
@@ -1983,9 +1983,9 @@ class Popen(object):
             with _PopenSelector() as selector:
                 if self.stdin and input:
                     selector.register(self.stdin, selectors.EVENT_WRITE)
-                if self.stdout:
+                if self.stdout and not self.stdout.closed:
                     selector.register(self.stdout, selectors.EVENT_READ)
-                if self.stderr:
+                if self.stderr and not self.stderr.closed:
                     selector.register(self.stderr, selectors.EVENT_READ)
 
                 while selector.get_map():
index f1fb93455dd7da8d8a386c36b7230ae23846ba11..2bbdbaef84e9926280b7471399300195fe5d3954 100644 (file)
@@ -3145,6 +3145,17 @@ class POSIXProcessTestCase(BaseTestCase):
         # so Popen failed to read it and uses a default returncode instead.
         self.assertIsNotNone(proc.returncode)
 
+    def test_communicate_repeated_call_after_stdout_close(self):
+        proc = subprocess.Popen([sys.executable, '-c',
+                                 'import os, time; os.close(1), time.sleep(2)'],
+                                stdout=subprocess.PIPE)
+        while True:
+            try:
+                proc.communicate(timeout=0.1)
+                return
+            except subprocess.TimeoutExpired:
+                pass
+
 
 @unittest.skipUnless(mswindows, "Windows specific tests")
 class Win32ProcessTestCase(BaseTestCase):
diff --git a/Misc/NEWS.d/next/Library/2019-10-31-19-23-25.bpo-35182.hzeNl9.rst b/Misc/NEWS.d/next/Library/2019-10-31-19-23-25.bpo-35182.hzeNl9.rst
new file mode 100644 (file)
index 0000000..9438cd8
--- /dev/null
@@ -0,0 +1,3 @@
+Fixed :func:`Popen.communicate` subsequent call crash when the child process
+has already closed any piped standard stream, but still continues to be
+running. Patch by Andriy Maletsky.