Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 92ca515

Browse files
miss-islingtonnascheme
authored andcommitted
Clear weakrefs in garbage found by the GC (pythonGH-16495) (python#16499)
Fix a bug due to the interaction of weakrefs and the cyclic garbage collector. We must clear any weakrefs in garbage in order to prevent their callbacks from executing and causing a crash. (cherry picked from commit bcda460) Co-authored-by: Neil Schemenauer <[email protected]>
1 parent 18c4ba9 commit 92ca515

File tree

2 files changed

+20
-0
lines changed

2 files changed

+20
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a bug due to the interaction of weakrefs and the cyclic garbage
2+
collector. We must clear any weakrefs in garbage in order to prevent their
3+
callbacks from executing and causing a crash.

Modules/gcmodule.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,21 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
678678
op = FROM_GC(gc);
679679
next = GC_NEXT(gc);
680680

681+
if (PyWeakref_Check(op)) {
682+
/* A weakref inside the unreachable set must be cleared. If we
683+
* allow its callback to execute inside delete_garbage(), it
684+
* could expose objects that have tp_clear already called on
685+
* them. Or, it could resurrect unreachable objects. One way
686+
* this can happen is if some container objects do not implement
687+
* tp_traverse. Then, wr_object can be outside the unreachable
688+
* set but can be deallocated as a result of breaking the
689+
* reference cycle. If we don't clear the weakref, the callback
690+
* will run and potentially cause a crash. See bpo-38006 for
691+
* one example.
692+
*/
693+
_PyWeakref_ClearRef((PyWeakReference *)op);
694+
}
695+
681696
if (! PyType_SUPPORTS_WEAKREFS(Py_TYPE(op)))
682697
continue;
683698

@@ -733,6 +748,8 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
733748
* is moved to wrcb_to_call in this case.
734749
*/
735750
if (gc_is_collecting(AS_GC(wr))) {
751+
/* it should already have been cleared above */
752+
assert(wr->wr_object == Py_None);
736753
continue;
737754
}
738755

0 commit comments

Comments
 (0)