[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 557b05c7a5)

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
Co-authored-by: Anders Kaseorg <andersk@mit.edu>
This commit is contained in:
Miss Islington (bot) 2023-08-01 03:42:55 -07:00 committed by GitHub
parent fc4532a55d
commit b68faa3fa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 9 additions and 6 deletions

View file

@ -0,0 +1,2 @@
Fix a segmentation fault caused by a use-after-free bug in ``frame_dealloc``
when the trashcan delays the deallocation of a ``PyFrameObject``.

View file

@ -878,9 +878,6 @@ frame_dealloc(PyFrameObject *f)
/* It is the responsibility of the owning generator/coroutine /* It is the responsibility of the owning generator/coroutine
* to have cleared the generator pointer */ * to have cleared the generator pointer */
assert(f->f_frame->owner != FRAME_OWNED_BY_GENERATOR ||
_PyFrame_GetGenerator(f->f_frame)->gi_frame_state == FRAME_CLEARED);
if (_PyObject_GC_IS_TRACKED(f)) { if (_PyObject_GC_IS_TRACKED(f)) {
_PyObject_GC_UNTRACK(f); _PyObject_GC_UNTRACK(f);
} }
@ -888,10 +885,14 @@ frame_dealloc(PyFrameObject *f)
Py_TRASHCAN_BEGIN(f, frame_dealloc); Py_TRASHCAN_BEGIN(f, frame_dealloc);
PyCodeObject *co = NULL; PyCodeObject *co = NULL;
/* Kill all local variables including specials, if we own them */ /* GH-106092: If f->f_frame was on the stack and we reached the maximum
if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { * nesting depth for deallocations, the trashcan may have delayed this
assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data); * deallocation until after f->f_frame is freed. Avoid dereferencing
* f->f_frame unless we know it still points to valid memory. */
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data; _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data;
/* Kill all local variables including specials, if we own them */
if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
/* Don't clear code object until the end */ /* Don't clear code object until the end */
co = frame->f_code; co = frame->f_code;
frame->f_code = NULL; frame->f_code = NULL;