From 66130290846ec9438d80d99a4d1f7754e73c3078 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:26:01 +0200 Subject: [PATCH] [3.13] gh-121860: Fix crash when materializing managed dict (GH-121866) (#121867) The object's inline values may be marked invalid if the materialized dict was already initialized and then deleted. (cherry picked from commit 162b41f57757c1df40eb377985e2e877fb0f0ea3) Co-authored-by: Sam Gross --- Lib/test/test_class.py | 18 ++++++++++++++++++ ...4-07-16-18-23-22.gh-issue-121860.-FTauD.rst | 1 + Objects/dictobject.c | 17 ++++++++++++----- 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-16-18-23-22.gh-issue-121860.-FTauD.rst 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; }