-
-
Notifications
You must be signed in to change notification settings - Fork 34.4k
itertools.zip_longest stores NULL in GC-tracked ittuple when iterators exhaust #148181
Description
Crash report
What happened?
itertools.zip_longest stores NULL in its internal ittuple (the tuple of iterators) when an iterator is exhausted, at Modules/itertoolsmodule.c:3847,3877:
PyTuple_SET_ITEM(lz->ittuple, i, NULL);
Py_DECREF(it);The ittuple is GC-tracked and visited by zip_longest_traverse. When exposed via gc.get_referents(), the tuple contains NULL items. On builds without the PyUnicodeWriter_WriteRepr NULL fix (gh-146056), repr() of this tuple segfaults. On builds with the fix, it displays <NULL>.
This is the same class of bug as gh-146056 (NULL items in GC-tracked containers), but in itertools.zip_longest rather than xml.etree.ElementTree.
Reproducer
import itertools
import gc
zl = itertools.zip_longest(iter([1]), iter([1, 2, 3]))
next(zl) # (1, 1) — both iterators alive
next(zl) # (None, 2) — first iterator exhausted, ittuple[0] set to NULL
# Expose the internal ittuple via gc.get_referents
refs = gc.get_referents(zl)
for r in refs:
if isinstance(r, tuple):
print(repr(r)) # Segfault on builds without gh-146056 fix
# Shows (<NULL>, <list_iterator ...>) on builds with itSuggested fix
Replace NULL with Py_None as the sentinel for exhausted iterators. Py_None can never be a valid iterator in the ittuple (passing None as an iterable to zip_longest fails at PyObject_GetIter in zip_longest_new).
--- a/Modules/itertoolsmodule.c
+++ b/Modules/itertoolsmodule.c
@@ -3832,7 +3832,7 @@
for (i=0 ; i < tuplesize ; i++) {
it = PyTuple_GET_ITEM(lz->ittuple, i);
- if (it == NULL) {
+ if (it == Py_None) {
item = Py_NewRef(lz->fillvalue);
} else {
@@ -3844,7 +3844,7 @@
} else {
item = Py_NewRef(lz->fillvalue);
- PyTuple_SET_ITEM(lz->ittuple, i, NULL);
+ PyTuple_SET_ITEM(lz->ittuple, i, Py_NewRef(Py_None));
Py_DECREF(it);
}The same change applies to both the result-reuse path (line ~3835/3847) and the new-result path (line ~3865/3877) — 4 lines total.
Found with assistance from Claude Code, while investigating a related crash reported by @zhuyifei1999.
CPython versions tested on:
CPython main branch, 3.15, 3.14
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a7+ free-threading build (heads/main:4d0e8ee649c, Mar 29 2026, 23:20:29) [Clang 21.1.2 (2ubuntu6)]