[issue25395] SIGSEGV using json.tool: highly nested OrderedDict

Serhiy Storchaka report at bugs.python.org
Sun Oct 18 06:39:35 EDT 2015


Serhiy Storchaka added the comment:

Using Py_CLEAR() fixes symptoms in this test, but the real issue is that 
deallocating code is executed twice for some objects (for every 25th 
OrderedDict).

PyDict_Type.tp_dealloc() can deposit an object in the _PyTrash_delete_later 
list if trash_delete_nesting exceeds PyTrash_UNWIND_LEVEL. Later Py_TYPE(op)-
>tp_dealloc is called for objects in the _PyTrash_delete_later list. But this 
is odict_dealloc that already was called.

One of ways to avoid repeated deallocation is to change the type of the object 
before calling PyDict_Type.tp_dealloc(). This looks as a hack, but correct 
hack. May be there is more straightforward solution.

----------
Added file: http://bugs.python.org/file40808/odict-trashcan.v3.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue25395>
_______________________________________
-------------- next part --------------
diff -r 741ef17e9b86 Lib/test/test_collections.py
--- a/Lib/test/test_collections.py	Sun Oct 18 09:54:42 2015 +0300
+++ b/Lib/test/test_collections.py	Sun Oct 18 13:28:41 2015 +0300
@@ -2033,6 +2033,14 @@ 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
+        obj = None
+        for _ in range(1000):
+            obj = self.module.OrderedDict([(None, obj)])
+        del obj
+        support.gc_collect()
+
 
 class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
 
diff -r 741ef17e9b86 Misc/NEWS
--- a/Misc/NEWS	Sun Oct 18 09:54:42 2015 +0300
+++ b/Misc/NEWS	Sun Oct 18 13:28:41 2015 +0300
@@ -10,6 +10,9 @@ Release date: XXXX-XX-XX
 Core and Builtins
 -----------------
 
+- Issue #25395: Fix crash when highly nested OrderedDict structures were
+  garbage collected.
+
 - Issue #25401: Optimize bytes.fromhex() and bytearray.fromhex(): they are now
   between 2x and 3.5x faster.
 
diff -r 741ef17e9b86 Objects/odictobject.c
--- a/Objects/odictobject.c	Sun Oct 18 09:54:42 2015 +0300
+++ b/Objects/odictobject.c	Sun Oct 18 13:28:41 2015 +0300
@@ -1438,16 +1438,19 @@ static void
 odict_dealloc(PyODictObject *self)
 {
     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 */
+    Py_TYPE(self) = &PyDict_Type;
     PyDict_Type.tp_dealloc((PyObject *)self);
+
+    Py_TRASHCAN_SAFE_END(self)
 };
 
 /* tp_repr */


More information about the Python-bugs-list mailing list