Skip to content

Commit 5b71873

Browse files
committed
Unlink frames before clearing them
1 parent 3586f17 commit 5b71873

File tree

2 files changed

+11
-10
lines changed

2 files changed

+11
-10
lines changed

Python/ceval.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,14 +1617,6 @@ trace_function_exit(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject
16171617
return 0;
16181618
}
16191619

1620-
static _PyInterpreterFrame *
1621-
pop_frame(PyThreadState *tstate, _PyInterpreterFrame *frame)
1622-
{
1623-
_PyInterpreterFrame *prev_frame = frame->previous;
1624-
_PyEvalFrameClearAndPop(tstate, frame);
1625-
return prev_frame;
1626-
}
1627-
16281620
/* It is only between the PRECALL instruction and the following CALL,
16291621
* that this has any meaning.
16301622
*/
@@ -2441,7 +2433,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
24412433
DTRACE_FUNCTION_EXIT();
24422434
_Py_LeaveRecursiveCallTstate(tstate);
24432435
if (!frame->is_entry) {
2444-
frame = cframe.current_frame = pop_frame(tstate, frame);
2436+
// GH-99729: We need to unlink the frame *before* clearing it:
2437+
_PyInterpreterFrame *dying = frame;
2438+
frame = cframe.current_frame = dying->previous;
2439+
_PyEvalFrameClearAndPop(tstate, dying);
24452440
_PyFrame_StackPush(frame, retval);
24462441
goto resume_frame;
24472442
}
@@ -5833,7 +5828,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
58335828
assert(tstate->cframe->current_frame == frame->previous);
58345829
return NULL;
58355830
}
5836-
frame = cframe.current_frame = pop_frame(tstate, frame);
5831+
// GH-99729: We need to unlink the frame *before* clearing it:
5832+
_PyInterpreterFrame *dying = frame;
5833+
frame = cframe.current_frame = dying->previous;
5834+
_PyEvalFrameClearAndPop(tstate, dying);
58375835

58385836
resume_with_error:
58395837
SET_LOCALS_FROM_FRAME();

Python/frame.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ _PyFrame_Clear(_PyInterpreterFrame *frame)
123123
* to have cleared the enclosing generator, if any. */
124124
assert(frame->owner != FRAME_OWNED_BY_GENERATOR ||
125125
_PyFrame_GetGenerator(frame)->gi_frame_state == FRAME_CLEARED);
126+
// GH-99729: Clearing this frame can expose the stack (via finalizers). It's
127+
// crucial that this frame has been unlinked, and is no longer visible:
128+
assert(_PyThreadState_GET()->cframe->current_frame != frame);
126129
if (frame->frame_obj) {
127130
PyFrameObject *f = frame->frame_obj;
128131
frame->frame_obj = NULL;

0 commit comments

Comments
 (0)