python / python/cpython

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

オープン
#151,815 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

interpreter-core type-crash
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

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

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

Objects/templateobject.c の template_iter から始め、templateiter_clear と templateiter_dealloc を読んで、失敗しているクリーンアップパスを理解します。提供されている t-string メモリプレッシャー再現プログラムを debug+ASan ビルドで実行し、イテレーターのエラーパスが未初期化フィールドにアクセスしたりクラッシュしたりしなくなったことを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
backend
issue の種類
バグ
難易度
1/5
見積もり時間
1時間未満
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。