Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: python3...@packages.debian.org, d...@debian.org
Control: affects -1 + src:python3.11
User: release.debian....@packages.debian.org
Usertags: pu

[ Reason ]
A use-after-free causing a SEGV was found in python 3.11, affecting the
the Zulip chat server.

The bug is known to affect python 3.11.0 - 3.11.4. And since being fixed
upstream, there have been no known related regressions.

[ Impact ]
Potential SEGV in python3. Known to be triggered by zulip's CI when
running under coverage.

[ Tests ]
The Python stdlib testsuite is extensive and passes with this patch.

There is a stand-alone reproducer that I've manually reproduced the bug
with and verified that it's fixed.

[ Risks ]
The code is pretty straight-forward. It asserts that the f_frame hasn't
already been freed before freeing.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable
diff -Nru python3.11-3.11.2/debian/changelog python3.11-3.11.2/debian/changelog
--- python3.11-3.11.2/debian/changelog  2023-03-13 08:18:29.000000000 -0400
+++ python3.11-3.11.2/debian/changelog  2024-03-02 16:28:50.000000000 -0400
@@ -1,3 +1,11 @@
+python3.11 (3.11.2-6+deb12u1) bookworm; urgency=medium
+
+  [ Anders Kaseorg ]
+  * Fix a use-after-free crash when deallocating a frame object
+    (closes: #1050843).
+
+ -- Stefano Rivera <stefa...@debian.org>  Sat, 02 Mar 2024 16:28:50 -0400
+
 python3.11 (3.11.2-6) unstable; urgency=high
 
   [ Stefano Rivera ]
diff -Nru python3.11-3.11.2/debian/patches/frame_dealloc-crash.diff 
python3.11-3.11.2/debian/patches/frame_dealloc-crash.diff
--- python3.11-3.11.2/debian/patches/frame_dealloc-crash.diff   1969-12-31 
20:00:00.000000000 -0400
+++ python3.11-3.11.2/debian/patches/frame_dealloc-crash.diff   2024-03-02 
16:28:50.000000000 -0400
@@ -0,0 +1,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 <ande...@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;
diff -Nru python3.11-3.11.2/debian/patches/series 
python3.11-3.11.2/debian/patches/series
--- python3.11-3.11.2/debian/patches/series     2023-03-01 05:58:01.000000000 
-0400
+++ python3.11-3.11.2/debian/patches/series     2024-03-02 16:28:50.000000000 
-0400
@@ -39,3 +39,4 @@
 fix-py_compile.diff
 ntpath-import.diff
 shutdown-deadlock.diff
+frame_dealloc-crash.diff

Reply via email to