Your message dated Sat, 12 Sep 2026 08:05:41 +0000
with message-id <[email protected]>
and subject line Released in 13.7
has caused the Debian Bug report #1142261,
regarding trixie-pu: package python3.13/3.13.5-2+deb13u4
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1142261: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1142261
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:python3.13
User: [email protected]
Usertags: pu

Given the impact, this should go into trixie-updates.

Sorry, python3.13/3.13.5-2+deb13u3 had a use-after-free error, resulting 
in heap corruption, that trips up a number of users. See bug #1141977 
for user reports.

Zigo mentioned to me that it broke nova's test suite, and I verified 
this.

https://debusine.debian.net/debian/developers/work-request/927219/
The build fails with a couple of:

> testtools.testresult.real._StringException

It also broke building python3.13's own documentation, which is a good 
sign that it's a worrying regression.

https://debusine.debian.net/debian/developers/work-request/920956/

[ Reason ]
3.13.5-2+deb13u3 included a cherry-pick to fix a GC bug that affected 
OpenStack and existed in 3.13 -> 3.13.12. This depended on a related 
memory-handling change, that I missed when I grabbed the fix. This 
update includes the missing commit.

It was mentioned in the issue, I forgot to go down the rabbit hole of 
investigating the necessity for it.

[ Impact ]
Regression for many Python users :(

[ Tests ]
* The missing commit brings unit tests.
* Python's test suite passes (as it did for u3, sadface)
  https://debusine.debian.net/debian/r-stefanor-python/work-request/926428/
* Nova's test suite passes.
  https://debusine.debian.net/debian/r-stefanor-python/work-request/927171/
* Users who reported regressions in #1141977 have tested the update and 
  reported success.

[ Risks ]
This seems to make things better, rather than worse.

[ 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

[ Changes ]
* Patch: Fix use-after-free in dict.clear() with embedded values.
  Resolves a regression in 3.13.5-2+deb13u3. (Closes: #1141977)
diff -Nru python3.13-3.13.5/debian/changelog python3.13-3.13.5/debian/changelog
--- python3.13-3.13.5/debian/changelog  2026-06-13 11:18:01.000000000 -0300
+++ python3.13-3.13.5/debian/changelog  2026-07-15 17:25:40.000000000 -0300
@@ -1,3 +1,10 @@
+python3.13 (3.13.5-2+deb13u4) trixie; urgency=medium
+
+  * Patch: Fix use-after-free in dict.clear() with embedded values.
+    Resolves a regression in 3.13.5-2+deb13u3. (Closes: #1141977)
+
+ -- Stefano Rivera <[email protected]>  Wed, 15 Jul 2026 17:25:40 -0300
+
 python3.13 (3.13.5-2+deb13u3) trixie; urgency=medium
 
   [ Stefano Rivera ]
diff -Nru python3.13-3.13.5/debian/patches/dict-use-after-free.patch 
python3.13-3.13.5/debian/patches/dict-use-after-free.patch
--- python3.13-3.13.5/debian/patches/dict-use-after-free.patch  1969-12-31 
21:00:00.000000000 -0300
+++ python3.13-3.13.5/debian/patches/dict-use-after-free.patch  2026-07-15 
17:25:40.000000000 -0300
@@ -0,0 +1,143 @@
+From d567f451e157e241533ec4109fadd29301576060 Mon Sep 17 00:00:00 2001
+From: Sam Gross <[email protected]>
+Date: Mon, 2 Mar 2026 13:59:52 -0500
+Subject: [PATCH] [3.13] gh-130555: Fix use-after-free in dict.clear() with
+ embedded values (gh-145268) (#145430)
+
+---
+ Lib/test/test_dict.py                         | 63 +++++++++++++++++++
+ ...-02-26-12-00-00.gh-issue-130555.TMSOIu.rst |  3 +
+ Objects/dictobject.c                          | 31 ++++++---
+ 3 files changed, 88 insertions(+), 9 deletions(-)
+ create mode 100644 
Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-12-00-00.gh-issue-130555.TMSOIu.rst
+
+--- a/Lib/test/test_dict.py
++++ b/Lib/test/test_dict.py
+@@ -1664,6 +1664,69 @@
+ class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
+     type2test = Dict
+ 
++    def test_clear_reentrant_embedded(self):
++        # gh-130555: dict.clear() must be safe when values are embedded
++        # in an object and a destructor mutates the dict.
++        class MyObj: pass
++        class ClearOnDelete:
++            def __del__(self):
++                nonlocal x
++                del x
++
++        x = MyObj()
++        x.a = ClearOnDelete()
++
++        d = x.__dict__
++        d.clear()
++
++    def test_clear_reentrant_cycle(self):
++        # gh-130555: dict.clear() must be safe for embedded dicts when the
++        # object is part of a reference cycle and the last reference to the
++        # dict is via the cycle.
++        class MyObj: pass
++        obj = MyObj()
++        obj.f = obj
++        obj.attr = "attr"
++
++        d = obj.__dict__
++        del obj
++
++        d.clear()
++
++    def test_clear_reentrant_force_combined(self):
++        # gh-130555: dict.clear() must be safe when a destructor forces the
++        # dict from embedded/split to combined (setting ma_values to NULL).
++        class MyObj: pass
++        class ForceConvert:
++            def __del__(self):
++                d[1] = "trigger"
++
++        x = MyObj()
++        x.a = ForceConvert()
++        x.b = "other"
++
++        d = x.__dict__
++        d.clear()
++
++    def test_clear_reentrant_delete(self):
++        # gh-130555: dict.clear() must be safe when a destructor deletes
++        # a key from the same embedded dict.
++        class MyObj: pass
++        class DelKey:
++            def __del__(self):
++                try:
++                    del d['b']
++                except KeyError:
++                    pass
++
++        x = MyObj()
++        x.a = DelKey()
++        x.b = "value_b"
++        x.c = "value_c"
++
++        d = x.__dict__
++        d.clear()
++
+ 
+ if __name__ == "__main__":
+     unittest.main()
+--- /dev/null
++++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-26-12-00-00.gh-issue-130555.TMSOIu.rst
+@@ -0,0 +1,3 @@
++Fix use-after-free in :meth:`dict.clear` when the dictionary values are
++embedded in an object and a destructor causes re-entrant mutation of the
++dictionary.
+--- a/Objects/dictobject.c
++++ b/Objects/dictobject.c
+@@ -2794,6 +2794,21 @@
+ }
+ 
+ static void
++clear_embedded_values(PyDictValues *values, Py_ssize_t nentries)
++{
++    PyObject *refs[SHARED_KEYS_MAX_SIZE];
++    assert(nentries <= SHARED_KEYS_MAX_SIZE);
++    for (Py_ssize_t i = 0; i < nentries; i++) {
++        refs[i] = values->values[i];
++        values->values[i] = NULL;
++    }
++    values->size = 0;
++    for (Py_ssize_t i = 0; i < nentries; i++) {
++        Py_XDECREF(refs[i]);
++    }
++}
++
++static void
+ clear_lock_held(PyObject *op)
+ {
+     PyDictObject *mp;
+@@ -2824,20 +2839,18 @@
+         assert(oldkeys->dk_refcnt == 1);
+         dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
+     }
++    else if (oldvalues->embedded) {
++        clear_embedded_values(oldvalues, oldkeys->dk_nentries);
++    }
+     else {
++        set_values(mp, NULL);
++        set_keys(mp, Py_EMPTY_KEYS);
+         n = oldkeys->dk_nentries;
+         for (i = 0; i < n; i++) {
+             Py_CLEAR(oldvalues->values[i]);
+         }
+-        if (oldvalues->embedded) {
+-            oldvalues->size = 0;
+-        }
+-        else {
+-            set_values(mp, NULL);
+-            set_keys(mp, Py_EMPTY_KEYS);
+-            free_values(oldvalues, IS_DICT_SHARED(mp));
+-            dictkeys_decref(interp, oldkeys, false);
+-        }
++        free_values(oldvalues, IS_DICT_SHARED(mp));
++        dictkeys_decref(interp, oldkeys, false);
+     }
+     ASSERT_CONSISTENT(mp);
+ }
diff -Nru python3.13-3.13.5/debian/patches/series 
python3.13-3.13.5/debian/patches/series
--- python3.13-3.13.5/debian/patches/series     2026-06-13 11:18:01.000000000 
-0300
+++ python3.13-3.13.5/debian/patches/series     2026-07-15 17:25:40.000000000 
-0300
@@ -51,6 +51,7 @@
 CVE-2026-6100.patch
 ssl-sni-crash.patch
 ssl-reference-leak.patch
+dict-use-after-free.patch
 traverse-managed-dicts.patch
 CVE-2026-1502.patch
 CVE-2026-3276.patch
diff -Nru python3.13-3.13.5/debian/patches/traverse-managed-dicts.patch 
python3.13-3.13.5/debian/patches/traverse-managed-dicts.patch
--- python3.13-3.13.5/debian/patches/traverse-managed-dicts.patch       
2026-06-13 11:18:01.000000000 -0300
+++ python3.13-3.13.5/debian/patches/traverse-managed-dicts.patch       
2026-07-15 17:25:40.000000000 -0300
@@ -50,7 +50,7 @@
 +overwritten at runtime.
 --- a/Objects/dictobject.c
 +++ b/Objects/dictobject.c
-@@ -4553,10 +4553,8 @@
+@@ -4566,10 +4566,8 @@
  
      if (DK_IS_UNICODE(keys)) {
          if (_PyDict_HasSplitTable(mp)) {
@@ -63,7 +63,7 @@
              }
          }
          else {
-@@ -7121,16 +7119,21 @@
+@@ -7134,16 +7132,21 @@
      if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
          return 0;
      }

--- End Message ---
--- Begin Message ---
Version: 13.7

This update was released as part of 13.7.

--- End Message ---

Reply via email to