]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #25395: Fixed crash when highly nested OrderedDict structures were
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 1 Nov 2015 14:12:34 +0000 (16:12 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Sun, 1 Nov 2015 14:12:34 +0000 (16:12 +0200)
garbage collected.

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

index 3ed3abdf2fe3dc4f47d02ff5e56a25946077e0e2..53a3ae4aa4495cc5874873dd350e9a6daa5f23ac 100644 (file)
@@ -2025,6 +2025,30 @@ class OrderedDictTests:
         items = [('a', 1), ('c', 3), ('b', 2)]
         self.assertEqual(list(MyOD(items).items()), items)
 
+    def test_highly_nested(self):
+        # Issue 25395: crashes during garbage collection
+        OrderedDict = self.module.OrderedDict
+        obj = None
+        for _ in range(1000):
+            obj = OrderedDict([(None, obj)])
+        del obj
+        support.gc_collect()
+
+    def test_highly_nested_subclass(self):
+        # Issue 25395: crashes during garbage collection
+        OrderedDict = self.module.OrderedDict
+        deleted = []
+        class MyOD(OrderedDict):
+            def __del__(self):
+                deleted.append(self.i)
+        obj = None
+        for i in range(100):
+            obj = MyOD([(None, obj)])
+            obj.i = i
+        del obj
+        support.gc_collect()
+        self.assertEqual(deleted, list(reversed(range(100))))
+
 
 class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
 
index 5b02c7802f018490f49ff6db952fef5ea1321cfd..a8a1f3170a2ee142d2c93b8ff17c014b396c5836 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -11,6 +11,9 @@ Release date: TBA
 Core and Builtins
 -----------------
 
+- Issue #25395: Fixed crash when highly nested OrderedDict structures were
+  garbage collected.
+
 - Issue #25274: sys.setrecursionlimit() now raises a RecursionError if the new
   recursion limit is too low depending at the current recursion depth. Modify
   also the "lower-water mark" formula to make it monotonic. This mark is used
index 4bdd45ab82694dcf205ad1b931cb6ba818c80df6..a03e99567ec02ac0e042eb02e3a7086291c1db6d 100644 (file)
@@ -1431,17 +1431,28 @@ static PyMemberDef odict_members[] = {
 static void
 odict_dealloc(PyODictObject *self)
 {
+    PyThreadState *tstate = PyThreadState_GET();
+
     PyObject_GC_UnTrack(self);
-    Py_TRASHCAN_SAFE_BEGIN(self);
+    Py_TRASHCAN_SAFE_BEGIN(self)
+
     Py_XDECREF(self->od_inst_dict);
     if (self->od_weakreflist != NULL)
         PyObject_ClearWeakRefs((PyObject *)self);
 
     _odict_clear_nodes(self);
-    Py_TRASHCAN_SAFE_END(self);
 
-    /* must be last */
+    /* Call the base tp_dealloc().  Since it too uses the trashcan mechanism,
+     * temporarily decrement trash_delete_nesting to prevent triggering it
+     * and putting the partially deallocated object on the trashcan's
+     * to-be-deleted-later list.
+     */
+    --tstate->trash_delete_nesting;
+    assert(_tstate->trash_delete_nesting < PyTrash_UNWIND_LEVEL);
     PyDict_Type.tp_dealloc((PyObject *)self);
+    ++tstate->trash_delete_nesting;
+
+    Py_TRASHCAN_SAFE_END(self)
 };
 
 /* tp_repr */