diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 5fd6526963f..e06beed3dd5 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -2312,8 +2312,6 @@ class POSIXProcessTestCase(BaseTestCase): func = lambda: None gc.enable() - executable_list = "exec" # error: must be a sequence - for args, exe_list, cwd, env_list in ( (123, [b"exe"], None, [b"env"]), ([b"arg"], 123, None, [b"env"]), @@ -2331,6 +2329,34 @@ class POSIXProcessTestCase(BaseTestCase): if not gc_enabled: gc.disable() + @support.cpython_only + def test_fork_exec_sorted_fd_sanity_check(self): + # Issue #23564: sanity check the fork_exec() fds_to_keep sanity check. + import _posixsubprocess + gc_enabled = gc.isenabled() + try: + gc.enable() + + for fds_to_keep in ( + (-1, 2, 3, 4, 5), # Negative number. + ('str', 4), # Not an int. + (18, 23, 42, 2**63), # Out of range. + (5, 4), # Not sorted. + (6, 7, 7, 8), # Duplicate. + ): + with self.assertRaises( + ValueError, + msg='fds_to_keep={}'.format(fds_to_keep)) as c: + _posixsubprocess.fork_exec( + [b"false"], [b"false"], + True, fds_to_keep, None, [b"env"], + -1, -1, -1, -1, + 1, 2, 3, 4, + True, True, None) + self.assertIn('fds_to_keep', str(c.exception)) + finally: + if not gc_enabled: + gc.disable() @unittest.skipUnless(mswindows, "Windows specific tests") diff --git a/Misc/NEWS b/Misc/NEWS index 41ed26213bc..f72f8ac1f12 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -11,6 +11,10 @@ Release date: TBA Core and Builtins ----------------- +- Issue #23564: Fixed a partially broken sanity check in the _posixsubprocess + internals regarding how fds_to_pass were passed to the child. The bug had + no actual impact as subprocess.py already avoided it. + - Issue #25388: Fixed tokenizer crash when processing undecodable source code with a null byte. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 800b3019c8f..2cdc38176be 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -109,10 +109,11 @@ _sanity_check_python_fd_sequence(PyObject *fd_sequence) for (seq_idx = 0; seq_idx < seq_len; ++seq_idx) { PyObject* py_fd = PySequence_Fast_GET_ITEM(fd_sequence, seq_idx); long iter_fd = PyLong_AsLong(py_fd); - if (iter_fd < 0 || iter_fd < prev_fd || iter_fd > INT_MAX) { + if (iter_fd < 0 || iter_fd <= prev_fd || iter_fd > INT_MAX) { /* Negative, overflow, not a Long, unsorted, too big for a fd. */ return 1; } + prev_fd = iter_fd; } return 0; }