Skip to content

gh-144503: Pass sys.argv to forkserver as real argv elements#148194

Merged
gpshead merged 1 commit intopython:mainfrom
gpshead:gh-144503-argv-passthrough
Apr 7, 2026
Merged

gh-144503: Pass sys.argv to forkserver as real argv elements#148194
gpshead merged 1 commit intopython:mainfrom
gpshead:gh-144503-argv-passthrough

Conversation

@gpshead
Copy link
Copy Markdown
Member

@gpshead gpshead commented Apr 7, 2026

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. (well, almost, we consume two more arguments for the -c blah so the forkserver only works if there are slightly less than the maximum consumption of command line arguments the platform allows, 2MiB total a.k.a. 1/4 stack size for argv+envp space on Linux in most default configs... a far less common scenario than sys.argv repr'ing to >128KiB)

Regression introduced by gh-143706 / 298d544 which landed as a bugfix in 3.14.3 and 3.13.12.

This PR is an alternative to #144508, perhaps less risky to backport as a bugfix.

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 pythongh-143706 / 298d544.
@bedevere-bot
Copy link
Copy Markdown

🤖 New build scheduled with the buildbot fleet by @gpshead for commit 77028cd 🤖

Results will be shown at:

https://buildbot.python.org/all/#/grid?branch=refs%2Fpull%2F148194%2Fmerge

If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again.

@gpshead
Copy link
Copy Markdown
Member Author

gpshead commented Apr 7, 2026

buildbot result notes (editing as i see things):

Copy link
Copy Markdown
Member

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this makes sense and feels safer than the alternative.

@gpshead gpshead merged commit 5e9d90b into python:main Apr 7, 2026
94 of 103 checks passed
@miss-islington-app
Copy link
Copy Markdown

Thanks @gpshead for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14.
🐍🍒⛏🤖

@miss-islington-app
Copy link
Copy Markdown

Sorry, @gpshead, I could not cleanly backport this to 3.14 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 5e9d90b615b94469081b39a7b0808fea86c417be 3.14

@miss-islington-app
Copy link
Copy Markdown

Sorry, @gpshead, I could not cleanly backport this to 3.13 due to a conflict.
Please backport using cherry_picker on command line.

cherry_picker 5e9d90b615b94469081b39a7b0808fea86c417be 3.13

gpshead added a commit to gpshead/cpython that referenced this pull request Apr 7, 2026
…ents (pythonGH-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 pythongh-143706 / 298d544.
(cherry picked from commit 5e9d90b)

Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
@bedevere-app
Copy link
Copy Markdown

bedevere-app bot commented Apr 7, 2026

GH-148195 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.14 bugs and security fixes label Apr 7, 2026
gpshead added a commit to gpshead/cpython that referenced this pull request Apr 7, 2026
…ents (pythonGH-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 pythongh-143706 / 298d544.
(cherry picked from commit 5e9d90b)

Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
@bedevere-app
Copy link
Copy Markdown

bedevere-app bot commented Apr 7, 2026

GH-148196 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.13 bugs and security fixes label Apr 7, 2026
@bedevere-bot
Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot x86 Debian Non-Debug with X 3.x (no tier) has failed when building commit 5e9d90b.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1245/builds/7959) and take a look at the build logs.
  4. Check if the failure is related to this commit (5e9d90b) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1245/builds/7959

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/contextlib.py"�[0m, line �[35m85�[0m, in �[35minner�[0m
    return func(*args, **kwds)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m649�[0m, in �[35mtest_interrupt�[0m
    exitcode = self._kill_process(multiprocessing.Process.interrupt)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/contextlib.py"�[0m, line �[35m85�[0m, in �[35minner�[0m
    return func(*args, **kwds)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m630�[0m, in �[35m_kill_process�[0m
    self.assertEqual(�[31mjoin�[0m�[1;31m()�[0m, None)
                     �[31m~~~~�[0m�[1;31m^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m303�[0m, in �[35m__call__�[0m
    return �[31mself.func�[0m�[1;31m(*args, **kwds)�[0m
           �[31m~~~~~~~~~�[0m�[1;31m^^^^^^^^^^^^^^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/multiprocessing/process.py"�[0m, line �[35m156�[0m, in �[35mjoin�[0m
    res = self._popen.wait(timeout)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/multiprocessing/popen_fork.py"�[0m, line �[35m44�[0m, in �[35mwait�[0m
    return �[31mself.poll�[0m�[1;31m(os.WNOHANG if timeout == 0.0 else 0)�[0m
           �[31m~~~~~~~~~�[0m�[1;31m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/multiprocessing/popen_fork.py"�[0m, line �[35m28�[0m, in �[35mpoll�[0m
    pid, sts = �[31mos.waitpid�[0m�[1;31m(self.pid, flag)�[0m
               �[31m~~~~~~~~~~�[0m�[1;31m^^^^^^^^^^^^^^^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m626�[0m, in �[35mhandler�[0m
    raise RuntimeError('join took too long: %s' % p)
�[1;35mRuntimeError�[0m: �[35mjoin took too long: <Process name='Process-158' pid=10431 parent=6517 started daemon>�[0m


Traceback (most recent call last):
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/contextlib.py"�[0m, line �[35m85�[0m, in �[35minner�[0m
    return func(*args, **kwds)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m649�[0m, in �[35mtest_interrupt�[0m
    exitcode = self._kill_process(multiprocessing.Process.interrupt)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/contextlib.py"�[0m, line �[35m85�[0m, in �[35minner�[0m
    return func(*args, **kwds)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m630�[0m, in �[35m_kill_process�[0m
    self.assertEqual(�[31mjoin�[0m�[1;31m()�[0m, None)
                     �[31m~~~~�[0m�[1;31m^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m303�[0m, in �[35m__call__�[0m
    return �[31mself.func�[0m�[1;31m(*args, **kwds)�[0m
           �[31m~~~~~~~~~�[0m�[1;31m^^^^^^^^^^^^^^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/multiprocessing/process.py"�[0m, line �[35m156�[0m, in �[35mjoin�[0m
    res = self._popen.wait(timeout)
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/multiprocessing/popen_fork.py"�[0m, line �[35m44�[0m, in �[35mwait�[0m
    return �[31mself.poll�[0m�[1;31m(os.WNOHANG if timeout == 0.0 else 0)�[0m
           �[31m~~~~~~~~~�[0m�[1;31m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/multiprocessing/popen_fork.py"�[0m, line �[35m28�[0m, in �[35mpoll�[0m
    pid, sts = �[31mos.waitpid�[0m�[1;31m(self.pid, flag)�[0m
               �[31m~~~~~~~~~~�[0m�[1;31m^^^^^^^^^^^^^^^^�[0m
  File �[35m"/buildbot/buildarea/3.x.ware-debian-x86.nondebug/build/Lib/test/_test_multiprocessing.py"�[0m, line �[35m626�[0m, in �[35mhandler�[0m
    raise RuntimeError('join took too long: %s' % p)
�[1;35mRuntimeError�[0m: �[35mjoin took too long: <Process name='Process-1' pid=29842 parent=29840 started daemon>�[0m

gpshead added a commit that referenced this pull request Apr 7, 2026
…H-148194) (#148195)

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 / 298d544.
(cherry picked from commit 5e9d90b)
gpshead added a commit that referenced this pull request Apr 7, 2026
…H-148194) (#148196)

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 / 298d544.
(cherry picked from commit 5e9d90b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

3 participants