Skip to content

Commit a6b4e19

Browse files
bpo-27863: Fixed multiple crashes in ElementTree. (#765) (#903)
(cherry picked from commit 576def0)
1 parent 1b43a95 commit a6b4e19

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
@@ -27,6 +27,9 @@ Core and Builtins
2727
Library
2828
-------
2929

30+
- bpo-27863: Fixed multiple crashes in ElementTree caused by race conditions
31+
and wrong types.
32+
3033
- bpo-28699: Fixed a bug in pools in multiprocessing.pool that raising an
3134
exception at the very first of an iterable may swallow the exception or
3235
make the program hang. Patch by Davin Potts and Xiang Zhang.

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

@@ -2147,6 +2149,12 @@ elementiter_next(ElementIterObject *it)
21472149
continue;
21482150
}
21492151

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

23982406
static int
2399-
treebuilder_set_element_text_or_tail(PyObject *element, PyObject *data,
2407+
treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data,
24002408
PyObject **dest, _Py_Identifier *name)
24012409
{
24022410
if (Element_CheckExact(element)) {
2403-
Py_DECREF(JOIN_OBJ(*dest));
2404-
*dest = JOIN_SET(data, PyList_CheckExact(data));
2411+
PyObject *tmp = JOIN_OBJ(*dest);
2412+
*dest = JOIN_SET(*data, PyList_CheckExact(*data));
2413+
*data = NULL;
2414+
Py_DECREF(tmp);
24052415
return 0;
24062416
}
24072417
else {
2408-
PyObject *joined = list_join(data);
2418+
PyObject *joined = list_join(*data);
24092419
int r;
24102420
if (joined == NULL)
24112421
return -1;
24122422
r = _PyObject_SetAttrId(element, name, joined);
24132423
Py_DECREF(joined);
2414-
return r;
2424+
if (r < 0)
2425+
return -1;
2426+
Py_CLEAR(*data);
2427+
return 0;
24152428
}
24162429
}
24172430

2418-
/* These two functions steal a reference to data */
2419-
static int
2420-
treebuilder_set_element_text(PyObject *element, PyObject *data)
2431+
LOCAL(int)
2432+
treebuilder_flush_data(TreeBuilderObject* self)
24212433
{
2422-
_Py_IDENTIFIER(text);
2423-
return treebuilder_set_element_text_or_tail(
2424-
element, data, &((ElementObject *) element)->text, &PyId_text);
2425-
}
2434+
PyObject *element = self->last;
24262435

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

24352454
static int
@@ -2479,16 +2498,8 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag,
24792498
PyObject* this;
24802499
elementtreestate *st = ET_STATE_GLOBAL;
24812500

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

24942505
if (!self->element_factory || self->element_factory == Py_None) {
@@ -2591,15 +2602,8 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag)
25912602
{
25922603
PyObject* item;
25932604

2594-
if (self->data) {
2595-
if (self->this == self->last) {
2596-
if (treebuilder_set_element_text(self->last, self->data))
2597-
return NULL;
2598-
} else {
2599-
if (treebuilder_set_element_tail(self->last, self->data))
2600-
return NULL;
2601-
}
2602-
self->data = NULL;
2605+
if (treebuilder_flush_data(self) < 0) {
2606+
return NULL;
26032607
}
26042608

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

0 commit comments

Comments
 (0)