summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2015-06-04 06:09:56 (GMT)
committerEric Snow <ericsnowcurrently@gmail.com>2015-06-04 06:09:56 (GMT)
commit4fabf02633f7f537a8318a7541eec02cb3338a0d (patch)
treea81cc569e4f838d4f55a6c0bfac2abcfa44861b6
parentb6c6a4dc04beba45bc9053abfcc56980c5e31e3d (diff)
downloadcpython-4fabf02633f7f537a8318a7541eec02cb3338a0d.zip
cpython-4fabf02633f7f537a8318a7541eec02cb3338a0d.tar.gz
cpython-4fabf02633f7f537a8318a7541eec02cb3338a0d.tar.bz2
Issue #24369: Defend against key-changes during iteration.
-rw-r--r--Lib/test/test_collections.py17
-rw-r--r--Misc/NEWS2
-rw-r--r--Objects/odictobject.c32
3 files changed, 38 insertions, 13 deletions
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index eaceb97..ab2b733 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -2047,6 +2047,23 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
del od[colliding]
self.assertEqual(list(od.items()), [(key, ...), ('after', ...)])
+ def test_key_change_during_iteration(self):
+ OrderedDict = self.module.OrderedDict
+
+ od = OrderedDict.fromkeys('abcde')
+ self.assertEqual(list(od), list('abcde'))
+ with self.assertRaises(RuntimeError):
+ for i, k in enumerate(od):
+ od.move_to_end(k)
+ self.assertLess(i, 5)
+ with self.assertRaises(RuntimeError):
+ for k in od:
+ od['f'] = None
+ with self.assertRaises(RuntimeError):
+ for k in od:
+ del od['c']
+ self.assertEqual(list(od), list('bdeaf'))
+
def test_issue24347(self):
OrderedDict = self.module.OrderedDict
diff --git a/Misc/NEWS b/Misc/NEWS
index c4bfa70..6367afd 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -27,6 +27,8 @@ Library
- Issue #24377: Fix a ref leak in OrderedDict.__repr__.
+- Issue #24369: Defend against key-changes during iteration.
+
What's New in Python 3.5.0 beta 2?
==================================
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index 55055ac..313b21a 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -480,20 +480,15 @@ typedef struct _odictnode _ODictNode;
/* PyODictObject */
struct _odictobject {
- /* od_dict is the underlying dict. */
- PyDictObject od_dict;
- /* od_first is the first node in the odict, if any. */
- _ODictNode *od_first;
- /* od_last is the last node in the odict, if any. */
- _ODictNode *od_last;
- /* od_size is the number of entries in od_fast_nodes. */
- Py_ssize_t od_size; /* managed by _odict_resize() */
- /* od_fast_nodes is a hash table that mirrors the dict table. */
+ PyDictObject od_dict; /* the underlying dict */
+ _ODictNode *od_first; /* first node in the linked list, if any */
+ _ODictNode *od_last; /* last node in the linked list, if any */
+ /* od_size and od_fast_nodes are managed by _odict_resize() */
+ Py_ssize_t od_size; /* hash table that mirrors the dict table */
_ODictNode **od_fast_nodes; /* managed by _odict_resize() */
- /* od_inst_dict is OrderedDict().__dict__. */
- PyObject *od_inst_dict;
- /* od_weakreflist holds weakrefs to the odict. */
- PyObject *od_weakreflist;
+ size_t od_state; /* incremented whenever the LL changes */
+ PyObject *od_inst_dict; /* OrderedDict().__dict__ */
+ PyObject *od_weakreflist; /* holds weakrefs to the odict */
};
@@ -608,6 +603,7 @@ _odict_get_index(PyODictObject *od, PyObject *key)
static int
_odict_initialize(PyODictObject *od)
{
+ od->od_state = 0;
_odict_FIRST(od) = NULL;
_odict_LAST(od) = NULL;
return _odict_resize((PyODictObject *)od);
@@ -642,6 +638,7 @@ _odict_add_head(PyODictObject *od, _ODictNode *node)
_odict_FIRST(od) = node;
_odictnode_PREV(_odict_FIRST(od)) = node;
}
+ od->od_state++;
}
static void
@@ -659,6 +656,7 @@ _odict_add_tail(PyODictObject *od, _ODictNode *node)
_odictnode_NEXT(_odict_LAST(od)) = node;
_odict_LAST(od) = node;
}
+ od->od_state++;
}
/* adds the node to the end of the list */
@@ -725,6 +723,7 @@ _odict_remove_node(PyODictObject *od, _ODictNode *node)
_odictnode_PREV(node) = NULL;
_odictnode_NEXT(node) = NULL;
+ od->od_state++;
}
static _ODictNode *
@@ -1829,6 +1828,7 @@ typedef struct {
int kind;
PyODictObject *di_odict;
Py_ssize_t di_size;
+ size_t di_state;
PyObject *di_current;
PyObject *di_result; /* reusable result tuple for iteritems */
} odictiterobject;
@@ -1869,6 +1869,11 @@ odictiter_nextkey(odictiterobject *di)
goto done; /* We're already done. */
/* Check for unsupported changes. */
+ if (di->di_odict->od_state != di->di_state) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "OrderedDict mutated during iteration");
+ goto done;
+ }
if (di->di_size != PyODict_SIZE(di->di_odict)) {
PyErr_SetString(PyExc_RuntimeError,
"OrderedDict changed size during iteration");
@@ -2075,6 +2080,7 @@ odictiter_new(PyODictObject *od, int kind)
di->di_current = node ? _odictnode_KEY(node) : NULL;
Py_XINCREF(di->di_current);
di->di_size = PyODict_SIZE(od);
+ di->di_state = od->od_state;
di->di_odict = od;
Py_INCREF(od);