gh-141044: Fix ASan leak with small threading.stack_size() - #157691
Open
KingLizard1020 wants to merge 20 commits into
Open
KingLizard1020 wants to merge 20 commits into
KingLizard1020 wants to merge 20 commits into
Conversation
KingLizard1020
requested review from
AA-Turner,
JacobCoffee,
ezio-melotti,
hugovk,
itamaro and
webknjaz
as code owners
September 17, 2026 17:34
AddressSanitizer instrumentation consumes extra C stack, so the old minimum left no working space above the soft recursion limit. threading.Thread bootstrap then leaked thread objects. Require 6 stack margins under ASan, matching TSan. Cover the unjoined original repro and confirm the new minimum does not leak. Move the NEWS blurb to Library, matching pythongh-143191.
github-actions
Bot
force-pushed
the
cursor/fix-threading-stack-size-leak-a999
branch
from
September 17, 2026 17:36
1f5b1e7 to
00fe666
Compare
hugovk
removed request for
AA-Turner,
JacobCoffee,
ezio-melotti,
hugovk,
itamaro and
webknjaz
September 18, 2026 10:19
Py_DEBUG (and UBSan) share the larger stack margin with ASan/TSan, but still used a 3-margin minimum. threading.Thread bootstrap then leaked references at that floor; the new refcount test failed on Ubuntu/macOS CI. Align the minimum with ASan/TSan. Release builds stay at 3 margins.
TSan only uses half the stack in tstate_set_stack(), so the 6-margin minimum that is enough for ASan/debug still leaked Thread objects at the floor under ThreadSanitizer (refcount delta ~355). Require 12 margins for TSan. Update tests that assumed 256 KiB is always accepted.
Docs CI checks out the PR head, so register soft-deprecated and deprecated-removed in ChangesBuilder.typemap (pythongh-155095).
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.
Fix a LeakSanitizer / reference leak when creating a
threading.Threadafterthreading.stack_size()is set to a small value (the reported repro used 127 KiB and did not join).Issue: #141044
Diagnosis
This is a real reference leak, not an LSan false positive. With a debug + AddressSanitizer build:
threading.stack_size(127 * 1024)followed byThread.start()(with or withoutjoin()) leaksThread/_ThreadHandle/Contextobjects._thread.start_new_thread()at 127 KiB does not leak;threading.Threaddoes, because bootstrap runs_context.run(self.run)thenfinally: self._delete().C stack overflow protection (gh-130396) reserves soft/hard margins at the bottom of each thread stack. Under ASan, instrumentation consumes extra C frames, so the old 3-margin minimum left almost no working space above the soft limit. At 127 KiB,
_testinternalcapi.get_c_recursion_remaining()was already ~50 and bootstrap failed to finish cleanup.The new
test_stack_size_no_leakalso failed on Py_DEBUG (non-ASan) CI at the old 3-margin floor (~102 KiB): eight threads still leaked ~355 refs. Clean from ~139 KiB upward; 6 margins (~200 KiB) is clean.3.13 did not use
pthread_getattr_npstack limits, which is why it showed no leak.Fix
When built with Py_DEBUG, AddressSanitizer, ThreadSanitizer, or UndefinedBehaviorSanitizer (same set as
_PyOS_LOG2_STACK_MARGIN), require 6 stack margins for_PyOS_MIN_STACK_SIZE. Release builds stay at 3. Reasons differ and are documented in the header: TSan only uses half the stack intstate_set_stack(); ASan/debug need extra room for instrumentation / checked builds._thread.stack_size()already rejects sizes below_PyOS_MIN_STACK_SIZE + SYSTEM_PAGE_SIZE, so 127 KiB is now aValueErroron those builds (~200704 byte minimum on this builder).Tests
test_thread.ThreadRunningTests.test_stack_size: ASan/debugValueErrorfor127 * 1024, and a failed set must leave size at 0.test_threading.ThreadTests.test_stack_size_no_leak: original unjoined 127 KiB repro (exits cleanly when rejected), unjoined + joined threads at the new minimum withgettotalrefcount()andASAN_OPTIONS=detect_leaks=1.Verification
Built with
./configure --with-address-sanitizer --with-pydebug && make -j, and separately with pydebug alone.Before: LSan / refcount leaks on the issue reproducer and on the new minimum under Py_DEBUG. After:
threading.stack_size(127 * 1024)raisesValueErrorwhere the floor applies; threads at the new minimum are clean under refcount + LSan (joined and unjoined).Local:
./python -m test test_thread test_threading -v -m '*stack_size*'→ SUCCESS.