python / python/cpython

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

Open
#151,815 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

interpreter-core type-crash
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Objects/templateobject.c at template_iter, then read templateiter_clear and templateiter_dealloc to understand the failing cleanup path. Run the provided t-string memory-pressure reproducer on a debug+ASan build and verify that the iterator error paths no longer access uninitialized fields or crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.