bpo-31455: Fix an assertion failure in ElementTree.XMLParser(). (#3545)

* Avoid calling "PyObject_GetAttrString()" (and potentially executing user code) with a live exception set.

* Ignore only AttributeError on attribute lookups in ElementTree.XMLParser() and propagate all other exceptions.
This commit is contained in:
scoder 2017-09-14 22:00:03 +02:00 committed by Serhiy Storchaka
parent 0b3a87ef54
commit c8d8e15bfc
3 changed files with 60 additions and 2 deletions

View File

@ -2498,6 +2498,31 @@ class TreeBuilderTest(unittest.TestCase):
('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
def test_builder_lookup_errors(self):
class RaisingBuilder:
def __init__(self, raise_in=None, what=ValueError):
self.raise_in = raise_in
self.what = what
def __getattr__(self, name):
if name == self.raise_in:
raise self.what(self.raise_in)
def handle(*args):
pass
return handle
ET.XMLParser(target=RaisingBuilder())
# cET also checks for 'close' and 'doctype', PyET does it only at need
for event in ('start', 'data', 'end', 'comment', 'pi'):
with self.assertRaisesRegex(ValueError, event):
ET.XMLParser(target=RaisingBuilder(event))
ET.XMLParser(target=RaisingBuilder(what=AttributeError))
for event in ('start', 'data', 'end', 'comment', 'pi'):
parser = ET.XMLParser(target=RaisingBuilder(event, what=AttributeError))
parser.feed(self.sample1)
self.assertIsNone(parser.close())
class XMLParserTest(unittest.TestCase): class XMLParserTest(unittest.TestCase):
sample1 = b'<file><line>22</line></file>' sample1 = b'<file><line>22</line></file>'

View File

@ -0,0 +1,2 @@
The C accelerator module of ElementTree ignored exceptions raised when
looking up TreeBuilder target methods in XMLParser().

View File

@ -3260,6 +3260,18 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self; return (PyObject *)self;
} }
static int
ignore_attribute_error(PyObject *value)
{
if (value == NULL) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
return -1;
}
PyErr_Clear();
}
return 0;
}
/*[clinic input] /*[clinic input]
_elementtree.XMLParser.__init__ _elementtree.XMLParser.__init__
@ -3314,14 +3326,33 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
self->target = target; self->target = target;
self->handle_start = PyObject_GetAttrString(target, "start"); self->handle_start = PyObject_GetAttrString(target, "start");
if (ignore_attribute_error(self->handle_start)) {
return -1;
}
self->handle_data = PyObject_GetAttrString(target, "data"); self->handle_data = PyObject_GetAttrString(target, "data");
if (ignore_attribute_error(self->handle_data)) {
return -1;
}
self->handle_end = PyObject_GetAttrString(target, "end"); self->handle_end = PyObject_GetAttrString(target, "end");
if (ignore_attribute_error(self->handle_end)) {
return -1;
}
self->handle_comment = PyObject_GetAttrString(target, "comment"); self->handle_comment = PyObject_GetAttrString(target, "comment");
if (ignore_attribute_error(self->handle_comment)) {
return -1;
}
self->handle_pi = PyObject_GetAttrString(target, "pi"); self->handle_pi = PyObject_GetAttrString(target, "pi");
if (ignore_attribute_error(self->handle_pi)) {
return -1;
}
self->handle_close = PyObject_GetAttrString(target, "close"); self->handle_close = PyObject_GetAttrString(target, "close");
if (ignore_attribute_error(self->handle_close)) {
return -1;
}
self->handle_doctype = PyObject_GetAttrString(target, "doctype"); self->handle_doctype = PyObject_GetAttrString(target, "doctype");
if (ignore_attribute_error(self->handle_doctype)) {
PyErr_Clear(); return -1;
}
/* configure parser */ /* configure parser */
EXPAT(SetUserData)(self->parser, self); EXPAT(SetUserData)(self->parser, self);