]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-154709: Fix out-of-bounds access in dict reverse iterator (GH-154721)
authorBhuvansh <bhuvanshkataria@gmail.com>
Wed, 29 Jul 2026 17:26:45 +0000 (22:56 +0530)
committerGitHub <noreply@github.com>
Wed, 29 Jul 2026 17:26:45 +0000 (20:26 +0300)
Lib/test/test_dict.py
Misc/NEWS.d/next/Core_and_Builtins/2026-07-26-09-07-41.gh-issue-154709.M2uZ76.rst [new file with mode: 0644]
Objects/dictobject.c

index dc31d403b837adb3c51910fdc3e8935a5e79b245..1e665c86303078c6bc6f0e57a9b5b0bd426e702d 100644 (file)
@@ -1403,6 +1403,27 @@ class DictTest(unittest.TestCase):
         self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
         self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])
 
+    def test_reversed_dict_after_clear_and_restore(self):
+        d = {}
+        for i in range(1000):
+            d[f"k{i}"] = i
+
+        for i in range(1, 1000):
+            del d[f"k{i}"]
+
+        iterators = (
+            reversed(d),
+            reversed(d.keys()),
+            reversed(d.values()),
+            reversed(d.items()),
+        )
+
+        d.clear()
+        d["k0"] = 0
+
+        for it in iterators:
+            self.assertEqual(list(it), [])
+
     def test_dict_copy_order(self):
         # bpo-34320
         od = collections.OrderedDict([('a', 1), ('b', 2)])
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-26-09-07-41.gh-issue-154709.M2uZ76.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-26-09-07-41.gh-issue-154709.M2uZ76.rst
new file mode 100644 (file)
index 0000000..6eb1ce6
--- /dev/null
@@ -0,0 +1,2 @@
+Fix an out-of-bounds access in reverse dictionary iterators when the
+underlying dictionary is cleared and modified after the iterator is created.
index c650aa456d2cc9d40d273becdd0233ca0e74b71c..74b6d5d779a064c27fd61c1a9897384d23d7372c 100644 (file)
@@ -6276,9 +6276,12 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self)
         int index = get_index_from_order(d, i);
         key = LOAD_SHARED_KEY(DK_UNICODE_ENTRIES(k)[index].me_key);
         value = d->ma_values->values[index];
-        assert (value != NULL);
+        assert(value != NULL);
     }
     else {
+        if (i >= k->dk_nentries) {
+            goto fail;
+        }
         if (DK_IS_UNICODE(k)) {
             PyDictUnicodeEntry *entry_ptr = &DK_UNICODE_ENTRIES(k)[i];
             while (entry_ptr->me_value == NULL) {