Description
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
Projects
Status
Status