Issue #24091: Fixed various crashes in corner cases in C implementation of
ElementTree.
This commit is contained in:
parent
ca7fecb038
commit
5bf3120e24
|
@ -1709,6 +1709,126 @@ class BasicElementTest(ElementTestCase, unittest.TestCase):
|
||||||
self.assertEqual(e2[0].tag, 'dogs')
|
self.assertEqual(e2[0].tag, 'dogs')
|
||||||
|
|
||||||
|
|
||||||
|
class BadElementTest(ElementTestCase, unittest.TestCase):
|
||||||
|
def test_extend_mutable_list(self):
|
||||||
|
class X:
|
||||||
|
@property
|
||||||
|
def __class__(self):
|
||||||
|
L[:] = [ET.Element('baz')]
|
||||||
|
return ET.Element
|
||||||
|
L = [X()]
|
||||||
|
e = ET.Element('foo')
|
||||||
|
try:
|
||||||
|
e.extend(L)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Y(X, ET.Element):
|
||||||
|
pass
|
||||||
|
L = [Y('x')]
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend(L)
|
||||||
|
|
||||||
|
def test_extend_mutable_list2(self):
|
||||||
|
class X:
|
||||||
|
@property
|
||||||
|
def __class__(self):
|
||||||
|
del L[:]
|
||||||
|
return ET.Element
|
||||||
|
L = [X(), ET.Element('baz')]
|
||||||
|
e = ET.Element('foo')
|
||||||
|
try:
|
||||||
|
e.extend(L)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Y(X, ET.Element):
|
||||||
|
pass
|
||||||
|
L = [Y('bar'), ET.Element('baz')]
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend(L)
|
||||||
|
|
||||||
|
def test_remove_with_mutating(self):
|
||||||
|
class X(ET.Element):
|
||||||
|
def __eq__(self, o):
|
||||||
|
del e[:]
|
||||||
|
return False
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([X('bar')])
|
||||||
|
self.assertRaises(ValueError, e.remove, ET.Element('baz'))
|
||||||
|
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
self.assertRaises(ValueError, e.remove, X('baz'))
|
||||||
|
|
||||||
|
|
||||||
|
class MutatingElementPath(str):
|
||||||
|
def __new__(cls, elem, *args):
|
||||||
|
self = str.__new__(cls, *args)
|
||||||
|
self.elem = elem
|
||||||
|
return self
|
||||||
|
def __eq__(self, o):
|
||||||
|
del self.elem[:]
|
||||||
|
return True
|
||||||
|
MutatingElementPath.__hash__ = str.__hash__
|
||||||
|
|
||||||
|
class BadElementPath(str):
|
||||||
|
def __eq__(self, o):
|
||||||
|
raise 1/0
|
||||||
|
BadElementPath.__hash__ = str.__hash__
|
||||||
|
|
||||||
|
class BadElementPathTest(ElementTestCase, unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
from xml.etree import ElementPath
|
||||||
|
self.path_cache = ElementPath._cache
|
||||||
|
ElementPath._cache = {}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
from xml.etree import ElementPath
|
||||||
|
ElementPath._cache = self.path_cache
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def test_find_with_mutating(self):
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
e.find(MutatingElementPath(e, 'x'))
|
||||||
|
|
||||||
|
def test_find_with_error(self):
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
try:
|
||||||
|
e.find(BadElementPath('x'))
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_findtext_with_mutating(self):
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
e.findtext(MutatingElementPath(e, 'x'))
|
||||||
|
|
||||||
|
def test_findtext_with_error(self):
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
try:
|
||||||
|
e.findtext(BadElementPath('x'))
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_findall_with_mutating(self):
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
e.findall(MutatingElementPath(e, 'x'))
|
||||||
|
|
||||||
|
def test_findall_with_error(self):
|
||||||
|
e = ET.Element('foo')
|
||||||
|
e.extend([ET.Element('bar')])
|
||||||
|
try:
|
||||||
|
e.findall(BadElementPath('x'))
|
||||||
|
except ZeroDivisionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ElementTreeTypeTest(unittest.TestCase):
|
class ElementTreeTypeTest(unittest.TestCase):
|
||||||
def test_istype(self):
|
def test_istype(self):
|
||||||
self.assertIsInstance(ET.ParseError, type)
|
self.assertIsInstance(ET.ParseError, type)
|
||||||
|
@ -2556,6 +2676,8 @@ def test_main(module=None):
|
||||||
ModuleTest,
|
ModuleTest,
|
||||||
ElementSlicingTest,
|
ElementSlicingTest,
|
||||||
BasicElementTest,
|
BasicElementTest,
|
||||||
|
BadElementTest,
|
||||||
|
BadElementPathTest,
|
||||||
ElementTreeTest,
|
ElementTreeTest,
|
||||||
IOTest,
|
IOTest,
|
||||||
ParseErrorTest,
|
ParseErrorTest,
|
||||||
|
|
|
@ -50,6 +50,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #24091: Fixed various crashes in corner cases in C implementation of
|
||||||
|
ElementTree.
|
||||||
|
|
||||||
- Issue #21931: msilib.FCICreate() now raises TypeError in the case of a bad
|
- Issue #21931: msilib.FCICreate() now raises TypeError in the case of a bad
|
||||||
argument instead of a ValueError with a bogus FCI error number.
|
argument instead of a ValueError with a bogus FCI error number.
|
||||||
Patch by Jeffrey Armstrong.
|
Patch by Jeffrey Armstrong.
|
||||||
|
|
|
@ -1036,7 +1036,7 @@ static PyObject*
|
||||||
element_extend(ElementObject* self, PyObject* args)
|
element_extend(ElementObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
PyObject* seq;
|
PyObject* seq;
|
||||||
Py_ssize_t i, seqlen = 0;
|
Py_ssize_t i;
|
||||||
|
|
||||||
PyObject* seq_in;
|
PyObject* seq_in;
|
||||||
if (!PyArg_ParseTuple(args, "O:extend", &seq_in))
|
if (!PyArg_ParseTuple(args, "O:extend", &seq_in))
|
||||||
|
@ -1051,22 +1051,25 @@ element_extend(ElementObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
seqlen = PySequence_Size(seq);
|
for (i = 0; i < PySequence_Fast_GET_SIZE(seq); i++) {
|
||||||
for (i = 0; i < seqlen; i++) {
|
|
||||||
PyObject* element = PySequence_Fast_GET_ITEM(seq, i);
|
PyObject* element = PySequence_Fast_GET_ITEM(seq, i);
|
||||||
if (!PyObject_IsInstance(element, (PyObject *)&Element_Type)) {
|
Py_INCREF(element);
|
||||||
Py_DECREF(seq);
|
if (!PyObject_TypeCheck(element, (PyTypeObject *)&Element_Type)) {
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
PyExc_TypeError,
|
PyExc_TypeError,
|
||||||
"expected an Element, not \"%.200s\"",
|
"expected an Element, not \"%.200s\"",
|
||||||
Py_TYPE(element)->tp_name);
|
Py_TYPE(element)->tp_name);
|
||||||
|
Py_DECREF(seq);
|
||||||
|
Py_DECREF(element);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element_add_subelement(self, element) < 0) {
|
if (element_add_subelement(self, element) < 0) {
|
||||||
Py_DECREF(seq);
|
Py_DECREF(seq);
|
||||||
|
Py_DECREF(element);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Py_DECREF(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_DECREF(seq);
|
Py_DECREF(seq);
|
||||||
|
@ -1099,11 +1102,16 @@ element_find(ElementObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
|
||||||
for (i = 0; i < self->extra->length; i++) {
|
for (i = 0; i < self->extra->length; i++) {
|
||||||
PyObject* item = self->extra->children[i];
|
PyObject* item = self->extra->children[i];
|
||||||
if (Element_CheckExact(item) &&
|
int rc;
|
||||||
PyObject_RichCompareBool(((ElementObject*)item)->tag, tag, Py_EQ) == 1) {
|
if (!Element_CheckExact(item))
|
||||||
Py_INCREF(item);
|
continue;
|
||||||
|
Py_INCREF(item);
|
||||||
|
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, tag, Py_EQ);
|
||||||
|
if (rc > 0)
|
||||||
return item;
|
return item;
|
||||||
}
|
Py_DECREF(item);
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -1136,14 +1144,24 @@ element_findtext(ElementObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
|
||||||
for (i = 0; i < self->extra->length; i++) {
|
for (i = 0; i < self->extra->length; i++) {
|
||||||
ElementObject* item = (ElementObject*) self->extra->children[i];
|
ElementObject* item = (ElementObject*) self->extra->children[i];
|
||||||
if (Element_CheckExact(item) &&
|
int rc;
|
||||||
(PyObject_RichCompareBool(item->tag, tag, Py_EQ) == 1)) {
|
if (!Element_CheckExact(item))
|
||||||
|
continue;
|
||||||
|
Py_INCREF(item);
|
||||||
|
rc = PyObject_RichCompareBool(item->tag, tag, Py_EQ);
|
||||||
|
if (rc > 0) {
|
||||||
PyObject* text = element_get_text(item);
|
PyObject* text = element_get_text(item);
|
||||||
if (text == Py_None)
|
if (text == Py_None) {
|
||||||
|
Py_DECREF(item);
|
||||||
return PyUnicode_New(0, 0);
|
return PyUnicode_New(0, 0);
|
||||||
|
}
|
||||||
Py_XINCREF(text);
|
Py_XINCREF(text);
|
||||||
|
Py_DECREF(item);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
Py_DECREF(item);
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_INCREF(default_value);
|
Py_INCREF(default_value);
|
||||||
|
@ -1180,13 +1198,17 @@ element_findall(ElementObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
|
||||||
for (i = 0; i < self->extra->length; i++) {
|
for (i = 0; i < self->extra->length; i++) {
|
||||||
PyObject* item = self->extra->children[i];
|
PyObject* item = self->extra->children[i];
|
||||||
if (Element_CheckExact(item) &&
|
int rc;
|
||||||
PyObject_RichCompareBool(((ElementObject*)item)->tag, tag, Py_EQ) == 1) {
|
if (!Element_CheckExact(item))
|
||||||
if (PyList_Append(out, item) < 0) {
|
continue;
|
||||||
Py_DECREF(out);
|
Py_INCREF(item);
|
||||||
return NULL;
|
rc = PyObject_RichCompareBool(((ElementObject*)item)->tag, tag, Py_EQ);
|
||||||
}
|
if (rc != 0 && (rc < 0 || PyList_Append(out, item) < 0)) {
|
||||||
|
Py_DECREF(item);
|
||||||
|
Py_DECREF(out);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Py_DECREF(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -1403,8 +1425,10 @@ static PyObject*
|
||||||
element_remove(ElementObject* self, PyObject* args)
|
element_remove(ElementObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int rc;
|
||||||
PyObject* element;
|
PyObject* element;
|
||||||
|
PyObject* found;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O!:remove", &Element_Type, &element))
|
if (!PyArg_ParseTuple(args, "O!:remove", &Element_Type, &element))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1420,11 +1444,14 @@ element_remove(ElementObject* self, PyObject* args)
|
||||||
for (i = 0; i < self->extra->length; i++) {
|
for (i = 0; i < self->extra->length; i++) {
|
||||||
if (self->extra->children[i] == element)
|
if (self->extra->children[i] == element)
|
||||||
break;
|
break;
|
||||||
if (PyObject_RichCompareBool(self->extra->children[i], element, Py_EQ) == 1)
|
rc = PyObject_RichCompareBool(self->extra->children[i], element, Py_EQ);
|
||||||
|
if (rc > 0)
|
||||||
break;
|
break;
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == self->extra->length) {
|
if (i >= self->extra->length) {
|
||||||
/* element is not in children, so raise exception */
|
/* element is not in children, so raise exception */
|
||||||
PyErr_SetString(
|
PyErr_SetString(
|
||||||
PyExc_ValueError,
|
PyExc_ValueError,
|
||||||
|
@ -1433,13 +1460,13 @@ element_remove(ElementObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_DECREF(self->extra->children[i]);
|
found = self->extra->children[i];
|
||||||
|
|
||||||
self->extra->length--;
|
self->extra->length--;
|
||||||
|
|
||||||
for (; i < self->extra->length; i++)
|
for (; i < self->extra->length; i++)
|
||||||
self->extra->children[i] = self->extra->children[i+1];
|
self->extra->children[i] = self->extra->children[i+1];
|
||||||
|
|
||||||
|
Py_DECREF(found);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2012,6 +2039,7 @@ elementiter_next(ElementIterObject *it)
|
||||||
*/
|
*/
|
||||||
ElementObject *cur_parent;
|
ElementObject *cur_parent;
|
||||||
Py_ssize_t child_index;
|
Py_ssize_t child_index;
|
||||||
|
int rc;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
/* Handle the case reached in the beginning and end of iteration, where
|
/* Handle the case reached in the beginning and end of iteration, where
|
||||||
|
@ -2033,14 +2061,22 @@ elementiter_next(ElementIterObject *it)
|
||||||
}
|
}
|
||||||
|
|
||||||
it->root_done = 1;
|
it->root_done = 1;
|
||||||
if (it->sought_tag == Py_None ||
|
rc = (it->sought_tag == Py_None);
|
||||||
PyObject_RichCompareBool(it->root_element->tag,
|
if (!rc) {
|
||||||
it->sought_tag, Py_EQ) == 1) {
|
rc = PyObject_RichCompareBool(it->root_element->tag,
|
||||||
|
it->sought_tag, Py_EQ);
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (rc) {
|
||||||
if (it->gettext) {
|
if (it->gettext) {
|
||||||
PyObject *text = element_get_text(it->root_element);
|
PyObject *text = element_get_text(it->root_element);
|
||||||
if (!text)
|
if (!text)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (PyObject_IsTrue(text)) {
|
rc = PyObject_IsTrue(text);
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
|
if (rc) {
|
||||||
Py_INCREF(text);
|
Py_INCREF(text);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
@ -2072,18 +2108,26 @@ elementiter_next(ElementIterObject *it)
|
||||||
PyObject *text = element_get_text(child);
|
PyObject *text = element_get_text(child);
|
||||||
if (!text)
|
if (!text)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (PyObject_IsTrue(text)) {
|
rc = PyObject_IsTrue(text);
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
|
if (rc) {
|
||||||
Py_INCREF(text);
|
Py_INCREF(text);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
} else if (it->sought_tag == Py_None ||
|
} else {
|
||||||
PyObject_RichCompareBool(child->tag,
|
rc = (it->sought_tag == Py_None);
|
||||||
it->sought_tag, Py_EQ) == 1) {
|
if (!rc) {
|
||||||
Py_INCREF(child);
|
rc = PyObject_RichCompareBool(child->tag,
|
||||||
return (PyObject *)child;
|
it->sought_tag, Py_EQ);
|
||||||
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (rc) {
|
||||||
|
Py_INCREF(child);
|
||||||
|
return (PyObject *)child;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject *tail;
|
PyObject *tail;
|
||||||
|
@ -2103,9 +2147,14 @@ elementiter_next(ElementIterObject *it)
|
||||||
* this is because itertext() is supposed to only return *inner*
|
* this is because itertext() is supposed to only return *inner*
|
||||||
* text, not text following the element it began iteration with.
|
* text, not text following the element it began iteration with.
|
||||||
*/
|
*/
|
||||||
if (it->parent_stack->parent && PyObject_IsTrue(tail)) {
|
if (it->parent_stack->parent) {
|
||||||
Py_INCREF(tail);
|
rc = PyObject_IsTrue(tail);
|
||||||
return tail;
|
if (rc < 0)
|
||||||
|
return NULL;
|
||||||
|
if (rc) {
|
||||||
|
Py_INCREF(tail);
|
||||||
|
return tail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2163,20 +2212,21 @@ static PyObject *
|
||||||
create_elementiter(ElementObject *self, PyObject *tag, int gettext)
|
create_elementiter(ElementObject *self, PyObject *tag, int gettext)
|
||||||
{
|
{
|
||||||
ElementIterObject *it;
|
ElementIterObject *it;
|
||||||
PyObject *star = NULL;
|
|
||||||
|
|
||||||
it = PyObject_GC_New(ElementIterObject, &ElementIter_Type);
|
it = PyObject_GC_New(ElementIterObject, &ElementIter_Type);
|
||||||
if (!it)
|
if (!it)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (PyUnicode_Check(tag))
|
if (PyUnicode_Check(tag)) {
|
||||||
star = PyUnicode_FromString("*");
|
if (PyUnicode_READY(tag) < 0)
|
||||||
else if (PyBytes_Check(tag))
|
return NULL;
|
||||||
star = PyBytes_FromString("*");
|
if (PyUnicode_GET_LENGTH(tag) == 1 && PyUnicode_READ_CHAR(tag, 0) == '*')
|
||||||
|
tag = Py_None;
|
||||||
if (star && PyObject_RichCompareBool(tag, star, Py_EQ) == 1)
|
}
|
||||||
tag = Py_None;
|
else if (PyBytes_Check(tag)) {
|
||||||
Py_XDECREF(star);
|
if (PyBytes_GET_SIZE(tag) == 1 && *PyBytes_AS_STRING(tag) == '*')
|
||||||
|
tag = Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(tag);
|
Py_INCREF(tag);
|
||||||
it->sought_tag = tag;
|
it->sought_tag = tag;
|
||||||
|
|
Loading…
Reference in New Issue