https://github.com/python/cpython/commit/85a67bafb4e0dfc9eeef5f75d6c961e4f6231ace
commit: 85a67bafb4e0dfc9eeef5f75d6c961e4f6231ace
branch: main
author: Neko Asakura <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-11-06T12:04:13+05:30
summary:

gh-161419: fix weakref reviving a dying object in free-threading (#161431)

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 f6a71fe0417601..a27e13f9c460bf 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -712,6 +712,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 da4155d09f7328..5b4ace36c46143 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
@@ -1056,6 +1057,29 @@ def __del__(self):
         del head
         self.assertEqual(found, [])
 
+    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