@@ -858,6 +858,19 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
858858 }
859859}
860860
861+ static bool
862+ should_clear_weakref (PyWeakReference * wr )
863+ {
864+ if (wr -> wr_callback == NULL && PyType_Check (wr -> wr_object )) {
865+ /* This is intended to match the values of the tp_subclasses dict.
866+ * It matches weakrefs to types in other places as well, which is
867+ * an unintended side effect.
868+ */
869+ return false;
870+ }
871+ return true;
872+ }
873+
861874/* Clear all weakrefs to unreachable objects, and if such a weakref has a
862875 * callback, invoke it if necessary. Note that it's possible for such
863876 * weakrefs to be outside the unreachable set -- indeed, those are precisely
@@ -895,7 +908,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
895908 op = FROM_GC (gc );
896909 next = GC_NEXT (gc );
897910
898- if (PyWeakref_Check (op )) {
911+ if (PyWeakref_Check (op ) && should_clear_weakref (( PyWeakReference * ) op ) ) {
899912 /* A weakref inside the unreachable set must be cleared. If we
900913 * allow its callback to execute inside delete_garbage(), it
901914 * could expose objects that have tp_clear already called on
@@ -925,16 +938,19 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
925938 * all the weakrefs, and move the weakrefs with callbacks
926939 * that must be called into wrcb_to_call.
927940 */
928- for (wr = * wrlist ; wr != NULL ; wr = * wrlist ) {
941+ PyWeakReference * next_wr ;
942+ for (wr = * wrlist ; wr != NULL ; wr = next_wr ) {
943+ next_wr = wr -> wr_next ;
929944 PyGC_Head * wrasgc ; /* AS_GC(wr) */
930945
931- /* _PyWeakref_ClearRef clears the weakref but leaves
932- * the callback pointer intact. Obscure: it also
933- * changes *wrlist.
934- */
935- _PyObject_ASSERT ((PyObject * )wr , wr -> wr_object == op );
936- _PyWeakref_ClearRef (wr );
937- _PyObject_ASSERT ((PyObject * )wr , wr -> wr_object == Py_None );
946+ if (should_clear_weakref (wr )) {
947+ /* _PyWeakref_ClearRef clears the weakref but leaves
948+ * the callback pointer intact. Obscure: it also
949+ * changes *wrlist.
950+ */
951+ _PyWeakref_ClearRef (wr );
952+ _PyObject_ASSERT ((PyObject * )wr , wr -> wr_object == Py_None );
953+ }
938954 if (wr -> wr_callback == NULL ) {
939955 /* no callback */
940956 continue ;
0 commit comments