https://github.com/python/cpython/commit/feafd5f69c5e7cf1bef0ebf9f49bd92d4b34e058
commit: feafd5f69c5e7cf1bef0ebf9f49bd92d4b34e058
branch: 3.13
author: Stan Ulbrych <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-04-25T10:38:14+01:00
summary:
[3.13] gh-148735: Fix a UAF in `Element.findtext()` (GH-148738) (#148923)
(cherry picked from commit 0469e6d38dcb3ff904690028cb3a25155bdcedae)
files:
A Misc/NEWS.d/next/Library/2026-04-18-21-39-15.gh-issue-148735.siw6DG.rst
M Lib/test/test_xml_etree.py
M Modules/_elementtree.c
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 597ec8306120e2..1ecf4051e4537a 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -3167,6 +3167,16 @@ def test_findtext_with_mutating(self):
e.extend([ET.Element('bar')])
e.findtext(cls(e, 'x'))
+ def test_findtext_with_mutating_non_none_text(self):
+ for cls in [MutationDeleteElementPath, MutationClearElementPath]:
+ with self.subTest(cls):
+ e = ET.Element('foo')
+ child = ET.Element('bar')
+ child.text = str(object())
+ e.append(child)
+ del child
+ repr(e.findtext(cls(e, 'x')))
+
def test_findtext_with_error(self):
e = ET.Element('foo')
e.extend([ET.Element('bar')])
diff --git
a/Misc/NEWS.d/next/Library/2026-04-18-21-39-15.gh-issue-148735.siw6DG.rst
b/Misc/NEWS.d/next/Library/2026-04-18-21-39-15.gh-issue-148735.siw6DG.rst
new file mode 100644
index 00000000000000..db5e94c0ccac50
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-04-18-21-39-15.gh-issue-148735.siw6DG.rst
@@ -0,0 +1,3 @@
+:mod:`xml.etree.ElementTree`: Fix a use-after-free in
+:meth:`Element.findtext <xml.etree.ElementTree.Element.findtext>` when the
+element tree is mutated concurrently during the search.
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c
index dfa03663c38c65..e144ad5699886c 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -563,7 +563,7 @@ element_get_attrib(ElementObject* self)
LOCAL(PyObject*)
element_get_text(ElementObject* self)
{
- /* return borrowed reference to text attribute */
+ /* return new reference to text attribute */
PyObject *res = self->text;
@@ -578,13 +578,13 @@ element_get_text(ElementObject* self)
}
}
- return res;
+ return Py_NewRef(res);
}
LOCAL(PyObject*)
element_get_tail(ElementObject* self)
{
- /* return borrowed reference to text attribute */
+ /* return new reference to tail attribute */
PyObject *res = self->tail;
@@ -599,7 +599,7 @@ element_get_tail(ElementObject* self)
}
}
- return res;
+ return Py_NewRef(res);
}
static PyObject*
@@ -1340,9 +1340,9 @@ _elementtree_Element_findtext_impl(ElementObject *self,
PyTypeObject *cls,
PyObject *text = element_get_text((ElementObject *)item);
Py_DECREF(item);
if (text == Py_None) {
+ Py_DECREF(text);
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
}
- Py_XINCREF(text);
return text;
}
Py_DECREF(item);
@@ -2042,15 +2042,13 @@ element_tag_getter(ElementObject *self, void *closure)
static PyObject*
element_text_getter(ElementObject *self, void *closure)
{
- PyObject *res = element_get_text(self);
- return Py_XNewRef(res);
+ return element_get_text(self);
}
static PyObject*
element_tail_getter(ElementObject *self, void *closure)
{
- PyObject *res = element_get_tail(self);
- return Py_XNewRef(res);
+ return element_get_tail(self);
}
static PyObject*
@@ -2283,16 +2281,14 @@ elementiter_next(ElementIterObject *it)
continue;
gettext:
+ Py_DECREF(elem);
if (!text) {
- Py_DECREF(elem);
return NULL;
}
if (text == Py_None) {
- Py_DECREF(elem);
+ Py_DECREF(text);
}
else {
- Py_INCREF(text);
- Py_DECREF(elem);
rc = PyObject_IsTrue(text);
if (rc > 0)
return text;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]