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.
This commit is contained in:
Neil Schemenauer 2025-07-08 12:19:57 -07:00 committed by GitHub
parent bc9bc078df
commit b6b99bf7f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 54 additions and 14 deletions

View file

@ -262,9 +262,11 @@ class Cyclic(tuple):
# finalizer.
def __del__(self):
# 5. Create a weakref to `func` now. If we had created
# it earlier, it would have been cleared by the
# garbage collector before calling the finalizers.
# 5. Create a weakref to `func` now. In previous
# versions of Python, this would avoid having it
# cleared by the garbage collector before calling
# the finalizers. Now, weakrefs get cleared after
# calling finalizers.
self[1].ref = weakref.ref(self[0])
# 6. Drop the global reference to `latefin`. The only
@ -293,14 +295,18 @@ def func():
# which will find `cyc` and `func` as garbage.
gc.collect()
# 9. Previously, this would crash because `func_qualname`
# had been NULL-ed out by func_clear().
# 9. Previously, this would crash because the weakref
# created in the finalizer revealed the function after
# `tp_clear` was called and `func_qualname`
# had been NULL-ed out by func_clear(). Now, we clear
# weakrefs to unreachable objects before calling `tp_clear`
# but after calling finalizers.
print(f"{func=}")
"""
# We're mostly just checking that this doesn't crash.
rc, stdout, stderr = assert_python_ok("-c", code)
self.assertEqual(rc, 0)
self.assertRegex(stdout, rb"""\A\s*func=<function at \S+>\s*\z""")
# The `func` global is None because the weakref was cleared.
self.assertRegex(stdout, rb"""\A\s*func=None""")
self.assertFalse(stderr)
@refcount_test