diff options
author | Brandt Bucher <brandtbucher@microsoft.com> | 2022-12-06 09:02:19 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-06 17:02:19 +0000 |
commit | 2182a71eedbc8e95c3cf2d8c0aa2fd66c7a93db4 (patch) | |
tree | 07a441ad880a7e112578a6ae6f3f0b95e67cd014 /Python | |
parent | 3fae04b10e2655a20a3aadb5e0d63e87206d0c67 (diff) | |
download | cpython3-2182a71eedbc8e95c3cf2d8c0aa2fd66c7a93db4.tar.gz |
[3.11] GH-99729: Unlink frames before clearing them (#100047)
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ceval.c | 18 | ||||
-rw-r--r-- | Python/frame.c | 3 |
2 files changed, 11 insertions, 10 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index 8cbe838ddf..a34e4ffb72 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1617,14 +1617,6 @@ trace_function_exit(PyThreadState *tstate, _PyInterpreterFrame *frame, PyObject return 0; } -static _PyInterpreterFrame * -pop_frame(PyThreadState *tstate, _PyInterpreterFrame *frame) -{ - _PyInterpreterFrame *prev_frame = frame->previous; - _PyEvalFrameClearAndPop(tstate, frame); - return prev_frame; -} - /* It is only between the PRECALL instruction and the following CALL, * that this has any meaning. */ @@ -2441,7 +2433,10 @@ handle_eval_breaker: DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallTstate(tstate); if (!frame->is_entry) { - frame = cframe.current_frame = pop_frame(tstate, frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = cframe.current_frame = dying->previous; + _PyEvalFrameClearAndPop(tstate, dying); _PyFrame_StackPush(frame, retval); goto resume_frame; } @@ -5833,7 +5828,10 @@ exit_unwind: assert(tstate->cframe->current_frame == frame->previous); return NULL; } - frame = cframe.current_frame = pop_frame(tstate, frame); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = cframe.current_frame = dying->previous; + _PyEvalFrameClearAndPop(tstate, dying); resume_with_error: SET_LOCALS_FROM_FRAME(); diff --git a/Python/frame.c b/Python/frame.c index d8f2f801f3..3ea3a2ced4 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -123,6 +123,9 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) * to have cleared the enclosing generator, if any. */ assert(frame->owner != FRAME_OWNED_BY_GENERATOR || _PyFrame_GetGenerator(frame)->gi_frame_state == FRAME_CLEARED); + // GH-99729: Clearing this frame can expose the stack (via finalizers). It's + // crucial that this frame has been unlinked, and is no longer visible: + assert(_PyThreadState_GET()->cframe->current_frame != frame); if (frame->frame_obj) { PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; |