summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2015-11-04 20:33:07 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2015-11-04 20:33:07 (GMT)
commit710cd34bdbaddda5c5f03033c068e44a1bac7f21 (patch)
tree3d46562db71c1e242fd2fdf498715b79ba4925e9
parentb45b7b213742261c95324766027d44e30656b8e7 (diff)
downloadcpython-710cd34bdbaddda5c5f03033c068e44a1bac7f21.zip
cpython-710cd34bdbaddda5c5f03033c068e44a1bac7f21.tar.gz
cpython-710cd34bdbaddda5c5f03033c068e44a1bac7f21.tar.bz2
Issue #25449: Fixed a crash and leaking NULL in repr() of OrderedDict that
was mutated by direct calls of dict methods.
-rw-r--r--Lib/test/test_collections.py54
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/odictobject.c15
3 files changed, 70 insertions, 2 deletions
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 3f5304b..b9fdf79 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -2145,6 +2145,60 @@ class OrderedDictTests:
key = c0 + c1
od[key] = key
+ # Direct use of dict methods
+
+ def test_dict_setitem(self):
+ OrderedDict = self.OrderedDict
+ od = OrderedDict()
+ dict.__setitem__(od, 'spam', 1)
+ self.assertNotIn('NULL', repr(od))
+
+ def test_dict_delitem(self):
+ OrderedDict = self.OrderedDict
+ od = OrderedDict()
+ od['spam'] = 1
+ od['ham'] = 2
+ dict.__delitem__(od, 'spam')
+ with self.assertRaises(KeyError):
+ repr(od)
+
+ def test_dict_clear(self):
+ OrderedDict = self.OrderedDict
+ od = OrderedDict()
+ od['spam'] = 1
+ od['ham'] = 2
+ dict.clear(od)
+ self.assertNotIn('NULL', repr(od))
+
+ def test_dict_pop(self):
+ OrderedDict = self.OrderedDict
+ od = OrderedDict()
+ od['spam'] = 1
+ od['ham'] = 2
+ dict.pop(od, 'spam')
+ with self.assertRaises(KeyError):
+ repr(od)
+
+ def test_dict_popitem(self):
+ OrderedDict = self.OrderedDict
+ od = OrderedDict()
+ od['spam'] = 1
+ od['ham'] = 2
+ dict.popitem(od)
+ with self.assertRaises(KeyError):
+ repr(od)
+
+ def test_dict_setdefault(self):
+ OrderedDict = self.OrderedDict
+ od = OrderedDict()
+ dict.setdefault(od, 'spam', 1)
+ self.assertNotIn('NULL', repr(od))
+
+ def test_dict_update(self):
+ od = OrderedDict()
+ dict.update(od, [('spam', 1)])
+ self.assertNotIn('NULL', repr(od))
+
class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
diff --git a/Misc/NEWS b/Misc/NEWS
index bf1f521..c637cb4 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -11,6 +11,9 @@ Release date: TBA
Core and Builtins
-----------------
+- Issue #25449: Fixed a crash and leaking NULL in repr() of OrderedDict that
+ was mutated by direct calls of dict methods.
+
- Issue #25449: Iterating OrderedDict with keys with unstable hash now raises
KeyError in C implementations as well as in Python implementation.
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index d6189a3..28537a8 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -1462,7 +1462,6 @@ odict_repr(PyODictObject *self)
{
int i;
_Py_IDENTIFIER(items);
- Py_ssize_t count = -1;
PyObject *pieces = NULL, *result = NULL;
const char *classname;
@@ -1481,6 +1480,7 @@ odict_repr(PyODictObject *self)
}
if (PyODict_CheckExact(self)) {
+ Py_ssize_t count = 0;
_ODictNode *node;
pieces = PyList_New(PyODict_SIZE(self));
if (pieces == NULL)
@@ -1499,8 +1499,19 @@ odict_repr(PyODictObject *self)
if (pair == NULL)
goto Done;
- PyList_SET_ITEM(pieces, ++count, pair); /* steals reference */
+ if (count < PyList_GET_SIZE(pieces))
+ PyList_SET_ITEM(pieces, count, pair); /* steals reference */
+ else {
+ if (PyList_Append(pieces, pair) < 0) {
+ Py_DECREF(pair);
+ goto Done;
+ }
+ Py_DECREF(pair);
+ }
+ count++;
}
+ if (count < PyList_GET_SIZE(pieces))
+ PyList_GET_SIZE(pieces) = count;
}
else {
PyObject *items = _PyObject_CallMethodIdObjArgs((PyObject *)self,