Skip to content

Commit

Permalink
Don't raise an error if tuple allocation fails when clearing weakrefs
Browse files Browse the repository at this point in the history
It's not safe to raise an exception in `PyObject_ClearWeakRefs()` if one
is not already set, since it may be called by `_Py_Dealloc()` and hit
https://github.com/python/cpython/blob/5a90de0d4cbc151a6deea36a27eb81b192410e56/Objects/object.c#L2843-L2860.

Additionally, make sure we clear the weakrefs even when tuple allocation
fails.

This bug predates pythongh-111926. If it's getting tickled now, I suspect it's
because we always allocate a tuple when clearing weakrefs that have callbacks.
  • Loading branch information
mpage committed Apr 26, 2024
1 parent 194fd17 commit 6f141bd
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 1 deletion.
28 changes: 28 additions & 0 deletions Lib/test/test_weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import threading
import time
import random
import textwrap

from test import support
from test.support import script_helper, ALWAYS_EQ
Expand Down Expand Up @@ -1009,6 +1010,33 @@ def __del__(self): pass
del x
support.gc_collect()

@support.cpython_only
def test_no_memory_when_clearing(self):
# gh-118331: Make sure we do not raise an exception from the destructor
# when clearing weakrefs if allocating the intermediate tuple fails.
code = textwrap.dedent("""
import _testcapi
import weakref
class Obj:
pass
def callback(obj):
pass
obj = Obj()
# The choice of 50 is arbitrary, but must be large enough to ensure
# the allocation won't be serviced by the free list.
wrs = [weakref.ref(obj, callback) for _ in range(50)]
_testcapi.set_nomemory(0)
del obj
print("hi matt")
""").strip()
res = script_helper.assert_python_failure("-c", code)
stderr = res.err.decode("ascii", "backslashreplace")
self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type")


class SubclassableWeakrefTestCase(TestBase):

Expand Down
8 changes: 7 additions & 1 deletion Objects/weakrefobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,13 @@ PyObject_ClearWeakRefs(PyObject *object)
PyObject *exc = PyErr_GetRaisedException();
PyObject *tuple = PyTuple_New(num_weakrefs * 2);
if (tuple == NULL) {
_PyErr_ChainExceptions1(exc);
_PyWeakref_ClearWeakRefsExceptCallbacks(object);
if (exc == NULL) {
PyErr_WriteUnraisable(object);
}
else {
_PyErr_ChainExceptions1(exc);
}
return;
}

Expand Down

0 comments on commit 6f141bd

Please sign in to comment.