diff options
| author | Gregory P. Smith <greg@krypto.org> | 2017-01-22 17:29:44 -0800 | 
|---|---|---|
| committer | Gregory P. Smith <greg@krypto.org> | 2017-01-22 17:29:44 -0800 | 
| commit | 78034c81fb0f5b82845533e215f7e5686ce88964 (patch) | |
| tree | 006793f296a6f98d4e9c7707b2cfb7d0034532bf | |
| parent | b1681189af1157d9b6161c8a3a645c2eb816b415 (diff) | |
| parent | 50e16e33af69aca6e908bace793178c8d6ab8272 (diff) | |
| download | cpython-git-78034c81fb0f5b82845533e215f7e5686ce88964.tar.gz | |
Issue #29335: Fix subprocess.Popen.wait() when the child process has
exited to a stopped instead of terminated state (ex: when under ptrace).
| -rw-r--r-- | Lib/subprocess.py | 5 | ||||
| -rw-r--r-- | Lib/test/test_subprocess.py | 41 | ||||
| -rw-r--r-- | Misc/NEWS | 3 | 
3 files changed, 48 insertions, 1 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 13b9d44d31..822ddb459e 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1329,7 +1329,8 @@ class Popen(object):          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 @@ class Popen(object):                  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 --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 89de6d1b1a..faa153aac7 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -3,6 +3,8 @@ from unittest import mock  from test import support  import subprocess  import sys +import platform +import ctypes  import signal  import io  import os @@ -2498,6 +2500,45 @@ class POSIXProcessTestCase(BaseTestCase):              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): @@ -47,6 +47,9 @@ Core and Builtins  Library  ------- +- Issue #29335: Fix subprocess.Popen.wait() when the child process has +  exited to a stopped instead of terminated state (ex: when under ptrace). +  - Issue #29290: Fix a regression in argparse that help messages would wrap at    non-breaking spaces.  | 
