Skip to content

Commit d3ae95e

Browse files
alpireand800
authored andcommitted
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 <[email protected]>
1 parent 1f0f102 commit d3ae95e

File tree

3 files changed

+16
-2
lines changed

3 files changed

+16
-2
lines changed

Lib/subprocess.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,9 +1983,9 @@ def _communicate(self, input, endtime, orig_timeout):
19831983
with _PopenSelector() as selector:
19841984
if self.stdin and input:
19851985
selector.register(self.stdin, selectors.EVENT_WRITE)
1986-
if self.stdout:
1986+
if self.stdout and not self.stdout.closed:
19871987
selector.register(self.stdout, selectors.EVENT_READ)
1988-
if self.stderr:
1988+
if self.stderr and not self.stderr.closed:
19891989
selector.register(self.stderr, selectors.EVENT_READ)
19901990

19911991
while selector.get_map():

Lib/test/test_subprocess.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3145,6 +3145,17 @@ def test_send_signal_race(self):
31453145
# so Popen failed to read it and uses a default returncode instead.
31463146
self.assertIsNotNone(proc.returncode)
31473147

3148+
def test_communicate_repeated_call_after_stdout_close(self):
3149+
proc = subprocess.Popen([sys.executable, '-c',
3150+
'import os, time; os.close(1), time.sleep(2)'],
3151+
stdout=subprocess.PIPE)
3152+
while True:
3153+
try:
3154+
proc.communicate(timeout=0.1)
3155+
return
3156+
except subprocess.TimeoutExpired:
3157+
pass
3158+
31483159

31493160
@unittest.skipUnless(mswindows, "Windows specific tests")
31503161
class Win32ProcessTestCase(BaseTestCase):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed :func:`Popen.communicate` subsequent call crash when the child process
2+
has already closed any piped standard stream, but still continues to be
3+
running. Patch by Andriy Maletsky.

0 commit comments

Comments
 (0)