Single-threaded re-entrancy via a recursive generator causes an access violation (segfault) in `pairwise_next`
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 77.2k
- Forks
- 35.9k
- PR merge metrics
- PR metrics pending
Description
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the pairwise_next entry point and review linked PR gh-150589 alongside the recursive-generator reproducer. Verify that the second tp_iternext call remains safe during same-thread re-entrancy, and rerun the reproducer to confirm it no longer causes an access violation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100