Skip to content

Commit 0e21cc6

Browse files
authored
GH-124547: Clear instance dictionary if memory error occurs during object dealloc (GH-124627)
1 parent 2357d5b commit 0e21cc6

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

Lib/test/test_class.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"Test the functionality of Python classes implementing operators."
22

33
import unittest
4+
import test.support
45

56
testmeths = [
67

@@ -932,6 +933,20 @@ class C:
932933
C.a = X()
933934
C.a = X()
934935

936+
def test_detach_materialized_dict_no_memory(self):
937+
import _testcapi
938+
class A:
939+
def __init__(self):
940+
self.a = 1
941+
self.b = 2
942+
a = A()
943+
d = a.__dict__
944+
with test.support.catch_unraisable_exception() as ex:
945+
_testcapi.set_nomemory(0, 1)
946+
del a
947+
self.assertEqual(ex.unraisable.exc_type, MemoryError)
948+
with self.assertRaises(KeyError):
949+
d["a"]
935950

936951
if __name__ == '__main__':
937952
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When deallocating an object with inline values whose ``__dict__`` is still
2+
live: if memory allocation for the inline values fails, clear the
3+
dictionary. Prevents an interpreter crash.

Objects/dictobject.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3930,13 +3930,13 @@ dict_copy_impl(PyDictObject *self)
39303930
}
39313931

39323932
/* Copies the values, but does not change the reference
3933-
* counts of the objects in the array. */
3933+
* counts of the objects in the array.
3934+
* Return NULL, but does *not* set an exception on failure */
39343935
static PyDictValues *
39353936
copy_values(PyDictValues *values)
39363937
{
39373938
PyDictValues *newvalues = new_values(values->capacity);
39383939
if (newvalues == NULL) {
3939-
PyErr_NoMemory();
39403940
return NULL;
39413941
}
39423942
newvalues->size = values->size;
@@ -7216,6 +7216,13 @@ _PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
72167216
PyDictValues *values = copy_values(mp->ma_values);
72177217

72187218
if (values == NULL) {
7219+
/* Out of memory. Clear the dict */
7220+
PyInterpreterState *interp = _PyInterpreterState_GET();
7221+
PyDictKeysObject *oldkeys = mp->ma_keys;
7222+
set_keys(mp, Py_EMPTY_KEYS);
7223+
dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
7224+
STORE_USED(mp, 0);
7225+
PyErr_NoMemory();
72197226
return -1;
72207227
}
72217228
mp->ma_values = values;

0 commit comments

Comments
 (0)