[3.13] gh-148801: Fix unbound C recursion in Element.__deepcopy__() (GH-148802) (#148843)

This commit is contained in:
Stan Ulbrych
2026-06-08 20:14:48 +01:00
committed by GitHub
parent 4834b45ee7
commit 8e73feee5c
3 changed files with 28 additions and 7 deletions
+10
View File
@@ -3124,6 +3124,16 @@ class BadElementTest(ElementTestCase, unittest.TestCase):
self.assertEqual([c.tag for c in children[3:]],
[a.tag, b.tag, a.tag, b.tag])
def test_deeply_nested_deepcopy(self):
# This should raise a RecursionError and not crash.
# See https://github.com/python/cpython/issues/148801.
root = cur = ET.Element('s')
for _ in range(500_000):
cur = ET.SubElement(cur, 'u')
with support.infinite_recursion():
with self.assertRaises(RecursionError):
copy.deepcopy(root)
class MutationDeleteElementPath(str):
def __new__(cls, elem, *args):
@@ -0,0 +1,2 @@
:mod:`xml.etree.ElementTree`: Fix a crash in :meth:`Element.__deepcopy__
<object.__deepcopy__>` on deeply nested trees.
+16 -7
View File
@@ -16,6 +16,7 @@
#endif
#include "Python.h"
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
#include "pycore_import.h" // _PyImport_GetModuleAttrString()
#include "pycore_pyhash.h" // _Py_HashSecret
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
@@ -801,26 +802,31 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
/*[clinic end generated code: output=eefc3df50465b642 input=a2d40348c0aade10]*/
{
Py_ssize_t i;
ElementObject* element;
ElementObject* element = NULL;
PyObject* tag;
PyObject* attrib;
PyObject* text;
PyObject* tail;
PyObject* id;
if (_Py_EnterRecursiveCall(" in Element.__deepcopy__")) {
return NULL;
}
PyTypeObject *tp = Py_TYPE(self);
elementtreestate *st = get_elementtree_state_by_type(tp);
// The deepcopy() helper takes care of incrementing the refcount
// of the object to copy so to avoid use-after-frees.
tag = deepcopy(st, self->tag, memo);
if (!tag)
return NULL;
if (!tag) {
goto error;
}
if (self->extra && self->extra->attrib) {
attrib = deepcopy(st, self->extra->attrib, memo);
if (!attrib) {
Py_DECREF(tag);
return NULL;
goto error;
}
} else {
attrib = NULL;
@@ -831,8 +837,9 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
Py_DECREF(tag);
Py_XDECREF(attrib);
if (!element)
return NULL;
if (!element) {
goto error;
}
text = deepcopy(st, JOIN_OBJ(self->text), memo);
if (!text)
@@ -894,10 +901,12 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
if (i < 0)
goto error;
_Py_LeaveRecursiveCall();
return (PyObject*) element;
error:
Py_DECREF(element);
_Py_LeaveRecursiveCall();
Py_XDECREF(element);
return NULL;
}