Skip to content

Commit b68faa3

Browse files
[3.12] gh-106092: Fix use-after-free crash in frame_dealloc (GH-106875) (#107532)
gh-106092: Fix use-after-free crash in frame_dealloc (GH-106875) It was possible for the trashcan to delay the deallocation of a PyFrameObject until after its corresponding _PyInterpreterFrame has already been freed. So frame_dealloc needs to avoid dereferencing the f_frame pointer unless it first checks that the pointer still points to the interpreter frame within the frame object. (cherry picked from commit 557b05c) Signed-off-by: Anders Kaseorg <[email protected]> Co-authored-by: Anders Kaseorg <[email protected]>
1 parent fc4532a commit b68faa3

File tree

2 files changed

+9
-6
lines changed

2 files changed

+9
-6
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a segmentation fault caused by a use-after-free bug in ``frame_dealloc``
2+
when the trashcan delays the deallocation of a ``PyFrameObject``.

Objects/frameobject.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -878,20 +878,21 @@ frame_dealloc(PyFrameObject *f)
878878
/* It is the responsibility of the owning generator/coroutine
879879
* to have cleared the generator pointer */
880880

881-
assert(f->f_frame->owner != FRAME_OWNED_BY_GENERATOR ||
882-
_PyFrame_GetGenerator(f->f_frame)->gi_frame_state == FRAME_CLEARED);
883-
884881
if (_PyObject_GC_IS_TRACKED(f)) {
885882
_PyObject_GC_UNTRACK(f);
886883
}
887884

888885
Py_TRASHCAN_BEGIN(f, frame_dealloc);
889886
PyCodeObject *co = NULL;
890887

888+
/* GH-106092: If f->f_frame was on the stack and we reached the maximum
889+
* nesting depth for deallocations, the trashcan may have delayed this
890+
* deallocation until after f->f_frame is freed. Avoid dereferencing
891+
* f->f_frame unless we know it still points to valid memory. */
892+
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data;
893+
891894
/* Kill all local variables including specials, if we own them */
892-
if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
893-
assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data);
894-
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data;
895+
if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
895896
/* Don't clear code object until the end */
896897
co = frame->f_code;
897898
frame->f_code = NULL;

0 commit comments

Comments
 (0)