cpython/Lib/test/mp_preload_large_sysargv.py
Gregory P. Smith 5e9d90b615
gh-144503: Pass sys.argv to forkserver as real argv elements (GH-148194)
Avoid embedding the parent's sys.argv into the forkserver -c command
string via repr().  When sys.argv is large (e.g. thousands of file
paths from a pre-commit hook), the resulting single argument could
exceed the OS per-argument length limit (MAX_ARG_STRLEN on Linux,
typically 128 KiB), causing posix_spawn to fail and the parent to
observe a BrokenPipeError.

Instead, append the argv entries as separate command-line arguments
after -c; the forkserver child reads them back as sys.argv[1:].  This
cannot exceed any limit the parent itself did not already satisfy.

Regression introduced by gh-143706 / 298d5440eb.
2026-04-06 22:41:02 -07:00

30 lines
1 KiB
Python

# gh-144503: Test that the forkserver can start when the parent process has
# a very large sys.argv. Prior to the fix, sys.argv was repr'd into the
# forkserver ``-c`` command string which could exceed the OS limit on the
# length of a single argv element (MAX_ARG_STRLEN on Linux, ~128 KiB),
# causing posix_spawn to fail and the parent to see a BrokenPipeError.
import multiprocessing
import sys
EXPECTED_LEN = 5002 # argv[0] + 5000 padding entries + sentinel
def fun():
print(f"worker:{len(sys.argv)}:{sys.argv[-1]}")
if __name__ == "__main__":
# Inflate sys.argv well past 128 KiB before the forkserver is started.
sys.argv[1:] = ["x" * 50] * 5000 + ["sentinel"]
assert len(sys.argv) == EXPECTED_LEN
ctx = multiprocessing.get_context("forkserver")
p = ctx.Process(target=fun)
p.start()
p.join()
sys.exit(p.exitcode)
else:
# This branch runs when the forkserver preloads this module as
# __mp_main__; confirm the large argv was propagated intact.
print(f"preload:{len(sys.argv)}:{sys.argv[-1]}")