mirror of
https://github.com/python/cpython.git
synced 2025-08-19 00:00:48 +00:00
[3.13] gh-133009: fix UAF in xml.etree.ElementTree.Element.__deepcopy__
(GH-133010) (#133806)
gh-133009: fix UAF in `xml.etree.ElementTree.Element.__deepcopy__` (GH-133010)
(cherry picked from commit 116a9f9b37
)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
57efb77fef
commit
9718880ba9
3 changed files with 81 additions and 7 deletions
|
@ -810,6 +810,8 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
|
|||
|
||||
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;
|
||||
|
@ -844,11 +846,13 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
|
|||
|
||||
assert(!element->extra || !element->extra->length);
|
||||
if (self->extra) {
|
||||
if (element_resize(element, self->extra->length) < 0)
|
||||
Py_ssize_t expected_count = self->extra->length;
|
||||
if (element_resize(element, expected_count) < 0) {
|
||||
assert(!element->extra->length);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// TODO(picnixz): check for an evil child's __deepcopy__ on 'self'
|
||||
for (i = 0; i < self->extra->length; i++) {
|
||||
for (i = 0; self->extra && i < self->extra->length; i++) {
|
||||
PyObject* child = deepcopy(st, self->extra->children[i], memo);
|
||||
if (!child || !Element_Check(st, child)) {
|
||||
if (child) {
|
||||
|
@ -858,11 +862,24 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
|
|||
element->extra->length = i;
|
||||
goto error;
|
||||
}
|
||||
if (self->extra && expected_count != self->extra->length) {
|
||||
// 'self->extra' got mutated and 'element' may not have
|
||||
// sufficient space to hold the next iteration's item.
|
||||
expected_count = self->extra->length;
|
||||
if (element_resize(element, expected_count) < 0) {
|
||||
Py_DECREF(child);
|
||||
element->extra->length = i;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
element->extra->children[i] = child;
|
||||
}
|
||||
|
||||
assert(!element->extra->length);
|
||||
element->extra->length = self->extra->length;
|
||||
// The original 'self->extra' may be gone at this point if deepcopy()
|
||||
// has side-effects. However, 'i' is the number of copied items that
|
||||
// we were able to successfully copy.
|
||||
element->extra->length = i;
|
||||
}
|
||||
|
||||
/* add object to memo dictionary (so deepcopy won't visit it again) */
|
||||
|
@ -905,13 +922,20 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (simple)
|
||||
if (simple) {
|
||||
return PyDict_Copy(object);
|
||||
}
|
||||
/* Fall through to general case */
|
||||
}
|
||||
else if (Element_CheckExact(st, object)) {
|
||||
return _elementtree_Element___deepcopy___impl(
|
||||
// The __deepcopy__() call may call arbitrary code even if the
|
||||
// object to copy is a built-in XML element (one of its children
|
||||
// any of its parents in its own __deepcopy__() implementation).
|
||||
Py_INCREF(object);
|
||||
PyObject *res = _elementtree_Element___deepcopy___impl(
|
||||
(ElementObject *)object, memo);
|
||||
Py_DECREF(object);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -922,8 +946,11 @@ deepcopy(elementtreestate *st, PyObject *object, PyObject *memo)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(object);
|
||||
PyObject *args[2] = {object, memo};
|
||||
return PyObject_Vectorcall(st->deepcopy_obj, args, 2, NULL);
|
||||
PyObject *res = PyObject_Vectorcall(st->deepcopy_obj, args, 2, NULL);
|
||||
Py_DECREF(object);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue