gh-126037: fix UAF in xml.etree.ElementTree.Element.find* when concurrent mutations happen (#127964)

We fix a use-after-free in the `find`, `findtext` and `findall` methods of `xml.etree.ElementTree.Element`
objects that can be triggered when the tag to find implements an `__eq__` method that mutates the
element being queried.
This commit is contained in:
Bénédikt Tran 2025-03-31 12:26:52 +02:00 committed by GitHub
parent 6aa88a2cb3
commit c57623c221
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 69 additions and 48 deletions

View file

@ -1252,29 +1252,28 @@ _elementtree_Element_find_impl(ElementObject *self, PyTypeObject *cls,
PyObject *path, PyObject *namespaces)
/*[clinic end generated code: output=18f77d393c9fef1b input=94df8a83f956acc6]*/
{
Py_ssize_t i;
elementtreestate *st = get_elementtree_state_by_cls(cls);
if (checkpath(path) || namespaces != Py_None) {
return PyObject_CallMethodObjArgs(
st->elementpath_obj, st->str_find, self, path, namespaces, NULL
);
);
}
if (!self->extra)
Py_RETURN_NONE;
for (i = 0; i < self->extra->length; i++) {
PyObject* item = self->extra->children[i];
int rc;
for (Py_ssize_t i = 0; self->extra && i < self->extra->length; i++) {
PyObject *item = self->extra->children[i];
assert(Element_Check(st, item));
Py_INCREF(item);
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
if (rc > 0)
PyObject *tag = Py_NewRef(((ElementObject *)item)->tag);
int rc = PyObject_RichCompareBool(tag, path, Py_EQ);
Py_DECREF(tag);
if (rc > 0) {
return item;
}
Py_DECREF(item);
if (rc < 0)
if (rc < 0) {
return NULL;
}
}
Py_RETURN_NONE;
@ -1297,38 +1296,34 @@ _elementtree_Element_findtext_impl(ElementObject *self, PyTypeObject *cls,
PyObject *namespaces)
/*[clinic end generated code: output=6af7a2d96aac32cb input=32f252099f62a3d2]*/
{
Py_ssize_t i;
elementtreestate *st = get_elementtree_state_by_cls(cls);
if (checkpath(path) || namespaces != Py_None)
return PyObject_CallMethodObjArgs(
st->elementpath_obj, st->str_findtext,
self, path, default_value, namespaces, NULL
);
);
if (!self->extra) {
return Py_NewRef(default_value);
}
for (i = 0; i < self->extra->length; i++) {
for (Py_ssize_t i = 0; self->extra && i < self->extra->length; i++) {
PyObject *item = self->extra->children[i];
int rc;
assert(Element_Check(st, item));
Py_INCREF(item);
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
PyObject *tag = Py_NewRef(((ElementObject *)item)->tag);
int rc = PyObject_RichCompareBool(tag, path, Py_EQ);
Py_DECREF(tag);
if (rc > 0) {
PyObject* text = element_get_text((ElementObject*)item);
PyObject *text = element_get_text((ElementObject *)item);
Py_DECREF(item);
if (text == Py_None) {
Py_DECREF(item);
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
}
Py_XINCREF(text);
Py_DECREF(item);
return text;
}
Py_DECREF(item);
if (rc < 0)
if (rc < 0) {
return NULL;
}
}
return Py_NewRef(default_value);
@ -1349,29 +1344,26 @@ _elementtree_Element_findall_impl(ElementObject *self, PyTypeObject *cls,
PyObject *path, PyObject *namespaces)
/*[clinic end generated code: output=65e39a1208f3b59e input=7aa0db45673fc9a5]*/
{
Py_ssize_t i;
PyObject* out;
elementtreestate *st = get_elementtree_state_by_cls(cls);
if (checkpath(path) || namespaces != Py_None) {
return PyObject_CallMethodObjArgs(
st->elementpath_obj, st->str_findall, self, path, namespaces, NULL
);
);
}
out = PyList_New(0);
if (!out)
PyObject *out = PyList_New(0);
if (out == NULL) {
return NULL;
}
if (!self->extra)
return out;
for (i = 0; i < self->extra->length; i++) {
PyObject* item = self->extra->children[i];
int rc;
for (Py_ssize_t i = 0; self->extra && i < self->extra->length; i++) {
PyObject *item = self->extra->children[i];
assert(Element_Check(st, item));
Py_INCREF(item);
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, path, Py_EQ);
PyObject *tag = Py_NewRef(((ElementObject *)item)->tag);
int rc = PyObject_RichCompareBool(tag, path, Py_EQ);
Py_DECREF(tag);
if (rc != 0 && (rc < 0 || PyList_Append(out, item) < 0)) {
Py_DECREF(item);
Py_DECREF(out);