Skip to content

Commit 22eb7a9

Browse files
gh-142731: Fix use-after-free in _PyObject_StoreInstanceAttribute and add regression test
1 parent dd2da42 commit 22eb7a9

3 files changed

Lines changed: 27 additions & 2 deletions

File tree

Lib/test/test_descr.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5361,6 +5361,22 @@ def foo(self):
53615361
with self.assertRaisesRegex(NotImplementedError, "BAR"):
53625362
B().foo
53635363

5364+
def test_reentrant_hash_during_setattr_delattr(self):
5365+
class Evil(str):
5366+
def __hash__(self):
5367+
old_dict = target.__dict__
5368+
target.__dict__ = {}
5369+
del old_dict
5370+
return super().__hash__()
5371+
class Target:
5372+
pass
5373+
target = Target()
5374+
setattr(target, Evil("marker"), 123)
5375+
try:
5376+
delattr(target, Evil("marker"))
5377+
except AttributeError:
5378+
pass
5379+
53645380

53655381
class DictProxyTests(unittest.TestCase):
53665382
def setUp(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a potential use-after-free crash when setting or deleting instance
2+
attributes if a re-entrant ``__hash__`` implementation mutates the instance
3+
``__dict__`` during attribute handling.

Objects/dictobject.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6983,7 +6983,10 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value)
69836983
Py_DECREF(dict);
69846984
return res;
69856985
}
6986-
return store_instance_attr_dict(obj, dict, name, value);
6986+
Py_INCREF(dict);
6987+
int res = store_instance_attr_dict(obj, dict, name, value);
6988+
Py_DECREF(dict);
6989+
return res;
69876990
}
69886991

69896992
#ifdef Py_GIL_DISABLED
@@ -7012,7 +7015,10 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value)
70127015
return res;
70137016
}
70147017
}
7015-
return store_instance_attr_dict(obj, dict, name, value);
7018+
Py_INCREF((PyObject *)dict);
7019+
int res = store_instance_attr_dict(obj, dict, name, value);
7020+
Py_DECREF((PyObject *)dict);
7021+
return res;
70167022
#else
70177023
return store_instance_attr_lock_held(obj, values, name, value);
70187024
#endif

0 commit comments

Comments
 (0)