From 5cd56b249f239deab8d6d566fe4a62e74da3d60a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 11 May 2025 23:10:04 +0200 Subject: [PATCH] [3.13] gh-133441: Fix STORE_ATTR_WITH_HINT bytecode (#133446) Deoptimize if the dict is a dict subclass. Co-authored-by: Peter Bierma --- Lib/test/test_opcache.py | 18 ++++++++++++++++++ ...5-05-05-17-02-08.gh-issue-133441.EpjHD4.rst | 2 ++ Python/bytecodes.c | 3 ++- Python/generated_cases.c.h | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index c4fcc1993ca..aeb4de6caa5 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1155,6 +1155,24 @@ class TestInstanceDict(unittest.TestCase): {'a':1, 'b':2} ) + def test_store_attr_with_hint(self): + # gh-133441: Regression test for STORE_ATTR_WITH_HINT bytecode + class Node: + def __init__(self): + self.parents = {} + + def __setstate__(self, data_dict): + self.__dict__ = data_dict + self.parents = {} + + class Dict(dict): + pass + + obj = Node() + obj.__setstate__({'parents': {}}) + obj.__setstate__({'parents': {}}) + obj.__setstate__(Dict({'parents': {}})) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst new file mode 100644 index 00000000000..4e893045b1d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-05-17-02-08.gh-issue-133441.EpjHD4.rst @@ -0,0 +1,2 @@ +Fix crash upon setting an attribute with a :class:`dict` subclass. +Patch by Victor Stinner. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6d5a42943b0..1da434bbbc8 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2149,7 +2149,8 @@ dummy_func( assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL); - assert(PyDict_CheckExact((PyObject *)dict)); + DEOPT_IF(!PyDict_CheckExact((PyObject *)dict)); + PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries); PyObject *old_value; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 9665774cf9c..be00bf6eb6a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5591,7 +5591,7 @@ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = _PyObject_GetManagedDict(owner); DEOPT_IF(dict == NULL, STORE_ATTR); - assert(PyDict_CheckExact((PyObject *)dict)); + DEOPT_IF(!PyDict_CheckExact((PyObject *)dict), STORE_ATTR); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); PyObject *old_value;