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]