summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_collections.py24
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/odictobject.c17
3 files changed, 41 insertions, 3 deletions
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index af79af9..4cb369f 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -2033,6 +2033,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):
diff --git a/Misc/NEWS b/Misc/NEWS
index b581823..6ce5f2d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ Release date: XXXX-XX-XX
Core and Builtins
-----------------
+- Issue #25395: Fixed 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 --git a/Objects/odictobject.c b/Objects/odictobject.c
index 4bdd45a..a03e995 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -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 */