Skip to content

Commit f15058a

Browse files
orenmnserhiy-storchaka
authored andcommitted
[2.7] bpo-31728: Prevent crashes in _elementtree due to unsafe cleanup of Element.text and Element.tail (GH-3924) (#3950)
1 parent cfe1aef commit f15058a

File tree

3 files changed

+48
-20
lines changed

3 files changed

+48
-20
lines changed

‎Lib/test/test_xml_etree_c.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,31 @@ def test_del_attribute(self):
5353
del element.attrib
5454
self.assertEqual(element.attrib, {'A': 'B', 'C': 'D'})
5555

56+
def test_bpo_31728(self):
57+
# A crash shouldn't happen in case garbage collection triggers a call
58+
# to clear() or a reading of text or tail, while a setter or clear()
59+
# is already running.
60+
elem = cET.Element('elem')
61+
class X:
62+
def __del__(self):
63+
elem.text
64+
elem.tail
65+
elem.clear()
66+
67+
elem.text = X()
68+
elem.clear() # shouldn't crash
69+
70+
elem.tail = X()
71+
elem.clear() # shouldn't crash
72+
73+
elem.text = X()
74+
elem.text = X() # shouldn't crash
75+
elem.clear()
76+
77+
elem.tail = X()
78+
elem.tail = X() # shouldn't crash
79+
elem.clear()
80+
5681

5782
def test_main():
5883
from test import test_xml_etree, test_xml_etree_c
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Prevent crashes in `_elementtree` due to unsafe cleanup of `Element.text`
2+
and `Element.tail`. Patch by Oren Milman.

‎Modules/_elementtree.c‎

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ static PyObject* elementpath_obj;
131131

132132
/* helpers */
133133

134+
/* Py_SETREF for a PyObject* that uses a join flag. */
135+
Py_LOCAL_INLINE(void)
136+
_set_joined_ptr(PyObject **p, PyObject *new_joined_ptr)
137+
{
138+
PyObject *tmp = JOIN_OBJ(*p);
139+
*p = new_joined_ptr;
140+
Py_DECREF(tmp);
141+
}
142+
134143
LOCAL(PyObject*)
135144
deepcopy(PyObject* object, PyObject* memo)
136145
{
@@ -585,12 +594,10 @@ element_clear(ElementObject* self, PyObject* args)
585594
}
586595

587596
Py_INCREF(Py_None);
588-
Py_DECREF(JOIN_OBJ(self->text));
589-
self->text = Py_None;
597+
_set_joined_ptr(&self->text, Py_None);
590598

591599
Py_INCREF(Py_None);
592-
Py_DECREF(JOIN_OBJ(self->tail));
593-
self->tail = Py_None;
600+
_set_joined_ptr(&self->tail, Py_None);
594601

595602
Py_RETURN_NONE;
596603
}
@@ -610,13 +617,11 @@ element_copy(ElementObject* self, PyObject* args)
610617
if (!element)
611618
return NULL;
612619

613-
Py_DECREF(JOIN_OBJ(element->text));
614-
element->text = self->text;
615-
Py_INCREF(JOIN_OBJ(element->text));
620+
Py_INCREF(JOIN_OBJ(self->text));
621+
_set_joined_ptr(&element->text, self->text);
616622

617-
Py_DECREF(JOIN_OBJ(element->tail));
618-
element->tail = self->tail;
619-
Py_INCREF(JOIN_OBJ(element->tail));
623+
Py_INCREF(JOIN_OBJ(self->tail));
624+
_set_joined_ptr(&element->tail, self->tail);
620625

621626
if (self->extra) {
622627

@@ -678,14 +683,12 @@ element_deepcopy(ElementObject* self, PyObject* args)
678683
text = deepcopy(JOIN_OBJ(self->text), memo);
679684
if (!text)
680685
goto error;
681-
Py_DECREF(element->text);
682-
element->text = JOIN_SET(text, JOIN_GET(self->text));
686+
_set_joined_ptr(&element->text, JOIN_SET(text, JOIN_GET(self->text)));
683687

684688
tail = deepcopy(JOIN_OBJ(self->tail), memo);
685689
if (!tail)
686690
goto error;
687-
Py_DECREF(element->tail);
688-
element->tail = JOIN_SET(tail, JOIN_GET(self->tail));
691+
_set_joined_ptr(&element->tail, JOIN_SET(tail, JOIN_GET(self->tail)));
689692

690693
if (self->extra) {
691694

@@ -1624,13 +1627,11 @@ element_setattr(ElementObject* self, const char* name, PyObject* value)
16241627
Py_INCREF(value);
16251628
Py_SETREF(self->tag, value);
16261629
} else if (strcmp(name, "text") == 0) {
1627-
Py_DECREF(JOIN_OBJ(self->text));
1628-
self->text = value;
1629-
Py_INCREF(self->text);
1630+
Py_INCREF(value);
1631+
_set_joined_ptr(&self->text, value);
16301632
} else if (strcmp(name, "tail") == 0) {
1631-
Py_DECREF(JOIN_OBJ(self->tail));
1632-
self->tail = value;
1633-
Py_INCREF(self->tail);
1633+
Py_INCREF(value);
1634+
_set_joined_ptr(&self->tail, value);
16341635
} else if (strcmp(name, "attrib") == 0) {
16351636
if (!self->extra)
16361637
element_new_extra(self, NULL);

0 commit comments

Comments
 (0)