Package: python3.11 / 3.11.2-6+deb12u6

frame_dealloc-crash.diff Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Description: Fix use-after-free crash in frame_dealloc
 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.
Origin: https://github.com/python/cpython/commit/46cae02085311481dc8b1ea9a5110969d9325bc7
Bug-upstream: https://github.com/python/cpython/issues/106092
Bug-Debian: https://bugs.debian.org/1050843
Author: Anders Kaseorg <andersk@mit.edu>
Last-Update: 2023-08-29
Applied-Upstream: 3.11.5

---
 .../2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst  |  2 ++
 Objects/frameobject.c                               | 13 +++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst

--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-18-16-13-51.gh-issue-106092.bObgRM.rst
@@ -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``.
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -851,9 +851,6 @@
     /* 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);
     }
@@ -861,10 +858,14 @@
     Py_TRASHCAN_BEGIN(f, frame_dealloc);
     PyCodeObject *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_code;
         frame->f_code = NULL;