There was an error while loading. Please reload this page.
Exception.__setstate__()
tp_hash
1 parent 8baef8a commit d639438Copy full SHA for d639438
3 files changed
Lib/test/test_baseexception.py
@@ -114,6 +114,31 @@ def test_interface_no_arg(self):
114
[repr(exc), exc.__class__.__name__ + '()'])
115
self.interface_test_driver(results)
116
117
+ def test_setstate_refcount_no_crash(self):
118
+ # gh-97591: Acquire strong reference before calling tp_hash slot
119
+ # in PyObject_SetAttr.
120
+ import gc
121
+ d = {}
122
+ class HashThisKeyWillClearTheDict(str):
123
+ def __hash__(self) -> int:
124
+ d.clear()
125
+ return super().__hash__()
126
+ class Value(str):
127
+ pass
128
+ exc = Exception()
129
+
130
+ d[HashThisKeyWillClearTheDict()] = Value() # refcount of Value() is 1 now
131
132
+ # Exception.__setstate__ should aquire a strong reference of key and
133
+ # value in the dict. Otherwise, Value()'s refcount would go below
134
+ # zero in the tp_hash call in PyObject_SetAttr(), and it would cause
135
+ # crash in GC.
136
+ exc.__setstate__(d) # __hash__() is called again here, clearing the dict.
137
138
+ # This GC would crash if the refcount of Value() goes below zero.
139
+ gc.collect()
140
141
142
class UsageTests(unittest.TestCase):
143
144
"""Test usage of exceptions"""
Misc/NEWS.d/next/Core and Builtins/2022-10-01-08-55-09.gh-issue-97591.pw6kkH.rst
@@ -0,0 +1,2 @@
1
+Fixed a missing incref/decref pair in `Exception.__setstate__()`.
2
+Patch by Ofey Chan.
Objects/exceptions.c
@@ -167,8 +167,14 @@ BaseException_setstate(PyObject *self, PyObject *state)
167
return NULL;
168
}
169
while (PyDict_Next(state, &i, &d_key, &d_value)) {
170
- if (PyObject_SetAttr(self, d_key, d_value) < 0)
+ Py_INCREF(d_key);
171
+ Py_INCREF(d_value);
172
+ int res = PyObject_SetAttr(self, d_key, d_value);
173
+ Py_DECREF(d_value);
174
+ Py_DECREF(d_key);
175
+ if (res < 0) {
176
177
+ }
178
179
180
Py_RETURN_NONE;
0 commit comments