python / python/cpython

Segfault: dealloc of uninitialized iterator in template_iter (Objects/templateobject.c:232)

Ouverte
#151,815 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

interpreter-core type-crash
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

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_dealloctemplateiter_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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans Objects/templateobject.c, au niveau de template_iter, puis lisez templateiter_clear et templateiter_dealloc pour comprendre le chemin de nettoyage défaillant. Exécutez le reproducteur de pression mémoire t-string fourni dans une build debug+ASan et vérifiez que les chemins d’erreur de l’itérateur n’accèdent plus à des champs non initialisés et ne provoquent plus de crash.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
backend
Type d'issue
Bug
Difficulté
1/5
Temps estimé
Moins d'une heure
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.