[Python-checkins] cpython (3.3): protect against mutation of the dict during insertion (closes #24407)

benjamin.peterson python-checkins at python.org
Sun Jul 5 05:54:13 CEST 2015


https://hg.python.org/cpython/rev/37fed8b02f00
changeset:   96811:37fed8b02f00
branch:      3.3
parent:      96688:8daf7b74462a
user:        Benjamin Peterson <benjamin at python.org>
date:        Sat Jul 04 19:55:16 2015 -0500
summary:
  protect against mutation of the dict during insertion (closes #24407)

files:
  Lib/test/test_dict.py |  15 +++++++++++++++
  Misc/NEWS             |   2 ++
  Objects/dictobject.c  |  26 +++++++++++++++++++-------
  3 files changed, 36 insertions(+), 7 deletions(-)


diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -906,6 +906,21 @@
         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):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@
 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.
 
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -1941,20 +1941,32 @@
             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;
+                }
             }
         }
     }

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list