Skip to content

Commit a82f77f

Browse files
committed
protect against mutation of the dict during insertion (closes python#24407)
1 parent dac3ab8 commit a82f77f

File tree

3 files changed

+36
-7
lines changed

3 files changed

+36
-7
lines changed

Lib/test/test_dict.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,21 @@ class Foo: pass
906906
f.a = 'a'
907907
self.assertEqual(f.__dict__, {1:1, 'a':'a'})
908908

909+
def test_merge_and_mutate(self):
910+
class X:
911+
def __hash__(self):
912+
return 0
913+
914+
def __eq__(self, o):
915+
other.clear()
916+
return False
917+
918+
l = [(i,0) for i in range(1, 1337)]
919+
other = dict(l)
920+
other[X()] = 0
921+
d = {X(): 0, 1: 1}
922+
self.assertRaises(RuntimeError, d.update, other)
923+
909924
from test import mapping_tests
910925

911926
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ What's New in Python 3.3.7?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #24407: Fix crash when dict is mutated while being updated.
14+
1315
- Issue #24096: Make warnings.warn_explicit more robust against mutation of the
1416
warnings.filters list.
1517

Objects/dictobject.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,20 +1941,32 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
19411941
if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0)
19421942
return -1;
19431943
for (i = 0, n = DK_SIZE(other->ma_keys); i < n; i++) {
1944-
PyObject *value;
1944+
PyObject *key, *value;
1945+
Py_hash_t hash;
19451946
entry = &other->ma_keys->dk_entries[i];
1947+
key = entry->me_key;
1948+
hash = entry->me_hash;
19461949
if (other->ma_values)
19471950
value = other->ma_values[i];
19481951
else
19491952
value = entry->me_value;
19501953

1951-
if (value != NULL &&
1952-
(override ||
1953-
PyDict_GetItem(a, entry->me_key) == NULL)) {
1954-
if (insertdict(mp, entry->me_key,
1955-
entry->me_hash,
1956-
value) != 0)
1954+
if (value != NULL) {
1955+
int err = 0;
1956+
Py_INCREF(key);
1957+
Py_INCREF(value);
1958+
if (override || PyDict_GetItem(a, key) == NULL)
1959+
err = insertdict(mp, key, hash, value);
1960+
Py_DECREF(value);
1961+
Py_DECREF(key);
1962+
if (err != 0)
1963+
return -1;
1964+
1965+
if (n != DK_SIZE(other->ma_keys)) {
1966+
PyErr_SetString(PyExc_RuntimeError,
1967+
"dict mutated during update");
19571968
return -1;
1969+
}
19581970
}
19591971
}
19601972
}

0 commit comments

Comments
 (0)