From c8d8e15bfc24abeeaaf3d8be9073276b0c011cdf Mon Sep 17 00:00:00 2001 From: scoder Date: Thu, 14 Sep 2017 22:00:03 +0200 Subject: [PATCH] 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. --- Lib/test/test_xml_etree.py | 25 +++++++++++++ .../2017-09-13-19-55-35.bpo-31544.beTh6t.rst | 2 ++ Modules/_elementtree.c | 35 +++++++++++++++++-- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-09-13-19-55-35.bpo-31544.beTh6t.rst diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 233b4da58c3..baa4e1f5342 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -2498,6 +2498,31 @@ class TreeBuilderTest(unittest.TestCase): ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', '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): sample1 = b'22' diff --git a/Misc/NEWS.d/next/Library/2017-09-13-19-55-35.bpo-31544.beTh6t.rst b/Misc/NEWS.d/next/Library/2017-09-13-19-55-35.bpo-31544.beTh6t.rst new file mode 100644 index 00000000000..9ea3599ee0b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-13-19-55-35.bpo-31544.beTh6t.rst @@ -0,0 +1,2 @@ +The C accelerator module of ElementTree ignored exceptions raised when +looking up TreeBuilder target methods in XMLParser(). diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 98d5e7f5062..28d0181594b 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -3260,6 +3260,18 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 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] _elementtree.XMLParser.__init__ @@ -3314,14 +3326,33 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html, self->target = target; self->handle_start = PyObject_GetAttrString(target, "start"); + if (ignore_attribute_error(self->handle_start)) { + return -1; + } self->handle_data = PyObject_GetAttrString(target, "data"); + if (ignore_attribute_error(self->handle_data)) { + return -1; + } self->handle_end = PyObject_GetAttrString(target, "end"); + if (ignore_attribute_error(self->handle_end)) { + return -1; + } self->handle_comment = PyObject_GetAttrString(target, "comment"); + if (ignore_attribute_error(self->handle_comment)) { + return -1; + } self->handle_pi = PyObject_GetAttrString(target, "pi"); + if (ignore_attribute_error(self->handle_pi)) { + return -1; + } self->handle_close = PyObject_GetAttrString(target, "close"); + if (ignore_attribute_error(self->handle_close)) { + return -1; + } self->handle_doctype = PyObject_GetAttrString(target, "doctype"); - - PyErr_Clear(); + if (ignore_attribute_error(self->handle_doctype)) { + return -1; + } /* configure parser */ EXPAT(SetUserData)(self->parser, self);