bpo-35182: fix communicate() crash after child closes its pipes (GH-17020) (GH-18117)

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>
This commit is contained in:
Alex Rebert 2020-01-22 18:28:31 -05:00 committed by Gregory P. Smith
parent 1f0f102dec
commit d3ae95e1e9
3 changed files with 16 additions and 2 deletions

View 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():

View 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):

View File

@ -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.