Segfault: dealloc of uninitialized iterator in template_iter (Objects/templateobject.c:232)
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 77.2k
- Fork
- 35.9k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
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
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia in Objects/templateobject.c, da template_iter, poi leggi templateiter_clear e templateiter_dealloc per comprendere il percorso di pulizia che fallisce. Esegui il riproduttore della pressione di memoria t-string fornito in una build debug+ASan e verifica che i percorsi di errore dell'iteratore non accedano più a campi non inizializzati né causino crash.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- python
- Ambito
- backend
- Tipo di issue
- Bug
- Difficoltà
- 1/5
- Tempo stimato
- Meno di un'ora
- Stato di attività
- Ferma
- Chiarezza
- Specificata chiaramente
- Idoneità per principianti
- 35/100