[Python-checkins] cpython (2.7): Issue #29335: Fix subprocess.Popen.wait() when the child process has

gregory.p.smith python-checkins at python.org
Mon Jan 23 01:38:39 EST 2017


https://hg.python.org/cpython/rev/8e3d412f8e89
changeset:   106279:8e3d412f8e89
branch:      2.7
parent:      106214:9b22d52a6d4b
user:        Gregory P. Smith <greg at krypto.org>
date:        Sun Jan 22 22:38:28 2017 -0800
summary:
  Issue #29335: Fix subprocess.Popen.wait() when the child process has
exited to a stopped instead of terminated state (ex: when under ptrace).

files:
  Lib/subprocess.py           |   5 ++-
  Lib/test/test_subprocess.py |  46 +++++++++++++++++++++++++
  Misc/NEWS                   |   3 +
  3 files changed, 53 insertions(+), 1 deletions(-)


diff --git a/Lib/subprocess.py b/Lib/subprocess.py
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -1026,13 +1026,16 @@
 
         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):
             # This method is called (indirectly) by __del__, so it cannot
             # refer to anything outside of its local scope.
             if _WIFSIGNALED(sts):
                 self.returncode = -_WTERMSIG(sts)
             elif _WIFEXITED(sts):
                 self.returncode = _WEXITSTATUS(sts)
+            elif _WIFSTOPPED(sts):
+                self.returncode = -_WSTOPSIG(sts)
             else:
                 # Should never happen
                 raise RuntimeError("Unknown child exit status!")
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -2,6 +2,7 @@
 from test import test_support
 import subprocess
 import sys
+import platform
 import signal
 import os
 import errno
@@ -11,6 +12,11 @@
 import sysconfig
 
 try:
+    import ctypes
+except ImportError:
+    ctypes = None
+
+try:
     import resource
 except ImportError:
     resource = None
@@ -1216,6 +1222,46 @@
 
         self.assertEqual(p2.returncode, 0, "Unexpected error: " + repr(stderr))
 
+    _libc_file_extensions = {
+      'Linux': 'so.6',
+      'Darwin': 'dylib',
+    }
+    @unittest.skipIf(not ctypes, 'ctypes module required.')
+    @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):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -23,6 +23,9 @@
 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 #29219: Fixed infinite recursion in the repr of uninitialized
   ctypes.CDLL instances.
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list