Skip to content

Commit c90ff1b

Browse files
bpo-27863: Fixed multiple crashes in ElementTree. (#765) (#904)
(cherry picked from commit 576def0)
1 parent 0fadf25 commit c90ff1b

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
@@ -1875,6 +1875,118 @@ def test_recursive_repr(self):
18751875
with self.assertRaises(RuntimeError):
18761876
repr(e) # Should not crash
18771877

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

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ Extension Modules
4646
Library
4747
-------
4848

49+
- bpo-27863: Fixed multiple crashes in ElementTree caused by race conditions
50+
and wrong types.
51+
4952
- bpo-28699: Fixed a bug in pools in multiprocessing.pool that raising an
5053
exception at the very first of an iterable may swallow the exception or
5154
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
@@ -155,7 +155,7 @@ deepcopy(PyObject* object, PyObject* memo)
155155
LOCAL(PyObject*)
156156
list_join(PyObject* list)
157157
{
158-
/* join list elements (destroying the list in the process) */
158+
/* join list elements */
159159
PyObject* joiner;
160160
PyObject* result;
161161

@@ -164,8 +164,6 @@ list_join(PyObject* list)
164164
return NULL;
165165
result = PyUnicode_Join(joiner, list);
166166
Py_DECREF(joiner);
167-
if (result)
168-
Py_DECREF(list);
169167
return result;
170168
}
171169

@@ -534,15 +532,17 @@ element_get_text(ElementObject* self)
534532
{
535533
/* return borrowed reference to text attribute */
536534

537-
PyObject* res = self->text;
535+
PyObject *res = self->text;
538536

539537
if (JOIN_GET(res)) {
540538
res = JOIN_OBJ(res);
541539
if (PyList_CheckExact(res)) {
542-
res = list_join(res);
543-
if (!res)
540+
PyObject *tmp = list_join(res);
541+
if (!tmp)
544542
return NULL;
545-
self->text = res;
543+
self->text = tmp;
544+
Py_DECREF(res);
545+
res = tmp;
546546
}
547547
}
548548

@@ -554,15 +554,17 @@ element_get_tail(ElementObject* self)
554554
{
555555
/* return borrowed reference to text attribute */
556556

557-
PyObject* res = self->tail;
557+
PyObject *res = self->tail;
558558

559559
if (JOIN_GET(res)) {
560560
res = JOIN_OBJ(res);
561561
if (PyList_CheckExact(res)) {
562-
res = list_join(res);
563-
if (!res)
562+
PyObject *tmp = list_join(res);
563+
if (!tmp)
564564
return NULL;
565-
self->tail = res;
565+
self->tail = tmp;
566+
Py_DECREF(res);
567+
res = tmp;
566568
}
567569
}
568570

@@ -2147,6 +2149,12 @@ elementiter_next(ElementIterObject *it)
21472149
cur_parent = it->parent_stack->parent;
21482150
child_index = it->parent_stack->child_index;
21492151
if (cur_parent->extra && child_index < cur_parent->extra->length) {
2152+
if (!PyObject_TypeCheck(cur_parent->extra->children[child_index], &Element_Type)) {
2153+
PyErr_Format(PyExc_AttributeError,
2154+
"'%.100s' object has no attribute 'iter'",
2155+
Py_TYPE(cur_parent->extra->children[child_index])->tp_name);
2156+
return NULL;
2157+
}
21502158
elem = (ElementObject *)cur_parent->extra->children[child_index];
21512159
it->parent_stack->child_index++;
21522160
it->parent_stack = parent_stack_push_new(it->parent_stack,
@@ -2435,40 +2443,51 @@ treebuilder_dealloc(TreeBuilderObject *self)
24352443
/* helpers for handling of arbitrary element-like objects */
24362444

24372445
static int
2438-
treebuilder_set_element_text_or_tail(PyObject *element, PyObject *data,
2446+
treebuilder_set_element_text_or_tail(PyObject *element, PyObject **data,
24392447
PyObject **dest, _Py_Identifier *name)
24402448
{
24412449
if (Element_CheckExact(element)) {
2442-
Py_DECREF(JOIN_OBJ(*dest));
2443-
*dest = JOIN_SET(data, PyList_CheckExact(data));
2450+
PyObject *tmp = JOIN_OBJ(*dest);
2451+
*dest = JOIN_SET(*data, PyList_CheckExact(*data));
2452+
*data = NULL;
2453+
Py_DECREF(tmp);
24442454
return 0;
24452455
}
24462456
else {
2447-
PyObject *joined = list_join(data);
2457+
PyObject *joined = list_join(*data);
24482458
int r;
24492459
if (joined == NULL)
24502460
return -1;
24512461
r = _PyObject_SetAttrId(element, name, joined);
24522462
Py_DECREF(joined);
2453-
return r;
2463+
if (r < 0)
2464+
return -1;
2465+
Py_CLEAR(*data);
2466+
return 0;
24542467
}
24552468
}
24562469

2457-
/* These two functions steal a reference to data */
2458-
static int
2459-
treebuilder_set_element_text(PyObject *element, PyObject *data)
2470+
LOCAL(int)
2471+
treebuilder_flush_data(TreeBuilderObject* self)
24602472
{
2461-
_Py_IDENTIFIER(text);
2462-
return treebuilder_set_element_text_or_tail(
2463-
element, data, &((ElementObject *) element)->text, &PyId_text);
2464-
}
2473+
PyObject *element = self->last;
24652474

2466-
static int
2467-
treebuilder_set_element_tail(PyObject *element, PyObject *data)
2468-
{
2469-
_Py_IDENTIFIER(tail);
2470-
return treebuilder_set_element_text_or_tail(
2471-
element, data, &((ElementObject *) element)->tail, &PyId_tail);
2475+
if (!self->data) {
2476+
return 0;
2477+
}
2478+
2479+
if (self->this == element) {
2480+
_Py_IDENTIFIER(text);
2481+
return treebuilder_set_element_text_or_tail(
2482+
element, &self->data,
2483+
&((ElementObject *) element)->text, &PyId_text);
2484+
}
2485+
else {
2486+
_Py_IDENTIFIER(tail);
2487+
return treebuilder_set_element_text_or_tail(
2488+
element, &self->data,
2489+
&((ElementObject *) element)->tail, &PyId_tail);
2490+
}
24722491
}
24732492

24742493
static int
@@ -2517,16 +2536,8 @@ treebuilder_handle_start(TreeBuilderObject* self, PyObject* tag,
25172536
PyObject* this;
25182537
elementtreestate *st = ET_STATE_GLOBAL;
25192538

2520-
if (self->data) {
2521-
if (self->this == self->last) {
2522-
if (treebuilder_set_element_text(self->last, self->data))
2523-
return NULL;
2524-
}
2525-
else {
2526-
if (treebuilder_set_element_tail(self->last, self->data))
2527-
return NULL;
2528-
}
2529-
self->data = NULL;
2539+
if (treebuilder_flush_data(self) < 0) {
2540+
return NULL;
25302541
}
25312542

25322543
if (self->element_factory && self->element_factory != Py_None) {
@@ -2622,15 +2633,8 @@ treebuilder_handle_end(TreeBuilderObject* self, PyObject* tag)
26222633
{
26232634
PyObject* item;
26242635

2625-
if (self->data) {
2626-
if (self->this == self->last) {
2627-
if (treebuilder_set_element_text(self->last, self->data))
2628-
return NULL;
2629-
} else {
2630-
if (treebuilder_set_element_tail(self->last, self->data))
2631-
return NULL;
2632-
}
2633-
self->data = NULL;
2636+
if (treebuilder_flush_data(self) < 0) {
2637+
return NULL;
26342638
}
26352639

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

0 commit comments

Comments
 (0)