mirror of
https://github.com/python/cpython.git
synced 2025-11-24 12:20:42 +00:00
[3.14] gh-140551: Fix dict crash if clear is called at lookup stage (GH-140558) (#140743)
gh-140551: Fix `dict` crash if `clear` is called at `lookup` stage (#140558) Co-authored-by: Inada Naoki <songofacandy@gmail.com>
This commit is contained in:
parent
e595d995c4
commit
5954b5ba92
3 changed files with 54 additions and 50 deletions
|
|
@ -1601,6 +1601,26 @@ class DictTest(unittest.TestCase):
|
||||||
with self.assertRaises(KeyError):
|
with self.assertRaises(KeyError):
|
||||||
d.get(key2)
|
d.get(key2)
|
||||||
|
|
||||||
|
def test_clear_at_lookup(self):
|
||||||
|
class X:
|
||||||
|
def __hash__(self):
|
||||||
|
return 1
|
||||||
|
def __eq__(self, other):
|
||||||
|
nonlocal d
|
||||||
|
d.clear()
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
for _ in range(10):
|
||||||
|
d[X()] = None
|
||||||
|
|
||||||
|
self.assertEqual(len(d), 1)
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
for _ in range(10):
|
||||||
|
d.setdefault(X(), None)
|
||||||
|
|
||||||
|
self.assertEqual(len(d), 1)
|
||||||
|
|
||||||
|
|
||||||
class CAPITest(unittest.TestCase):
|
class CAPITest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fixed crash in :class:`dict` if :meth:`dict.clear` is called at the lookup
|
||||||
|
stage. Patch by Mikhail Efimov and Inada Naoki.
|
||||||
|
|
@ -1723,6 +1723,14 @@ static inline int
|
||||||
insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
|
insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
Py_hash_t hash, PyObject *key, PyObject *value)
|
Py_hash_t hash, PyObject *key, PyObject *value)
|
||||||
{
|
{
|
||||||
|
// gh-140551: If dict was cleared in _Py_dict_lookup,
|
||||||
|
// we have to resize one more time to force general key kind.
|
||||||
|
if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
|
||||||
|
if (insertion_resize(interp, mp, 0) < 0)
|
||||||
|
return -1;
|
||||||
|
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
if (mp->ma_keys->dk_usable <= 0) {
|
if (mp->ma_keys->dk_usable <= 0) {
|
||||||
/* Need to resize. */
|
/* Need to resize. */
|
||||||
if (insertion_resize(interp, mp, 1) < 0) {
|
if (insertion_resize(interp, mp, 1) < 0) {
|
||||||
|
|
@ -1819,38 +1827,31 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
|
||||||
PyObject *key, Py_hash_t hash, PyObject *value)
|
PyObject *key, Py_hash_t hash, PyObject *value)
|
||||||
{
|
{
|
||||||
PyObject *old_value;
|
PyObject *old_value;
|
||||||
|
Py_ssize_t ix;
|
||||||
|
|
||||||
ASSERT_DICT_LOCKED(mp);
|
ASSERT_DICT_LOCKED(mp);
|
||||||
|
|
||||||
if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
|
if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) {
|
||||||
if (insertion_resize(interp, mp, 0) < 0)
|
ix = insert_split_key(mp->ma_keys, key, hash);
|
||||||
goto Fail;
|
|
||||||
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_PyDict_HasSplitTable(mp)) {
|
|
||||||
Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash);
|
|
||||||
if (ix != DKIX_EMPTY) {
|
if (ix != DKIX_EMPTY) {
|
||||||
insert_split_value(interp, mp, key, value, ix);
|
insert_split_value(interp, mp, key, value, ix);
|
||||||
Py_DECREF(key);
|
Py_DECREF(key);
|
||||||
Py_DECREF(value);
|
Py_DECREF(value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// No space in shared keys. Go to insert_combined_dict() below.
|
||||||
/* No space in shared keys. Resize and continue below. */
|
|
||||||
if (insertion_resize(interp, mp, 1) < 0) {
|
|
||||||
goto Fail;
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
||||||
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
||||||
if (ix == DKIX_ERROR)
|
if (ix == DKIX_ERROR)
|
||||||
goto Fail;
|
goto Fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (ix == DKIX_EMPTY) {
|
if (ix == DKIX_EMPTY) {
|
||||||
assert(!_PyDict_HasSplitTable(mp));
|
// insert_combined_dict() will convert from non DICT_KEYS_GENERAL table
|
||||||
/* Insert into new slot. */
|
// into DICT_KEYS_GENERAL table if key is not Unicode.
|
||||||
assert(old_value == NULL);
|
// We don't convert it before _Py_dict_lookup because non-Unicode key
|
||||||
|
// may change generic table into Unicode table.
|
||||||
if (insert_combined_dict(interp, mp, hash, key, value) < 0) {
|
if (insert_combined_dict(interp, mp, hash, key, value) < 0) {
|
||||||
goto Fail;
|
goto Fail;
|
||||||
}
|
}
|
||||||
|
|
@ -4326,6 +4327,7 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
|
||||||
PyDictObject *mp = (PyDictObject *)d;
|
PyDictObject *mp = (PyDictObject *)d;
|
||||||
PyObject *value;
|
PyObject *value;
|
||||||
Py_hash_t hash;
|
Py_hash_t hash;
|
||||||
|
Py_ssize_t ix;
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
|
|
||||||
ASSERT_DICT_LOCKED(d);
|
ASSERT_DICT_LOCKED(d);
|
||||||
|
|
@ -4361,17 +4363,8 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PyUnicode_CheckExact(key) && DK_IS_UNICODE(mp->ma_keys)) {
|
if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) {
|
||||||
if (insertion_resize(interp, mp, 0) < 0) {
|
ix = insert_split_key(mp->ma_keys, key, hash);
|
||||||
if (result) {
|
|
||||||
*result = NULL;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_PyDict_HasSplitTable(mp)) {
|
|
||||||
Py_ssize_t ix = insert_split_key(mp->ma_keys, key, hash);
|
|
||||||
if (ix != DKIX_EMPTY) {
|
if (ix != DKIX_EMPTY) {
|
||||||
PyObject *value = mp->ma_values->values[ix];
|
PyObject *value = mp->ma_values->values[ix];
|
||||||
int already_present = value != NULL;
|
int already_present = value != NULL;
|
||||||
|
|
@ -4384,27 +4377,22 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
|
||||||
}
|
}
|
||||||
return already_present;
|
return already_present;
|
||||||
}
|
}
|
||||||
|
// No space in shared keys. Go to insert_combined_dict() below.
|
||||||
/* No space in shared keys. Resize and continue below. */
|
|
||||||
if (insertion_resize(interp, mp, 1) < 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
ix = _Py_dict_lookup(mp, key, hash, &value);
|
||||||
assert(!_PyDict_HasSplitTable(mp));
|
|
||||||
|
|
||||||
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &value);
|
|
||||||
if (ix == DKIX_ERROR) {
|
if (ix == DKIX_ERROR) {
|
||||||
if (result) {
|
if (result) {
|
||||||
*result = NULL;
|
*result = NULL;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ix == DKIX_EMPTY) {
|
if (ix == DKIX_EMPTY) {
|
||||||
assert(!_PyDict_HasSplitTable(mp));
|
|
||||||
value = default_value;
|
value = default_value;
|
||||||
|
|
||||||
|
// See comment to this function in insertdict.
|
||||||
if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) {
|
if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) {
|
||||||
Py_DECREF(key);
|
Py_DECREF(key);
|
||||||
Py_DECREF(value);
|
Py_DECREF(value);
|
||||||
|
|
@ -4429,12 +4417,6 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
|
||||||
*result = incref_result ? Py_NewRef(value) : value;
|
*result = incref_result ? Py_NewRef(value) : value;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
error:
|
|
||||||
if (result) {
|
|
||||||
*result = NULL;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue