]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-124547: Clear instance dictionary if memory error occurs during object dealloc...
authorMark Shannon <mark@hotpy.org>
Fri, 27 Sep 2024 21:51:01 +0000 (14:51 -0700)
committerGitHub <noreply@github.com>
Fri, 27 Sep 2024 21:51:01 +0000 (14:51 -0700)
Lib/test/test_class.py
Misc/NEWS.d/next/Core_and_Builtins/2024-09-26-12-19-13.gh-issue-124547.P_SHfU.rst [new file with mode: 0644]
Objects/dictobject.c

index 902f788edc22f036066ad858e78f0216fd45b2e2..d2b6a23cc3c10dc7e470aee33fe46f745b135be3 100644 (file)
@@ -1,6 +1,7 @@
 "Test the functionality of Python classes implementing operators."
 
 import unittest
+import test.support
 
 testmeths = [
 
@@ -932,6 +933,20 @@ class TestInlineValues(unittest.TestCase):
         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/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 (file)
index 0000000..1005c65
--- /dev/null
@@ -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.
index f38ab1b2865e995da2ec0e0f977e0535b8958b57..e50cf8bf1787f9190c4696da0c6cfbecf411ce27 100644 (file)
@@ -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;