Skip to content

Commit 39ecb9c

Browse files
orenmnserhiy-storchaka
authored andcommitted
bpo-31728: Prevent crashes in _elementtree due to unsafe cleanup of Element.text and Element.tail (#3924)
1 parent 93c5a5d commit 39ecb9c

File tree

3 files changed

+62
-34
lines changed

3 files changed

+62
-34
lines changed

Lib/test/test_xml_etree_c.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,38 @@ def parser_ref_cycle():
8585
# and so destroy the parser
8686
support.gc_collect()
8787

88+
def test_bpo_31728(self):
89+
# A crash or an assertion failure shouldn't happen, in case garbage
90+
# collection triggers a call to clear() or a reading of text or tail,
91+
# while a setter or clear() or __setstate__() is already running.
92+
elem = cET.Element('elem')
93+
class X:
94+
def __del__(self):
95+
elem.text
96+
elem.tail
97+
elem.clear()
98+
99+
elem.text = X()
100+
elem.clear() # shouldn't crash
101+
102+
elem.tail = X()
103+
elem.clear() # shouldn't crash
104+
105+
elem.text = X()
106+
elem.text = X() # shouldn't crash
107+
elem.clear()
108+
109+
elem.tail = X()
110+
elem.tail = X() # shouldn't crash
111+
elem.clear()
112+
113+
elem.text = X()
114+
elem.__setstate__({'tag': 42}) # shouldn't cause an assertion failure
115+
elem.clear()
116+
117+
elem.tail = X()
118+
elem.__setstate__({'tag': 42}) # shouldn't cause an assertion failure
119+
88120

89121
@unittest.skipUnless(cET, 'requires _elementtree')
90122
class TestAliasWorking(unittest.TestCase):
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: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,22 @@ do { memory -= size; printf("%8d - %s\n", memory, comment); } while (0)
6161
#define JOIN_SET(p, flag) ((void*) ((uintptr_t) (JOIN_OBJ(p)) | (flag)))
6262
#define JOIN_OBJ(p) ((PyObject*) ((uintptr_t) (p) & ~(uintptr_t)1))
6363

64+
/* Py_SETREF for a PyObject* that uses a join flag. */
65+
Py_LOCAL_INLINE(void)
66+
_set_joined_ptr(PyObject **p, PyObject *new_joined_ptr)
67+
{
68+
PyObject *tmp = JOIN_OBJ(*p);
69+
*p = new_joined_ptr;
70+
Py_DECREF(tmp);
71+
}
72+
6473
/* Py_CLEAR for a PyObject* that uses a join flag. Pass the pointer by
6574
* reference since this function sets it to NULL.
6675
*/
6776
static void _clear_joined_ptr(PyObject **p)
6877
{
6978
if (*p) {
70-
PyObject *tmp = JOIN_OBJ(*p);
71-
*p = NULL;
72-
Py_DECREF(tmp);
79+
_set_joined_ptr(p, NULL);
7380
}
7481
}
7582

@@ -356,7 +363,6 @@ static int
356363
element_init(PyObject *self, PyObject *args, PyObject *kwds)
357364
{
358365
PyObject *tag;
359-
PyObject *tmp;
360366
PyObject *attrib = NULL;
361367
ElementObject *self_elem;
362368

@@ -397,15 +403,11 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds)
397403
Py_INCREF(tag);
398404
Py_XSETREF(self_elem->tag, tag);
399405

400-
tmp = self_elem->text;
401406
Py_INCREF(Py_None);
402-
self_elem->text = Py_None;
403-
Py_DECREF(JOIN_OBJ(tmp));
407+
_set_joined_ptr(&self_elem->text, Py_None);
404408

405-
tmp = self_elem->tail;
406409
Py_INCREF(Py_None);
407-
self_elem->tail = Py_None;
408-
Py_DECREF(JOIN_OBJ(tmp));
410+
_set_joined_ptr(&self_elem->tail, Py_None);
409411

410412
return 0;
411413
}
@@ -675,12 +677,10 @@ _elementtree_Element_clear_impl(ElementObject *self)
675677
dealloc_extra(self);
676678

677679
Py_INCREF(Py_None);
678-
Py_DECREF(JOIN_OBJ(self->text));
679-
self->text = Py_None;
680+
_set_joined_ptr(&self->text, Py_None);
680681

681682
Py_INCREF(Py_None);
682-
Py_DECREF(JOIN_OBJ(self->tail));
683-
self->tail = Py_None;
683+
_set_joined_ptr(&self->tail, Py_None);
684684

685685
Py_RETURN_NONE;
686686
}
@@ -702,13 +702,11 @@ _elementtree_Element___copy___impl(ElementObject *self)
702702
if (!element)
703703
return NULL;
704704

705-
Py_DECREF(JOIN_OBJ(element->text));
706-
element->text = self->text;
707-
Py_INCREF(JOIN_OBJ(element->text));
705+
Py_INCREF(JOIN_OBJ(self->text));
706+
_set_joined_ptr(&element->text, self->text);
708707

709-
Py_DECREF(JOIN_OBJ(element->tail));
710-
element->tail = self->tail;
711-
Py_INCREF(JOIN_OBJ(element->tail));
708+
Py_INCREF(JOIN_OBJ(self->tail));
709+
_set_joined_ptr(&element->tail, self->tail);
712710

713711
if (self->extra) {
714712
if (element_resize(element, self->extra->length) < 0) {
@@ -776,14 +774,12 @@ _elementtree_Element___deepcopy___impl(ElementObject *self, PyObject *memo)
776774
text = deepcopy(JOIN_OBJ(self->text), memo);
777775
if (!text)
778776
goto error;
779-
Py_DECREF(element->text);
780-
element->text = JOIN_SET(text, JOIN_GET(self->text));
777+
_set_joined_ptr(&element->text, JOIN_SET(text, JOIN_GET(self->text)));
781778

782779
tail = deepcopy(JOIN_OBJ(self->tail), memo);
783780
if (!tail)
784781
goto error;
785-
Py_DECREF(element->tail);
786-
element->tail = JOIN_SET(tail, JOIN_GET(self->tail));
782+
_set_joined_ptr(&element->tail, JOIN_SET(tail, JOIN_GET(self->tail)));
787783

788784
if (self->extra) {
789785
if (element_resize(element, self->extra->length) < 0)
@@ -968,13 +964,13 @@ element_setstate_from_attributes(ElementObject *self,
968964
Py_INCREF(tag);
969965
Py_XSETREF(self->tag, tag);
970966

971-
_clear_joined_ptr(&self->text);
972-
self->text = text ? JOIN_SET(text, PyList_CheckExact(text)) : Py_None;
973-
Py_INCREF(JOIN_OBJ(self->text));
967+
text = text ? JOIN_SET(text, PyList_CheckExact(text)) : Py_None;
968+
Py_INCREF(JOIN_OBJ(text));
969+
_set_joined_ptr(&self->text, text);
974970

975-
_clear_joined_ptr(&self->tail);
976-
self->tail = tail ? JOIN_SET(tail, PyList_CheckExact(tail)) : Py_None;
977-
Py_INCREF(JOIN_OBJ(self->tail));
971+
tail = tail ? JOIN_SET(tail, PyList_CheckExact(tail)) : Py_None;
972+
Py_INCREF(JOIN_OBJ(tail));
973+
_set_joined_ptr(&self->tail, tail);
978974

979975
/* Handle ATTRIB and CHILDREN. */
980976
if (!children && !attrib)
@@ -2009,8 +2005,7 @@ element_text_setter(ElementObject *self, PyObject *value, void *closure)
20092005
{
20102006
_VALIDATE_ATTR_VALUE(value);
20112007
Py_INCREF(value);
2012-
Py_DECREF(JOIN_OBJ(self->text));
2013-
self->text = value;
2008+
_set_joined_ptr(&self->text, value);
20142009
return 0;
20152010
}
20162011

@@ -2019,8 +2014,7 @@ element_tail_setter(ElementObject *self, PyObject *value, void *closure)
20192014
{
20202015
_VALIDATE_ATTR_VALUE(value);
20212016
Py_INCREF(value);
2022-
Py_DECREF(JOIN_OBJ(self->tail));
2023-
self->tail = value;
2017+
_set_joined_ptr(&self->tail, value);
20242018
return 0;
20252019
}
20262020

0 commit comments

Comments
 (0)