Fix random Segmentation fault crashes on Alpine images (#8733) - #8779
Merged
Conversation
MegaLinter could die with "Segmentation fault (core dumped)" and no error message at all. The Alpine images run on musl, which gives every thread a 128 KiB stack instead of glibc's 8 MiB, and CPython below 3.14.7 miscomputed its stack guard there (python/cpython#148260). C-level recursion in a thread then crashed the process with SIGSEGV instead of raising RecursionError. multiprocessing.Pool pickles the linter object graph inside its handler threads, and every Linter reaches the whole Megalinter instance through Linter.master, so that pickling is exactly such a recursion. - Raise the thread stack size to 8 MiB before any thread is started - Enable faulthandler in the main process and in pool workers, so other fatal signals produce a traceback instead of silence - Assert a Python 3.14.7 floor at Docker build time - Close the git.Repo in list_updated_files/is_git_repo, which leaked one "git cat-file --batch" child process per fixer linter Verified on python:3.14.6-alpine: pickling a deeply nested object in a thread exits with signal 11, and either raising the thread stack size or moving to python:3.14.7-alpine turns it into a plain RecursionError.
nvuillam
requested review from
Kurt-von-Laven,
bdovaz and
echoix
as code owners
August 22, 2026 19:14
Contributor
✅
|
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fixes #8733
Problem
MegaLinter could die with
Segmentation fault (core dumped)and no error message at all:The crashing process is MegaLinter itself, not a linter.
Root cause
The Alpine images run on musl, which gives every thread a 128 KiB stack instead of glibc's 8 MiB, and CPython below 3.14.7 miscomputed its stack guard there (python/cpython#148260, #151546). C-level recursion in a thread then crashes the process with
SIGSEGVinstead of raisingRecursionError.multiprocessing.Poolpickles the linter object graph inside its handler threads, and everyLinterreaches the wholeMegalinterinstance throughLinter.master, so that pickling is exactly such a recursion.The timeline matches the report: CPython 3.14.7 was released 2026-08-05, after v9.6.0 was built on 2026-06-28, so v9.6.0 shipped the buggy configuration.
Note this is C-level recursion only. Pure Python recursion is unaffected since CPython 3.11 moved Python frames off the C stack, which is why this never showed up as a
RecursionErrorfirst.Verification
Reproduced in Docker by pickling a deeply nested object inside a thread:
python:3.14.6-alpinepython:3.14.6-alpineRecursionErrorpython:3.14.7-alpineRecursionErrorpython:3.14.7-alpineRecursionErrorEither remediation fixes it independently, so this holds whichever patch level the base image resolves to.
Changes
faulthandlerin the main process and in pool workers, so other fatal signals produce a traceback instead of silenceRUNso no extra layer. The tag stays floating becauserenovate.json5scopes thedockerfilemanager away from the mainDockerfile, whoseFROMlines are generated from descriptorsgit.Repoinlist_updated_files/is_git_repo, which leaked onegit cat-file --batchchild process per fixer linter — this is the call visible in the issue's logThe 150 Dockerfiles are regenerated by
build.sh; all carry the same single hunk and noARGversion line is touched.Caveat worth knowing
faulthandlercannot report a stack overflow — the handler has no stack left to run on. Verified: it prints nothing for this crash, while it does report ordinary segfaults. So the diagnostics help every other fatal signal, and the stack size increase is what actually fixes this one.Follow-ups (not in this PR)
forkserver(the Linux default since Python 3.14), workers inherit no logging handler, soinit_worker'sbasicConfigfallback sends worker output straight to stdout, bypassing theQueueHandlertunnel and keeping worker records out of the log file. Needs verification.apply_asyncpickles the wholeMegalintergraph once per linter group because of theLinter.masterback-reference — a real serialization cost. A__getstate__droppingmasteris the obvious remedy but workers do useself.master.