https://github.com/python/cpython/commit/26f84b0f69f3e0be6b969939511ffee8d0ff3c95
commit: 26f84b0f69f3e0be6b969939511ffee8d0ff3c95
branch: 3.15
author: Neko Asakura <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-11-06T13:17:05Z
summary:

[3.15] gh-161419: fix weakref reviving a dying object in free-threading 
(GH-161431) (#161488)

(cherry picked from commit 85a67bafb4e0dfc9eeef5f75d6c961e4f6231ace)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-11-05-10-19-48.gh-issue-161419.wc4DV0.rst
M Include/internal/pycore_object.h
M Lib/test/test_weakref.py

diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index a15685450f54db..d8b93040b2f98f 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -720,6 +720,8 @@ _PyObject_ResurrectEnd(PyObject *op)
     if (_Py_IsOwnedByCurrentThread(op) && local == 1 && shared == 0) {
         // Fast-path: object has a single refcount and is owned by this thread
         _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 0);
+        _Py_atomic_store_uintptr_relaxed(&op->ob_tid, _Py_UNOWNED_TID);
+        _Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
 # ifdef Py_TRACE_REFS
         _Py_ForgetReference(op);
 # endif
diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py
index ddfc2c395f9a5d..b43a25af32fd85 100644
--- a/Lib/test/test_weakref.py
+++ b/Lib/test/test_weakref.py
@@ -1,4 +1,5 @@
 import gc
+import io
 import sys
 import doctest
 import unittest
@@ -1012,6 +1013,29 @@ def cb(wparent):
         del root
         gc.collect()
 
+    def test_ref_to_object_dying_after_finalizer(self):
+        cleared = []
+        class Attr:
+            def __del__(self):
+                cleared.append(self.wr() is None)
+
+        class SetWithDel(set):
+            __slots__ = ('a',)
+            def __del__(self):
+                pass
+
+        class Raw(io.RawIOBase):
+            __slots__ = ('a',)
+
+        for cls in (SetWithDel, Raw):
+            with self.subTest(cls=cls.__name__):
+                cleared.clear()
+                o = cls()
+                o.a = Attr()
+                o.a.wr = weakref.ref(o)
+                del o
+                self.assertEqual(cleared, [True])
+
     def test_callback_attribute(self):
         x = Object(1)
         callback = lambda ref: None
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-11-05-10-19-48.gh-issue-161419.wc4DV0.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-11-05-10-19-48.gh-issue-161419.wc4DV0.rst
new file mode 100644
index 00000000000000..0d6605a9eeb550
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-11-05-10-19-48.gh-issue-161419.wc4DV0.rst
@@ -0,0 +1,3 @@
+Fix a crash in the :term:`free-threaded build` where a weak reference could
+revive an object whose finalizer had already run during deallocation, causing
+it to be deallocated twice.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to