[3.14] GH-91636: Clear weakrefs created by finalizers. (GH-136401) (#141993)

GH-91636: Clear weakrefs created by finalizers. (GH-136401)

Weakrefs to unreachable garbage that are created during running of
finalizers need to be cleared.  This avoids exposing objects that
have `tp_clear` called on them to Python-level code.
(cherry picked from commit b6b99bf7f1)

Co-authored-by: Neil Schemenauer <nas-github@arctrix.com>
This commit is contained in:
Miss Islington (bot) 2025-11-29 08:34:36 +01:00 committed by GitHub
parent f7d17ac9f8
commit fed2af6f9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 54 additions and 14 deletions

View file

@ -1493,9 +1493,9 @@ move_legacy_finalizer_reachable(struct collection_state *state)
}
// Clear all weakrefs to unreachable objects. Weakrefs with callbacks are
// enqueued in `wrcb_to_call`, but not invoked yet.
// optionally enqueued in `wrcb_to_call`, but not invoked yet.
static void
clear_weakrefs(struct collection_state *state)
clear_weakrefs(struct collection_state *state, bool enqueue_callbacks)
{
PyObject *op;
WORKSTACK_FOR_EACH(&state->unreachable, op) {
@ -1527,6 +1527,10 @@ clear_weakrefs(struct collection_state *state)
_PyWeakref_ClearRef(wr);
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
if (!enqueue_callbacks) {
continue;
}
// We do not invoke callbacks for weakrefs that are themselves
// unreachable. This is partly for historical reasons: weakrefs
// predate safe object finalization, and a weakref that is itself
@ -2212,7 +2216,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
interp->gc.long_lived_total = state->long_lived_total;
// Clear weakrefs and enqueue callbacks (but do not call them).
clear_weakrefs(state);
clear_weakrefs(state, true);
_PyEval_StartTheWorld(interp);
// Deallocate any object from the refcount merge step
@ -2223,11 +2227,19 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
call_weakref_callbacks(state);
finalize_garbage(state);
// Handle any objects that may have resurrected after the finalization.
_PyEval_StopTheWorld(interp);
// Handle any objects that may have resurrected after the finalization.
err = handle_resurrected_objects(state);
// Clear free lists in all threads
_PyGC_ClearAllFreeLists(interp);
if (err == 0) {
// Clear weakrefs to objects in the unreachable set. No Python-level
// code must be allowed to access those unreachable objects. During
// delete_garbage(), finalizers outside the unreachable set might
// run and create new weakrefs. If those weakrefs were not cleared,
// they could reveal unreachable objects.
clear_weakrefs(state, false);
}
_PyEval_StartTheWorld(interp);
if (err < 0) {