From 22eb7a983471f23a169a8afe736b176cee7640b9 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Mon, 9 Feb 2026 23:20:25 +0530 Subject: [PATCH] gh-142731: Fix use-after-free in _PyObject_StoreInstanceAttribute and add regression test --- Lib/test/test_descr.py | 16 ++++++++++++++++ ...026-02-09-23-18-55.gh-issue-142731.KYuPij.rst | 3 +++ Objects/dictobject.c | 10 ++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-02-09-23-18-55.gh-issue-142731.KYuPij.rst diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index d6e3719479a214..2e27963fb19470 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -5361,6 +5361,22 @@ def foo(self): with self.assertRaisesRegex(NotImplementedError, "BAR"): B().foo + def test_reentrant_hash_during_setattr_delattr(self): + class Evil(str): + def __hash__(self): + old_dict = target.__dict__ + target.__dict__ = {} + del old_dict + return super().__hash__() + class Target: + pass + target = Target() + setattr(target, Evil("marker"), 123) + try: + delattr(target, Evil("marker")) + except AttributeError: + pass + class DictProxyTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-09-23-18-55.gh-issue-142731.KYuPij.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-09-23-18-55.gh-issue-142731.KYuPij.rst new file mode 100644 index 00000000000000..dfedc86f117df6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-09-23-18-55.gh-issue-142731.KYuPij.rst @@ -0,0 +1,3 @@ +Fix a potential use-after-free crash when setting or deleting instance +attributes if a re-entrant ``__hash__`` implementation mutates the instance +``__dict__`` during attribute handling. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c1584be3f0ed4a..05021856e90be9 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -6983,7 +6983,10 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value) Py_DECREF(dict); return res; } - return store_instance_attr_dict(obj, dict, name, value); + Py_INCREF(dict); + int res = store_instance_attr_dict(obj, dict, name, value); + Py_DECREF(dict); + return res; } #ifdef Py_GIL_DISABLED @@ -7012,7 +7015,10 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value) return res; } } - return store_instance_attr_dict(obj, dict, name, value); + Py_INCREF((PyObject *)dict); + int res = store_instance_attr_dict(obj, dict, name, value); + Py_DECREF((PyObject *)dict); + return res; #else return store_instance_attr_lock_held(obj, values, name, value); #endif