Fix a LeakSanitizer / reference leak when creating a threading.Thread after threading.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 by Thread.start() (with or without join()) leaks Thread / _ThreadHandle / Context objects.
- Default stack size / 256 KiB do not leak.
_thread.start_new_thread() at 127 KiB does not leak; threading.Thread does, because bootstrap runs _context.run(self.run) then finally: 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_leak also 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_np stack 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 in tstate_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 a ValueError on those builds (~200704 byte minimum on this builder).
Tests
test_thread.ThreadRunningTests.test_stack_size: ASan/debug ValueError for 127 * 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 with gettotalrefcount() and ASAN_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) raises ValueError where 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.