]> granicus.if.org Git - python/commitdiff
protect against mutation of the dict during insertion (closes #24407)
authorBenjamin Peterson <benjamin@python.org>
Sun, 5 Jul 2015 00:55:16 +0000 (19:55 -0500)
committerBenjamin Peterson <benjamin@python.org>
Sun, 5 Jul 2015 00:55:16 +0000 (19:55 -0500)
Lib/test/test_dict.py
Misc/NEWS
Objects/dictobject.c

index a38895945ed5be8499e028a2a0827f90dffbf0ca..bd3040a7f6a33b01dd614e300ec6702ed51a8800 100644 (file)
@@ -906,6 +906,21 @@ class DictTest(unittest.TestCase):
         f.a = 'a'
         self.assertEqual(f.__dict__, {1:1, 'a':'a'})
 
+    def test_merge_and_mutate(self):
+        class X:
+            def __hash__(self):
+                return 0
+
+            def __eq__(self, o):
+                other.clear()
+                return False
+
+        l = [(i,0) for i in range(1, 1337)]
+        other = dict(l)
+        other[X()] = 0
+        d = {X(): 0, 1: 1}
+        self.assertRaises(RuntimeError, d.update, other)
+
 from test import mapping_tests
 
 class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
index 43a9f377f8245c2c53e2850be9df3dea12c58406..c5d7e65bcb130ad87854cb64610df056dff406f9 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@ What's New in Python 3.3.7?
 Core and Builtins
 -----------------
 
+- Issue #24407: Fix crash when dict is mutated while being updated.
+
 - Issue #24096: Make warnings.warn_explicit more robust against mutation of the
   warnings.filters list.
 
index 7aa5ea83d38dd0c4e546ae28143ae48d546409df..953484c1258d9093d27ee83fec7a8617903e8e5e 100644 (file)
@@ -1941,20 +1941,32 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
             if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0)
                return -1;
         for (i = 0, n = DK_SIZE(other->ma_keys); i < n; i++) {
-            PyObject *value;
+            PyObject *key, *value;
+            Py_hash_t hash;
             entry = &other->ma_keys->dk_entries[i];
+            key = entry->me_key;
+            hash = entry->me_hash;
             if (other->ma_values)
                 value = other->ma_values[i];
             else
                 value = entry->me_value;
 
-            if (value != NULL &&
-                (override ||
-                 PyDict_GetItem(a, entry->me_key) == NULL)) {
-                if (insertdict(mp, entry->me_key,
-                               entry->me_hash,
-                               value) != 0)
+            if (value != NULL) {
+                int err = 0;
+                Py_INCREF(key);
+                Py_INCREF(value);
+                if (override || PyDict_GetItem(a, key) == NULL)
+                    err = insertdict(mp, key, hash, value);
+                Py_DECREF(value);
+                Py_DECREF(key);
+                if (err != 0)
+                    return -1;
+
+                if (n != DK_SIZE(other->ma_keys)) {
+                    PyErr_SetString(PyExc_RuntimeError,
+                                    "dict mutated during update");
                     return -1;
+                }
             }
         }
     }