Skip to content

Commit de5dcfa

Browse files
bpo-40138: Fix Windows os.waitpid() for large exit code (GH-19654)
Fix the Windows implementation of os.waitpid() for exit code larger than "INT_MAX >> 8". The exit status is now interpreted as an unsigned number. (cherry picked from commit b073509) Co-authored-by: Victor Stinner <[email protected]>
1 parent abdfb3b commit de5dcfa

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed

Lib/test/test_os.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,12 +2417,37 @@ def test_getppid(self):
24172417
# We are the parent of our subprocess
24182418
self.assertEqual(int(stdout), os.getpid())
24192419

2420+
def check_waitpid(self, code, exitcode):
2421+
if sys.platform == 'win32':
2422+
# On Windows, os.spawnv() simply joins arguments with spaces:
2423+
# arguments need to be quoted
2424+
args = [f'"{sys.executable}"', '-c', f'"{code}"']
2425+
else:
2426+
args = [sys.executable, '-c', code]
2427+
pid = os.spawnv(os.P_NOWAIT, sys.executable, args)
2428+
2429+
pid2, status = os.waitpid(pid, 0)
2430+
if sys.platform == 'win32':
2431+
self.assertEqual(status, exitcode << 8)
2432+
else:
2433+
self.assertTrue(os.WIFEXITED(status), status)
2434+
self.assertEqual(os.WEXITSTATUS(status), exitcode)
2435+
self.assertEqual(pid2, pid)
2436+
24202437
def test_waitpid(self):
2421-
args = [sys.executable, '-c', 'pass']
2422-
# Add an implicit test for PyUnicode_FSConverter().
2423-
pid = os.spawnv(os.P_NOWAIT, FakePath(args[0]), args)
2424-
status = os.waitpid(pid, 0)
2425-
self.assertEqual(status, (pid, 0))
2438+
self.check_waitpid(code='pass', exitcode=0)
2439+
2440+
def test_waitpid_exitcode(self):
2441+
exitcode = 23
2442+
code = f'import sys; sys.exit({exitcode})'
2443+
self.check_waitpid(code, exitcode=exitcode)
2444+
2445+
@unittest.skipUnless(sys.platform == 'win32', 'win32-specific test')
2446+
def test_waitpid_windows(self):
2447+
# bpo-40138: test os.waitpid() with exit code larger than INT_MAX.
2448+
STATUS_CONTROL_C_EXIT = 0xC000013A
2449+
code = f'import _winapi; _winapi.ExitProcess({STATUS_CONTROL_C_EXIT})'
2450+
self.check_waitpid(code, exitcode=STATUS_CONTROL_C_EXIT)
24262451

24272452

24282453
class SpawnTests(unittest.TestCase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the Windows implementation of :func:`os.waitpid` for exit code larger than
2+
``INT_MAX >> 8``. The exit status is now interpreted as an unsigned number.

Modules/posixmodule.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7127,8 +7127,10 @@ os_waitpid_impl(PyObject *module, intptr_t pid, int options)
71277127
if (res < 0)
71287128
return (!async_err) ? posix_error() : NULL;
71297129

7130+
unsigned long long ustatus = (unsigned int)status;
7131+
71307132
/* shift the status left a byte so this is more like the POSIX waitpid */
7131-
return Py_BuildValue(_Py_PARSE_INTPTR "i", res, status << 8);
7133+
return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8);
71327134
}
71337135
#endif
71347136

0 commit comments

Comments
 (0)