diff -r 1754722ec296 Lib/subprocess.py --- a/Lib/subprocess.py Sun Jan 22 14:39:20 2017 +0800 +++ b/Lib/subprocess.py Sun Jan 22 13:20:52 2017 -0800 @@ -1329,7 +1329,8 @@ def _handle_exitstatus(self, sts, _WIFSIGNALED=os.WIFSIGNALED, _WTERMSIG=os.WTERMSIG, _WIFEXITED=os.WIFEXITED, - _WEXITSTATUS=os.WEXITSTATUS): + _WEXITSTATUS=os.WEXITSTATUS, _WIFSTOPPED=os.WIFSTOPPED, + _WSTOPSIG=os.WSTOPSIG): """All callers to this function MUST hold self._waitpid_lock.""" # This method is called (indirectly) by __del__, so it cannot # refer to anything outside of its local scope. @@ -1337,6 +1338,8 @@ self.returncode = -_WTERMSIG(sts) elif _WIFEXITED(sts): self.returncode = _WEXITSTATUS(sts) + elif _WIFSTOPPED(sts): + self.returncode = -_WSTOPSIG(sts) else: # Should never happen raise SubprocessError("Unknown child exit status!") diff -r 1754722ec296 Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Sun Jan 22 14:39:20 2017 +0800 +++ b/Lib/test/test_subprocess.py Sun Jan 22 13:20:52 2017 -0800 @@ -3,6 +3,8 @@ from test import support import subprocess import sys +import platform +import ctypes import signal import io import os @@ -2498,6 +2500,45 @@ proc.communicate(timeout=999) mock_proc_stdin.close.assert_called_once_with() + _libc_file_extensions = { + 'Linux': 'so.6', + 'Darwin': '.dylib', + } + @unittest.skipIf(platform.uname()[0] not in _libc_file_extensions, + 'Test requires a libc this code can load with ctypes.') + @unittest.skipIf(not sys.executable, 'Test requires sys.executable.') + def test_child_terminated_in_stopped_state(self): + """Test wait() behavior when waitpid returns WIFSTOPPED; issue29335.""" + PTRACE_TRACEME = 0 # From glibc and MacOS (PT_TRACE_ME). + libc_name = 'libc.' + self._libc_file_extensions[platform.uname()[0]] + libc = ctypes.CDLL(libc_name) + if not hasattr(libc, 'ptrace'): + raise unittest.SkipTest('ptrace() required.') + test_ptrace = subprocess.Popen( + [sys.executable, '-c', """if True: + import ctypes + libc = ctypes.CDLL({libc_name!r}) + libc.ptrace({PTRACE_TRACEME}, 0, 0) + """.format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME) + ]) + if test_ptrace.wait() != 0: + raise unittest.SkipTest('ptrace() failed - unable to test.') + child = subprocess.Popen( + [sys.executable, '-c', """if True: + import ctypes + libc = ctypes.CDLL({libc_name!r}) + libc.ptrace({PTRACE_TRACEME}, 0, 0) + libc.printf(ctypes.c_char_p(0xdeadbeef)) # Crash the process. + """.format(libc_name=libc_name, PTRACE_TRACEME=PTRACE_TRACEME) + ]) + try: + returncode = child.wait() + except Exception as e: + child.kill() # Clean up the hung stopped process. + raise e + self.assertNotEqual(0, returncode) + self.assertLess(returncode, 0) # signal death, likely SIGSEGV. + @unittest.skipUnless(mswindows, "Windows specific tests") class Win32ProcessTestCase(BaseTestCase):