Skip to content

Commit

Permalink
[3.13] pythonGH-124547: Clear instance dictionary if memory error occ…
Browse files Browse the repository at this point in the history
…urs during object dealloc (pythonGH-124627) (python#124714)

pythonGH-124547: Clear instance dictionary if memory error occurs during object dealloc (pythonGH-124627)
(cherry picked from commit 0e21cc6)

Co-authored-by: Mark Shannon <[email protected]>
  • Loading branch information
miss-islington and markshannon authored Sep 27, 2024
1 parent 0a125d9 commit 80de976
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 2 deletions.
15 changes: 15 additions & 0 deletions Lib/test/test_class.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"Test the functionality of Python classes implementing operators."

import unittest
import test.support

testmeths = [

Expand Down Expand Up @@ -919,6 +920,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()
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 9 additions & 2 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3918,13 +3918,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;
Expand Down Expand Up @@ -7189,6 +7189,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;
Expand Down

0 comments on commit 80de976

Please sign in to comment.