https://github.com/python/cpython/commit/8611f74e089d9ac9de84dd42be9d251db27889aa
commit: 8611f74e089d9ac9de84dd42be9d251db27889aa
branch: main
author: Peter Bierma <[email protected]>
committer: ZeroIntensity <[email protected]>
date: 2025-12-25T16:31:41Z
summary:

gh-142975: During GC, mark frozen objects with a merged zero refcount for 
destruction (GH-143156)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst
M Lib/test/test_free_threading/test_gc.py
M Python/gc_free_threading.c

diff --git a/Lib/test/test_free_threading/test_gc.py 
b/Lib/test/test_free_threading/test_gc.py
index 3b83e0431efa6b..8b45b6e2150c28 100644
--- a/Lib/test/test_free_threading/test_gc.py
+++ b/Lib/test/test_free_threading/test_gc.py
@@ -62,6 +62,38 @@ def mutator_thread():
         with threading_helper.start_threads(gcs + mutators):
             pass
 
+    def test_freeze_object_in_brc_queue(self):
+        # GH-142975: Freezing objects in the BRC queue could result in some
+        # objects having a zero refcount without being deallocated.
+
+        class Weird:
+            # We need a destructor to trigger the check for object resurrection
+            def __del__(self):
+                pass
+
+        # This is owned by the main thread, so the subthread will have to 
increment
+        # this object's reference count.
+        weird = Weird()
+
+        def evil():
+            gc.freeze()
+
+            # Decrement the reference count from this thread, which will 
trigger the
+            # slow path during resurrection and add our weird object to the 
BRC queue.
+            nonlocal weird
+            del weird
+
+            # Collection will merge the object's reference count and make it 
zero.
+            gc.collect()
+
+            # Unfreeze the object, making it visible to the GC.
+            gc.unfreeze()
+            gc.collect()
+
+        thread = Thread(target=evil)
+        thread.start()
+        thread.join()
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst
new file mode 100644
index 00000000000000..9d7f57ee60aa47
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-24-13-44-24.gh-issue-142975.8C4vIP.rst
@@ -0,0 +1,2 @@
+Fix crash after unfreezing all objects tracked by the garbage collector on
+the :term:`free threaded <free threading>` build.
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 04b9b8f3f85603..51261cea0cfe2c 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -906,7 +906,11 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState 
*interp, gc_mark_args_t *ar
 static void
 queue_untracked_obj_decref(PyObject *op, struct collection_state *state)
 {
-    if (!_PyObject_GC_IS_TRACKED(op)) {
+    assert(Py_REFCNT(op) == 0);
+    // gh-142975: We have to treat frozen objects as untracked in this function
+    // or else they might be picked up in a future collection, which breaks the
+    // assumption that all incoming objects have a non-zero reference count.
+    if (!_PyObject_GC_IS_TRACKED(op) || gc_is_frozen(op)) {
         // GC objects with zero refcount are handled subsequently by the
         // GC as if they were cyclic trash, but we have to handle dead
         // non-GC objects here. Add one to the refcount so that we can

_______________________________________________
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