Single-threaded re-entrancy via a recursive generator causes an access violation (segfault) in `pairwise_next`
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Crash report
What happened?
The existing partial fix in main (the it = po->it; if (it == NULL) re-read guard after the first tp_iternext call) does not cover the second tp_iternext call, where it is still a borrowed reference. In the single-threaded case a re-entrant call can trigger Py_CLEAR(po->it) which drops the generator's refcount to zero and frees it, leaving the outer call's local it as a dangling pointer.
Minimal reproducer:
from itertools import pairwise
def g():
yield next(it)
for _ in range(10000):
try:
it = pairwise(g())
next(it)
except ValueError:
pass
Exception ignored in: <generator object g at 0x...>
ValueError: generator already executing
Windows fatal exception: access violation
This is also the root cause of https://github.com/spyder-ide/qtconsole/issues/635.
Some options to address:
Py_INCREF(it)before the secondtp_iternext(it)call +Py_DECREFafter, or- a re-entrancy guard flag (like
teedataobject.running) as suggested by @rhettinger.
Note: Py_BEGIN_CRITICAL_SECTION (added in PR #144489) does not protect against same-thread re-entrancy.
CPython versions tested on:
CPython main branch
Operating systems tested on:
No response
Output from running 'python -VV' on the command line:
No response
Linked PRs
- gh-150589
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
エントリポイント pairwise_next から始め、リンクされた PR gh-150589 を recursive-generator の再現プログラムと併せて確認します。2 回目の tp_iternext 呼び出しが、同一スレッドでの再入中も安全なままであることを確認し、再現プログラムを再実行して、アクセス違反が発生しなくなったことを確認します。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- c, python
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 25/100