From: mpage Date: Mon, 29 Apr 2024 16:56:51 +0000 (-0700) Subject: gh-118331: Don't raise an error if tuple allocation fails when clearing weakrefs... X-Git-Tag: v3.13.0b1~226 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=43fa76638fc75958b592096b6830c15f0afa1a73;p=thirdparty%2FPython%2Fcpython.git gh-118331: Don't raise an error if tuple allocation fails when clearing weakrefs (#118338) 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()`, which requires that the active exception does not change. Additionally, make sure we clear the weakrefs even when tuple allocation fails. --- diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 499ba77fd195..a2f5b9b2902d 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -10,6 +10,7 @@ import copy import threading import time import random +import textwrap from test import support from test.support import script_helper, ALWAYS_EQ @@ -1009,6 +1010,31 @@ class ReferencesTestCase(TestBase): 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 TestObj: + pass + + def callback(obj): + pass + + obj = TestObj() + # 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 + """).strip() + res, _ = script_helper.run_python_until_end("-c", code) + stderr = res.err.decode("ascii", "backslashreplace") + self.assertNotRegex(stderr, "_Py_Dealloc: Deallocator of type 'TestObj'") + class SubclassableWeakrefTestCase(TestBase): diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c index 206107e8505d..93c5fe3aacec 100644 --- a/Objects/weakrefobject.c +++ b/Objects/weakrefobject.c @@ -1016,7 +1016,9 @@ PyObject_ClearWeakRefs(PyObject *object) PyObject *exc = PyErr_GetRaisedException(); PyObject *tuple = PyTuple_New(num_weakrefs * 2); if (tuple == NULL) { - _PyErr_ChainExceptions1(exc); + _PyWeakref_ClearWeakRefsExceptCallbacks(object); + PyErr_WriteUnraisable(NULL); + PyErr_SetRaisedException(exc); return; }