mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
[3.14] gh-148801: Fix unbound C recursion in Element.__deepcopy__() (GH-148802) (#148842)
(cherry picked from commit 33e82be174)
This commit is contained in:
@@ -3098,6 +3098,19 @@ class BadElementTest(ElementTestCase, unittest.TestCase):
|
||||
self.assertEqual([c.tag for c in children[3:]],
|
||||
[a.tag, b.tag, a.tag, b.tag])
|
||||
|
||||
@support.skip_if_unlimited_stack_size
|
||||
@support.skip_emscripten_stack_overflow()
|
||||
@support.skip_wasi_stack_overflow()
|
||||
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(150_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
@@ -16,6 +16,7 @@
|
||||
#endif
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
|
||||
#include "pycore_pyhash.h" // _Py_HashSecret
|
||||
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
|
||||
|
||||
@@ -802,26 +803,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;
|
||||
@@ -832,8 +838,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)
|
||||
@@ -895,10 +902,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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user