mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
gh-97591: In Exception.__setstate__()
acquire strong references before calling tp_hash
slot (#97700)
This commit is contained in:
parent
8baef8ae36
commit
d639438609
3 changed files with 34 additions and 1 deletions
|
@ -114,6 +114,31 @@ class ExceptionClassTests(unittest.TestCase):
|
||||||
[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.
|
|
@ -167,10 +167,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…
Add table
Add a link
Reference in a new issue