Skip to content

New generator frames have a dangling previous pointer #97752

Closed
@brandtbucher

Description

@brandtbucher

After RETURN_GENERATOR executes, the new generator's _PyInterpreterFrame has a previous member that still points to the caller's _PyInterpreterFrame. However, this is incorrect; it should be NULL, since the generator's frame isn't actually running anymore. This dangling pointer is dangerous, and can lead to hard crashes of the interpreter. Example:

Python 3.11.0rc2 (tags/v3.11.0rc2:ed7c3ff156, Oct  2 2022, 07:05:44) [GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def g():
...     yield
... 
>>> def f():
...     return g()
... 
>>> gen = f()

This should be None, but instead it refers to a dead _PyInterpreterFrame from the previous call:

>>> gen.gi_frame.f_back
<frame at 0x7f74318c6e80, file '<stdin>', line 2, code f>

Making other calls "updates" this frame, since it just points to an arbitrary location in the stack:

>>> def spam():
...     pass
... 
>>> spam()
>>> gen.gi_frame.f_back
<frame at 0x7f7431ab93f0, file '<stdin>', line 2, code spam>

It's also quite simple to corrupt:

>>> del spam
>>> gen.gi_frame.f_back
<frame at 0x7f7431ab93f0, file '<stdin>', line 1629515630, code '<stdin>'>
>>> gen.gi_frame.f_back.f_code
Segmentation fault

This bug also appears to affect PyAsyncGen_New, PyCoro_New, PyGen_New, and PyGen_NewWithQualName.

The fix is simple: set frame->previous to NULL after calls to _PyFrame_Copy. I'll open a PR at the sprint tomorrow.

Metadata

Metadata

Assignees

Labels

3.11only security fixes3.12only security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)release-blockersprinttype-crashA hard crash of the interpreter, possibly with a core dump

Projects

Status

Done

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions