mirror of https://github.com/python/cpython.git
gh-97591: In `Exception.__setstate__()` acquire strong references before calling `tp_hash` slot (GH-97700)
(cherry picked from commit d639438609
)
Co-authored-by: Ofey Chan <ofey206@gmail.com>
This commit is contained in:
parent
a4fbb94965
commit
c6fcbb4928
|
@ -114,6 +114,31 @@ def test_interface_no_arg(self):
|
||||||
[repr(exc), exc.__class__.__name__ + '()'])
|
[repr(exc), exc.__class__.__name__ + '()'])
|
||||||
self.interface_test_driver(results)
|
self.interface_test_driver(results)
|
||||||
|
|
||||||
|
def test_setstate_refcount_no_crash(self):
|
||||||
|
# gh-97591: Acquire strong reference before calling tp_hash slot
|
||||||
|
# in PyObject_SetAttr.
|
||||||
|
import gc
|
||||||
|
d = {}
|
||||||
|
class HashThisKeyWillClearTheDict(str):
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
d.clear()
|
||||||
|
return super().__hash__()
|
||||||
|
class Value(str):
|
||||||
|
pass
|
||||||
|
exc = Exception()
|
||||||
|
|
||||||
|
d[HashThisKeyWillClearTheDict()] = Value() # refcount of Value() is 1 now
|
||||||
|
|
||||||
|
# Exception.__setstate__ should aquire a strong reference of key and
|
||||||
|
# value in the dict. Otherwise, Value()'s refcount would go below
|
||||||
|
# zero in the tp_hash call in PyObject_SetAttr(), and it would cause
|
||||||
|
# crash in GC.
|
||||||
|
exc.__setstate__(d) # __hash__() is called again here, clearing the dict.
|
||||||
|
|
||||||
|
# This GC would crash if the refcount of Value() goes below zero.
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
class UsageTests(unittest.TestCase):
|
class UsageTests(unittest.TestCase):
|
||||||
|
|
||||||
"""Test usage of exceptions"""
|
"""Test usage of exceptions"""
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fixed a missing incref/decref pair in `Exception.__setstate__()`.
|
||||||
|
Patch by Ofey Chan.
|
|
@ -156,10 +156,16 @@ BaseException_setstate(PyObject *self, PyObject *state)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
while (PyDict_Next(state, &i, &d_key, &d_value)) {
|
while (PyDict_Next(state, &i, &d_key, &d_value)) {
|
||||||
if (PyObject_SetAttr(self, d_key, d_value) < 0)
|
Py_INCREF(d_key);
|
||||||
|
Py_INCREF(d_value);
|
||||||
|
int res = PyObject_SetAttr(self, d_key, d_value);
|
||||||
|
Py_DECREF(d_value);
|
||||||
|
Py_DECREF(d_key);
|
||||||
|
if (res < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue