Segfault: dealloc of uninitialized iterator in template_iter (Objects/templateobject.c:232)
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 77.2k
- Forks
- 35.9k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Crash report
Segfault: dealloc of uninitialized iterator in template_iter (Objects/templateobject.c:232)
template_iter() allocates the t-string iterator with PyObject_GC_New (which does not zero the new object) and only assigns iter->stringsiter and iter->interpolationsiter after both PyObject_GetIter calls succeed. If either PyObject_GetIter fails under memory pressure, the error-path Py_DECREF(iter) runs templateiter_dealloc → templateiter_clear, which Py_CLEARs the still-uninitialized (garbage / ASan-poisoned) pointers, causing a segfault.
Reproducer
(needs CPython 3.14+ for t-string / PEP 750 syntax)
from _testcapi import set_nomemory, remove_mem_hooks
t = t"x{1}y{2}z"
for start in range(1, 1000):
set_nomemory(start, 0)
try:
try:
iter(t)
finally:
remove_mem_hooks()
except MemoryError:
pass
Backtrace
#0 _Py_atomic_load_uint32_relaxed
#1 Py_DECREF
#2 templateiter_clear Objects/templateobject.c:53 # Py_CLEAR(self->stringsiter), uninitialized
#3 templateiter_dealloc Objects/templateobject.c:45
#4 _Py_Dealloc
#5 Py_DECREF
#6 template_iter Objects/templateobject.c:232 # Py_DECREF(iter) on the error path
#7 PyObject_GetIter
Crashes deterministically on debug+ASan and JIT debug+ASan builds. On non-ASan release builds it usually exits cleanly within the swept budget (the uninitialized memory often happens to be zero), but the underlying access of uninitialized fields is still incorrect.
Root cause
Objects/templateobject.c, template_iter:
templateiterobject *iter = PyObject_GC_New(templateiterobject, &_PyTemplateIter_Type); /* no zeroing */
if (iter == NULL) {
return NULL;
}
PyObject *stringsiter = PyObject_GetIter(self->strings);
if (stringsiter == NULL) {
Py_DECREF(iter); /* iter->stringsiter / ->interpolationsiter are uninitialized */
return NULL;
}
PyObject *interpolationsiter = PyObject_GetIter(self->interpolations);
if (interpolationsiter == NULL) {
Py_DECREF(iter); /* same: iter->interpolationsiter is uninitialized */
Py_DECREF(stringsiter);
return NULL;
}
Suggested fix
Initialize iter->stringsiter and iter->interpolationsiter to NULL immediately after PyObject_GC_New, so the partial-construction error paths can safely run templateiter_clear (which uses Py_CLEAR, NULL-safe):
templateiterobject *iter = PyObject_GC_New(templateiterobject, &_PyTemplateIter_Type);
if (iter == NULL) {
return NULL;
}
iter->stringsiter = NULL;
iter->interpolationsiter = NULL;
Notes
Part of #151763 (umbrella tracking 35 OOM-related crash findings); OOM-0024 in that table.
CPython versions tested on:
CPython main branch (3.16.0a0)
Operating systems tested on:
Linux, Windows
Linked PRs
- gh-151821
- gh-154714
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne in Objects/templateobject.c bei template_iter und lies anschließend templateiter_clear und templateiter_dealloc, um den fehlschlagenden Bereinigungspfad zu verstehen. Führe den bereitgestellten t-string-Speicherdruck-Reproduzierer in einem debug+ASan-Build aus und überprüfe, dass die Iterator-Fehlerpfade nicht mehr auf nicht initialisierte Felder zugreifen oder abstürzen.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- backend
- Issue-Typ
- Bug
- Schwierigkeit
- 1/5
- Geschätzter Aufwand
- Unter einer Stunde
- Aktivitätsstatus
- Veraltet
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 35/100