]> granicus.if.org Git - python/commitdiff
Issue #25449: Fixed a crash and leaking NULL in repr() of OrderedDict that
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 4 Nov 2015 20:33:07 +0000 (22:33 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Wed, 4 Nov 2015 20:33:07 +0000 (22:33 +0200)
was mutated by direct calls of dict methods.

Lib/test/test_collections.py
Misc/NEWS
Objects/odictobject.c

index 3f5304b517b4ca6cc9a893c8e94ad6a144e896b8..b9fdf79463905ef24731f36504ecafa73448989d 100644 (file)
@@ -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):
 
index bf1f52168a98c368a4bd2d0d6ed7925ea4c0bfde..c637cb46be6a3a1d591e24760fefd777a7cf7b24 100644 (file)
--- 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.
 
index d6189a3627556a17f842a7806c69df434cf2c946..28537a84da09ae77cfb568434c4454cc509181ae 100644 (file)
@@ -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,