From b4d7e60f060305425e49f7833ff56d7b84ca589b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 26 Sep 2024 12:12:11 -0700 Subject: [PATCH 1/2] Clear instance dictionary if memory error occurs when copying values into the dict when deallocating the object --- Lib/test/test_class.py | 15 +++++++++++++++ Objects/dictobject.c | 11 +++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 902f788edc22f0..d2b6a23cc3c10d 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -1,6 +1,7 @@ "Test the functionality of Python classes implementing operators." import unittest +import test.support testmeths = [ @@ -932,6 +933,20 @@ class C: C.a = X() C.a = X() + def test_detach_materialized_dict_no_memory(self): + import _testcapi + class A: + def __init__(self): + self.a = 1 + self.b = 2 + a = A() + d = a.__dict__ + with test.support.catch_unraisable_exception() as ex: + _testcapi.set_nomemory(0, 1) + del a + self.assertEqual(ex.unraisable.exc_type, MemoryError) + with self.assertRaises(KeyError): + d["a"] if __name__ == '__main__': unittest.main() diff --git a/Objects/dictobject.c b/Objects/dictobject.c index f38ab1b2865e99..e50cf8bf1787f9 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3930,13 +3930,13 @@ dict_copy_impl(PyDictObject *self) } /* Copies the values, but does not change the reference - * counts of the objects in the array. */ + * counts of the objects in the array. + * Return NULL, but does *not* set an exception on failure */ static PyDictValues * copy_values(PyDictValues *values) { PyDictValues *newvalues = new_values(values->capacity); if (newvalues == NULL) { - PyErr_NoMemory(); return NULL; } newvalues->size = values->size; @@ -7216,6 +7216,13 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj) PyDictValues *values = copy_values(mp->ma_values); if (values == NULL) { + /* Out of memory. Clear the dict */ + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyDictKeysObject *oldkeys = mp->ma_keys; + set_keys(mp, Py_EMPTY_KEYS); + dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp)); + STORE_USED(mp, 0); + PyErr_NoMemory(); return -1; } mp->ma_values = values; From 83698a5d35559a36a03e819885d3a866e58a5c7d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 26 Sep 2024 12:19:28 -0700 Subject: [PATCH 2/2] Add news --- .../2024-09-26-12-19-13.gh-issue-124547.P_SHfU.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-09-26-12-19-13.gh-issue-124547.P_SHfU.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-26-12-19-13.gh-issue-124547.P_SHfU.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-26-12-19-13.gh-issue-124547.P_SHfU.rst new file mode 100644 index 00000000000000..1005c651849f45 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-26-12-19-13.gh-issue-124547.P_SHfU.rst @@ -0,0 +1,3 @@ +When deallocating an object with inline values whose ``__dict__`` is still +live: if memory allocation for the inline values fails, clear the +dictionary. Prevents an interpreter crash.