diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index d89b24ac59bec0..bce7b37a17886f 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -172,8 +172,6 @@ def ensure_running(self): main_kws['sys_path'] = data['sys_path'] if 'init_main_from_path' in data: main_kws['main_path'] = data['init_main_from_path'] - if 'sys_argv' in data: - main_kws['sys_argv'] = data['sys_argv'] if self._preload_on_error != 'ignore': main_kws['on_error'] = self._preload_on_error @@ -197,6 +195,8 @@ def ensure_running(self): exe = spawn.get_executable() args = [exe] + util._args_from_interpreter_flags() args += ['-c', cmd] + if self._preload_modules: + args += data["sys_argv"] pid = util.spawnv_passfds(exe, args, fds_to_pass) except: os.close(alive_w) @@ -282,7 +282,7 @@ def _handle_preload(preload, main_path=None, sys_path=None, sys_argv=None, def main(listener_fd, alive_r, preload, main_path=None, sys_path=None, - *, sys_argv=None, authkey_r=None, on_error='ignore'): + *, authkey_r=None, on_error='ignore'): """Run forkserver.""" if authkey_r is not None: try: @@ -293,6 +293,11 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None, else: authkey = b'' + if preload: + sys_argv = sys.argv[1:] + else: + sys_argv = None + _handle_preload(preload, main_path, sys_path, sys_argv, on_error) util._close_stdin() diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index cc07062eee6f98..34bed9ac1e26a6 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -81,6 +81,11 @@ except ImportError: msvcrt = None +try: + import resource +except ImportError: + resource = None + if support.HAVE_ASAN_FORK_BUG: # gh-89363: Skip multiprocessing tests if Python is built with ASAN to @@ -7106,6 +7111,28 @@ def test_preload_main_sys_argv(self): '', ]) + def test_preload_main_sys_argv_limits(self): + # gh-144503: Check that sys.argv is set before __main__ is pre-loaded + if multiprocessing.get_start_method() != "forkserver": + self.skipTest("forkserver specific test") + + max_str_arglen = 32 * resource.getpagesize() + argv = ["a" * (max_str_arglen - 1), "b"] + name = os.path.join(os.path.dirname(__file__), 'mp_preload_sysargv.py') + _, out, err = test.support.script_helper.assert_python_ok( + name, *argv) + self.assertEqual(err, b'') + + out = out.decode().split("\n") + expected_argv = str(argv) + self.assertEqual(out, [ + f"module:{expected_argv}", + f"fun:{expected_argv}", + f"module:{expected_argv}", + f"fun:{expected_argv}", + '', + ]) + # # Mixins # diff --git a/Misc/NEWS.d/next/Library/2026-02-05-16-29-45.gh-issue-144503.f9sl_I.rst b/Misc/NEWS.d/next/Library/2026-02-05-16-29-45.gh-issue-144503.f9sl_I.rst new file mode 100644 index 00000000000000..684ee1ab716c8b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-05-16-29-45.gh-issue-144503.f9sl_I.rst @@ -0,0 +1,3 @@ +Fix :mod:`multiprocessing` ``forkserver`` bug which prevented starting of +the forkserver if the total length of command line arguments in ``sys.argv`` +exceeded the maximum length of a single command line argument.