From 710cd34bdbaddda5c5f03033c068e44a1bac7f21 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 4 Nov 2015 22:33:07 +0200 Subject: Issue #25449: Fixed a crash and leaking NULL in repr() of OrderedDict that was mutated by direct calls of dict methods. --- Lib/test/test_collections.py | 54 ++++++++++++++++++++++++++++++++++++++++++++ Misc/NEWS | 3 +++ Objects/odictobject.c | 15 ++++++++++-- 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, -- cgit v0.12