From b8311cf5e5d72f8a8aa688b7da1760d6a74a4d72 Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Tue, 2 Apr 2019 11:30:10 +0200 Subject: [PATCH] bpo-36473: add maximum iteration check for dict .values() and .items() (GH-12619) --- Lib/test/test_dict.py | 20 ++++++++++++++++++- .../2019-03-27-23-53-00.bpo-36452.xhK2lT.rst | 6 +++++- Objects/dictobject.c | 12 +++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index eecdc8beec6..13be857f7ab 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -477,7 +477,25 @@ class DictTest(unittest.TestCase): with self.assertRaises(RuntimeError): for i in d: del d[0] - d[1] = 1 + d[0] = 0 + + def test_mutating_iteration_delete_over_values(self): + # change dict content during iteration + d = {} + d[0] = 0 + with self.assertRaises(RuntimeError): + for i in d.values(): + del d[0] + d[0] = 0 + + def test_mutating_iteration_delete_over_items(self): + # change dict content during iteration + d = {} + d[0] = 0 + with self.assertRaises(RuntimeError): + for i in d.items(): + del d[0] + d[0] = 0 def test_mutating_lookup(self): # changing dict during a lookup (issue #14417) diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst b/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst index 37c0c503ede..26d85682f88 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst @@ -1 +1,5 @@ -Changing `dict` keys during iteration will now be detected in certain corner cases where the number of keys isn't changed (but they keys themselves are), and a `RuntimeError` will be raised. \ No newline at end of file +Changing ``dict`` keys during iteration of the dict itself, ``keys()``, +``values()``, or ``items()`` will now be detected in certain corner cases where +keys are deleted/added so that the number of keys isn't changed. +A `RuntimeError` will be raised after ``len(dict)`` iterations. +Contributed by Thomas Perl. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 7ea979cd176..bba27dd321a 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3630,6 +3630,12 @@ dictiter_iternextvalue(dictiterobject *di) goto fail; value = entry_ptr->me_value; } + // We found an element, but did not expect it + if (di->len == 0) { + PyErr_SetString(PyExc_RuntimeError, + "dictionary keys changed during iteration"); + goto fail; + } di->di_pos = i+1; di->len--; Py_INCREF(value); @@ -3713,6 +3719,12 @@ dictiter_iternextitem(dictiterobject *di) key = entry_ptr->me_key; value = entry_ptr->me_value; } + // We found an element, but did not expect it + if (di->len == 0) { + PyErr_SetString(PyExc_RuntimeError, + "dictionary keys changed during iteration"); + goto fail; + } di->di_pos = i+1; di->len--; Py_INCREF(key);