Skip to content

Commit 576def0

Browse files
bpo-27863: Fixed multiple crashes in ElementTree. (#765)
1 parent 918403c commit 576def0

File tree

3 files changed

+167
-48
lines changed

3 files changed

+167
-48
lines changed

‎Lib/test/test_xml_etree.py‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,118 @@ def test_recursive_repr(self):
18791879
with self.assertRaises(RuntimeError):
18801880
repr(e) # Should not crash
18811881

1882+
def test_element_get_text(self):
1883+
# Issue #27863
1884+
class X(str):
1885+
def __del__(self):
1886+
try:
1887+
elem.text
1888+
except NameError:
1889+
pass
1890+
1891+
b = ET.TreeBuilder()
1892+
b.start('tag', {})
1893+
b.data('ABCD')
1894+
b.data(X('EFGH'))
1895+
b.data('IJKL')
1896+
b.end('tag')
1897+
1898+
elem = b.close()
1899+
self.assertEqual(elem.text, 'ABCDEFGHIJKL')
1900+
1901+
def test_element_get_tail(self):
1902+
# Issue #27863
1903+
class X(str):
1904+
def __del__(self):
1905+
try:
1906+
elem[0].tail
1907+
except NameError:
1908+
pass
1909+
1910+
b = ET.TreeBuilder()
1911+
b.start('root', {})
1912+
b.start('tag', {})
1913+
b.end('tag')
1914+
b.data('ABCD')
1915+
b.data(X('EFGH'))
1916+
b.data('IJKL')
1917+
b.end('root')
1918+
1919+
elem = b.close()
1920+
self.assertEqual(elem[0].tail, 'ABCDEFGHIJKL')
1921+
1922+
def test_element_iter(self):
1923+
# Issue #27863
1924+
state = {
1925+
'tag': 'tag',
1926+
'_children': [None], # non-Element
1927+
'attrib': 'attr',
1928+
'tail': 'tail',
1929+
'text': 'text',
1930+
}
1931+
1932+
e = ET.Element('tag')
1933+
try:
1934+
e.__setstate__(state)
1935+
except AttributeError:
1936+
e.__dict__ = state
1937+
1938+
it = e.iter()
1939+
self.assertIs(next(it), e)
1940+
self.assertRaises(AttributeError, next, it)
1941+
1942+
def test_subscr(self):
1943+
# Issue #27863
1944+
class X:
1945+
def __index__(self):
1946+
del e[:]
1947+
return 1
1948+
1949+
e = ET.Element('elem')
1950+
e.append(ET.Element('child'))
1951+
e[:X()] # shouldn't crash
1952+
1953+
e.append(ET.Element('child'))
1954+
e[0:10:X()] # shouldn't crash
1955+
1956+
def test_ass_subscr(self):
1957+
# Issue #27863
1958+
class X:
1959+
def __index__(self):
1960+
e[:] = []
1961+
return 1
1962+
1963+
e = ET.Element('elem')
1964+
for _ in range(10):
1965+
e.insert(0, ET.Element('child'))
1966+
1967+
e[0:10:X()] = [] # shouldn't crash
1968+
1969+
def test_treebuilder_start(self):
1970+
# Issue #27863
1971+
def element_factory(x, y):
1972+
return []
1973+
b = ET.TreeBuilder(element_factory=element_factory)
1974+
1975+
b.start('tag', {})
1976+
b.data('ABCD')
1977+
self.assertRaises(AttributeError, b.start, 'tag2', {})
1978+
del b
1979+
gc_collect()
1980+
1981+
def test_treebuilder_end(self):
1982+
# Issue #27863
1983+
def element_factory(x, y):
1984+
return []
1985+
b = ET.TreeBuilder(element_factory=element_factory)
1986+
1987+
b.start('tag', {})
1988+
b.data('ABCD')
1989+
self.assertRaises(AttributeError, b.end, 'tag')
1990+
del b
1991+
gc_collect()
1992+
1993+
18821994
class MutatingElementPath(str):
18831995
def __new__(cls, elem, *args):
18841996
self = str.__new__(cls, *args)

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ Extension Modules
298298
Library
299299
-------
300300

301+
- bpo-27863: Fixed multiple crashes in ElementTree caused by race conditions
302+
and wrong types.
303+
301304
- bpo-25996: Added support of file descriptors in os.scandir() on Unix.
302305
os.fwalk() is sped up by 2 times by using os.scandir().
303306

‎Modules/_elementtree.c‎

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ elementtree_free(void *m)
131131
LOCAL(PyObject*)
132132
list_join(PyObject* list)
133133
{
134-
/* join list elements (destroying the list in the process) */
134+
/* join list elements */
135135
PyObject* joiner;
136136
PyObject* result;
137137

@@ -140,8 +140,6 @@ list_join(PyObject* list)
140140
return NULL;
141141
result = PyUnicode_Join(joiner, list);
142142
Py_DECREF(joiner);
143-
if (result)
144-
Py_DECREF(list);
145143
return result;
146144
}
147145

@@ -508,15 +506,17 @@ element_get_text(ElementObject* self)
508506
{
509507
/* return borrowed reference to text attribute */
510508

511-
PyObject* res = self->text;
509+
PyObject *res = self->text;
512510

513511
if (JOIN_GET(res)) {
514512
res = JOIN_OBJ(res);
515513
if (PyList_CheckExact(res)) {
516-
res = list_join(res);
517-
if (!res)
514+
PyObject *tmp = list_join(res);
515+
if (!tmp)
518516
return NULL;
519-
self->text = res;
517+
self->text = tmp;
518+
Py_DECREF(res);
519+
res = tmp;
520520
}
521521
}
522522

@@ -528,15 +528,17 @@ element_get_tail(ElementObject* self)
528528
{
529529
/* return borrowed reference to text attribute */
530530

531-
PyObject* res = self->tail;
531+
PyObject *res = self->tail;
532532

533533
if (JOIN_GET(res)) {
534534
res = JOIN_OBJ(res);
535535
if (PyList_CheckExact(res)) {
536-
res = list_join(res);
537-
if (!res)
536+
PyObject *tmp = list_join(res);
537+
if (!tmp)
538538
return NULL;
539-
self->tail = res;
539+
self->tail = tmp;
540+
Py_DECREF(res);
541+
res = tmp;
540542
}
541543
}
542544

@@ -2148,6 +2150,12 @@ elementiter_next(ElementIterObject *it)
21482150
continue;
21492151
}
21502152

2153+
if (!PyObject_TypeCheck(extra->children[child_index], &Element_Type)) {
2154+
PyErr_Format(PyExc_AttributeError,
2155+
"'%.100s' object has no attribute 'iter'",
2156+
Py_TYPE(extra->children[child_index])->tp_name);
2157+
return NULL;
2158+
}
21512159
elem = (ElementObject *)extra->children[child_index];
21522160
item->child_index++;
21532161
Py_INCREF(elem);
@@ -2397,40 +2405,51 @@ treebuilder_dealloc(TreeBuilderObject *self)
23972405
/* helpers for handling of arbitrary element-like objects */
23982406

23992407
static int
2400-
treebuilder_set_element_text_or_tail(PyObject *element, PyObject *data,
2408+
treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data,
24012409
PyObject **dest, _Py_Identifier *name)
24022410
{
24032411
if (Element_CheckExact(element)) {
2404-
Py_DECREF(JOIN_OBJ(*dest));
2405-
*dest = JOIN_SET(data, PyList_CheckExact(data));
2412+
PyObject *tmp = JOIN_OBJ(*dest);
2413+
*dest = JOIN_SET(*data, PyList_CheckExact(*data));
2414+
*data = NULL;
2415+
Py_DECREF(tmp);
24062416
return 0;
24072417
}
24082418
else {
2409-
PyObject *joined = list_join(data);
2419+
PyObject *joined = list_join(*data);
24102420
int r;
24112421
if (joined == NULL)
24122422
return -1;
24132423
r = _PyObject_SetAttrId(element, name, joined);
24142424
Py_DECREF(joined);
2415-
return r;
2425+
if (r < 0)
2426+
return -1;
2427+
Py_CLEAR(*data);
2428+
return 0;
24162429
}
24172430
}
24182431

2419-
/* These two functions steal a reference to data */
2420-
static int
2421-
treebuilder_set_element_text(PyObject *element, PyObject *data)
2432+
LOCAL(int)
2433+
treebuilder_flush_data(TreeBuilderObject* self)
24222434
{
2423-
_Py_IDENTIFIER(text);
2424-
return treebuilder_set_element_text_or_tail(
2425-
element, data, &((ElementObject *) element)->text, &PyId_text);
2426-
}
2435+
PyObject *element = self->last;
24272436

2428-
static int
2429-
treebuilder_set_element_tail(PyObject *element, PyObject *data)
2430-
{
2431-
_Py_IDENTIFIER(tail);
2432-
return treebuilder_set_element_text_or_tail(
2433-
element, data, &((ElementObject *) element)->tail, &PyId_tail);
2437+
if (!self->data) {
2438+
return 0;
2439+
}
2440+
2441+
if (self->this == element) {
2442+
_Py_IDENTIFIER(text);
2443+
return treebuilder_set_element_text_or_tail(
2444+
element, &self->data,
2445+
&((ElementObject *) element)->text, &PyId_text);
2446+
}
2447+
else {
2448+
_Py_IDENTIFIER(tail);
2449+
return treebuilder_set_element_text_or_tail(
2450+
element, &self->data,
2451+
&((ElementObject *) element)->tail, &PyId_tail);
2452+
}
24342453
}
24352454

24362455
static int
@@ -2480,16 +2499,8 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag,
24802499
PyObject* this;
24812500
elementtreestate *st = ET_STATE_GLOBAL;
24822501

2483-
if (self->data) {
2484-
if (self->this == self->last) {
2485-
if (treebuilder_set_element_text(self->last, self->data))
2486-
return NULL;
2487-
}
2488-
else {
2489-
if (treebuilder_set_element_tail(self->last, self->data))
2490-
return NULL;
2491-
}
2492-
self->data = NULL;
2502+
if (treebuilder_flush_data(self) < 0) {
2503+
return NULL;
24932504
}
24942505

24952506
if (!self->element_factory || self->element_factory == Py_None) {
@@ -2594,15 +2605,8 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag)
25942605
{
25952606
PyObject* item;
25962607

2597-
if (self->data) {
2598-
if (self->this == self->last) {
2599-
if (treebuilder_set_element_text(self->last, self->data))
2600-
return NULL;
2601-
} else {
2602-
if (treebuilder_set_element_tail(self->last, self->data))
2603-
return NULL;
2604-
}
2605-
self->data = NULL;
2608+
if (treebuilder_flush_data(self) < 0) {
2609+
return NULL;
26062610
}
26072611

26082612
if (self->index == 0) {

0 commit comments

Comments
 (0)