diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 655d53b8d5b..d1f828b1ed8 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -882,6 +882,24 @@ class TestInlineValues(unittest.TestCase): f.a = 3 self.assertEqual(f.a, 3) + def test_rematerialize_object_dict(self): + # gh-121860: rematerializing an object's managed dictionary after it + # had been deleted caused a crash. + class Foo: pass + f = Foo() + f.__dict__["attr"] = 1 + del f.__dict__ + + # Using a str subclass is a way to trigger the re-materialization + class StrSubclass(str): pass + self.assertFalse(hasattr(f, StrSubclass("attr"))) + + # Changing the __class__ also triggers the re-materialization + class Bar: pass + f.__class__ = Bar + self.assertIsInstance(f, Bar) + self.assertEqual(f.__dict__, {}) + def test_store_attr_type_cache(self): """Verifies that the type cache doesn't provide a value which is inconsistent from the dict.""" diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-16-18-23-22.gh-issue-121860.-FTauD.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-16-18-23-22.gh-issue-121860.-FTauD.rst new file mode 100644 index 00000000000..a03ee83d6f8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-16-18-23-22.gh-issue-121860.-FTauD.rst @@ -0,0 +1 @@ +Fix crash when rematerializing a managed dictionary after it was deleted. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 48aed1e4da8..6b26baaf3b5 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -6683,13 +6683,20 @@ _PyObject_MaterializeManagedDict_LockHeld(PyObject *obj) { ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj); - PyDictValues *values = _PyObject_InlineValues(obj); - PyInterpreterState *interp = _PyInterpreterState_GET(); - PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); OBJECT_STAT_INC(dict_materialized_on_request); - PyDictObject *dict = make_dict_from_instance_attributes(interp, keys, values); + + PyDictValues *values = _PyObject_InlineValues(obj); + PyDictObject *dict; + if (values->valid) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj)); + dict = make_dict_from_instance_attributes(interp, keys, values); + } + else { + dict = (PyDictObject *)PyDict_New(); + } FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict, - (PyDictObject *)dict); + dict); return dict; }