Repository navigation
_testcapi._tuple_resize() leaks item references on allocation failure #159514
Description
Bug report
_PyTuple_Resize() leaks references to the tuple's elements when the underlying PyObject_GC_Resize() fails. It sets the caller's pointer to NULL and frees the old tuple storage with PyObject_GC_Del(), but does not release the references held in that storage.
The old allocation remains valid after the failed resize, so its remaining elements must be decref'ed before it is freed. For a shrinking resize, the removed tail entries have already been cleared; the retained entries still need cleanup if resizing fails.
Reproducer
Run this with a build that includes _testcapi. The helper creates a private copy of the input tuple, satisfying _PyTuple_Resize()'s ownership requirement. The requested size exceeds the GC allocator's checked size limit because of header overhead; this does not depend on exhausting available RAM.
import gc
import struct
import sys
import weakref
import _testcapi
class Item:
pass
item = Item()
original = (item,)
ref = weakref.ref(item)
try:
# The helper creates a private copy of the tuple before resizing it.
# Header overhead makes this exceed the GC allocator's size limit.
_testcapi._tuple_resize(original, sys.maxsize // struct.calcsize("P"))
except MemoryError:
pass
else:
raise AssertionError("expected MemoryError")
del original, item
gc.collect()
print("element released:", ref() is None)
assert ref() is NoneExpected: element released: True.
Actual: element released: False, followed by AssertionError.
CPython versions tested on
Reproduced with the unmodified runtime from CPython main at 21a36aa79ca0b2af49995fa1b9216b01ef98f195 (3.16.0a1+dev), with --with-pydebug. Only the new regression test was added; no runtime code was changed for the failing run.
Source inspection also confirms the same cleanup path in release tags v3.14.8 and v3.15.0. Those versions were not executed for this report.
Operating systems tested on
macOS 27.0.1, Apple Silicon.
Related reports
I searched for an existing report. #155053 concerns a caller dereferencing NULL after resize failure; its proposed fix does not address these leaked element references.
The bug was manually spotted and investigated, but AI tools assisted with the investigation, reproducer, and proposed fix. The reproducer and regression test were executed locally before submission.
Activity
sergey-miryanov commentedon Oct 16, 2026
Would you like to open a PR?
[-]_PyTuple_Resize() leaks item references on allocation failure[/-][+]_testcapi._tuple_resize() leaks item references on allocation failure[/+]vstinner commentedon Oct 16, 2026
The leak is in
_testcapi._tuple_resize()wrapper (on error), not in_PyTuple_Resize().pythongh-159514: Use the current NEWS category directory
lagergren commentedon Oct 16, 2026
Looks like someone did already?
sergey-miryanov commentedon Oct 16, 2026
@zolaski333 Thanks for your time and effort. But it's usually best to check with the original issue author first to see if they're already working on it.
lagergren-oai commentedon Oct 19, 2026
Thanks, Sergey and Noah. I've compared the patch in #159526 with my fix. It implements the same cleanup in
_PyTuple_Resize()and includes a regression test for the allocation-failure leak.I'm happy to proceed with Noah's PR as the upstream fix, so I won't open a duplicate. Thanks @zolaski333 for taking this forward, and @sergey-miryanov for checking with me.
sergey-miryanov commentedon Oct 28, 2026
Fixed by #159754