summaryrefslogtreecommitdiffstats
path: root/Objects/frameobject.c
diff options
context:
space:
mode:
authorAnders Kaseorg <andersk@mit.edu>2023-08-01 09:32:18 (GMT)
committerGitHub <noreply@github.com>2023-08-01 09:32:18 (GMT)
commit557b05c7a5334de5da3dc94c108c0121f10b9191 (patch)
tree84c4eaea106080031c7f7a1110b9ac546afb1b76 /Objects/frameobject.c
parent052a0d1106fa3ee0c955a3b7ba48e82c49424e20 (diff)
downloadcpython-557b05c7a5334de5da3dc94c108c0121f10b9191.zip
cpython-557b05c7a5334de5da3dc94c108c0121f10b9191.tar.gz
cpython-557b05c7a5334de5da3dc94c108c0121f10b9191.tar.bz2
gh-106092: Fix use-after-free crash in frame_dealloc (#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. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
Diffstat (limited to 'Objects/frameobject.c')
-rw-r--r--Objects/frameobject.c13
1 files changed, 7 insertions, 6 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index cc9ac4b..1757153 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -879,9 +879,6 @@ frame_dealloc(PyFrameObject *f)
/* It is the responsibility of the owning generator/coroutine
* 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)) {
_PyObject_GC_UNTRACK(f);
}
@@ -889,10 +886,14 @@ frame_dealloc(PyFrameObject *f)
Py_TRASHCAN_BEGIN(f, frame_dealloc);
PyObject *co = NULL;
+ /* GH-106092: If f->f_frame was on the stack and we reached the maximum
+ * nesting depth for deallocations, the trashcan may have delayed this
+ * 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;
+
/* Kill all local variables including specials, if we own them */
- if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
- assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data);
- _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data;
+ if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
/* Don't clear code object until the end */
co = frame->f_executable;
frame->f_executable = NULL;