Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Lib/test/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,5 +1040,35 @@ def __init__(self):
self.assertFalse(out, msg=out.decode('utf-8'))
self.assertFalse(err, msg=err.decode('utf-8'))

@support.nomemtest
def test_clear_managed_dict_no_memory_keeps_exception(self):
# gh-152083: an exception may already be set when the managed dict is
# cleared under low memory. PyErr_FormatUnraisable() must not clear it.
code = """if 1:
import _testcapi

class A:
def __init__(self):
self.a = 1
self.b = 2

def f():
a = A()
a.__dict__
return [None] * 1000

for start in range(120):
_testcapi.set_nomemory(start)
try:
f()
except BaseException:
pass
_testcapi.remove_mem_hooks()
"""
rc, out, err = script_helper.assert_python_ok("-c", code)
self.assertEqual(rc, 0)
self.assertFalse(out, msg=out.decode('utf-8'))
self.assertFalse(err, msg=err.decode('utf-8'))

if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash when an object's managed dictionary is cleared under low memory
while an exception is set.
4 changes: 4 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -7944,6 +7944,9 @@ PyObject_ClearManagedDict(PyObject *obj)
// values. We need to materialize the keys. Nothing can modify
// this object, but we need to lock the dictionary.
int err;
// gh-152083: an exception may already be set, so keep it.
// The OOM path below reports via PyErr_FormatUnraisable() which clears it.
PyObject *exc = PyErr_GetRaisedException();
Py_BEGIN_CRITICAL_SECTION(dict);
err = detach_dict_from_object(dict, obj);
Py_END_CRITICAL_SECTION();
Expand All @@ -7963,6 +7966,7 @@ PyObject_ClearManagedDict(PyObject *obj)
clear_inline_values(_PyObject_InlineValues(obj));
Py_END_CRITICAL_SECTION();
}
PyErr_SetRaisedException(exc);
}
}
Py_CLEAR(_PyObject_ManagedDictPointer(obj)->dict);
Expand Down
Loading