[Python-checkins] bpo-31544: Avoid calling "PyObject_GetAttrString()" (and potentially executing user code) with a live exception set. (GH-3992)

Serhiy Storchaka webhook-mailer at python.org
Sat Mar 24 01:56:45 EDT 2018


https://github.com/python/cpython/commit/0694b6a651ba2a53f6323ffb3b23358f43885815
commit: 0694b6a651ba2a53f6323ffb3b23358f43885815
branch: 2.7
author: scoder <stefan_ml at behnel.de>
committer: Serhiy Storchaka <storchaka at gmail.com>
date: 2018-03-24T07:56:41+02:00
summary:

bpo-31544: Avoid calling "PyObject_GetAttrString()" (and potentially executing user code) with a live exception set. (GH-3992)

files:
A Misc/NEWS.d/next/Library/2017-09-13-19-55-35.bpo-31544.beTh6t.rst
M Lib/test/test_xml_etree.py
M Modules/_elementtree.c

diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index e466867b7cd1..c75d55f05c17 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -136,6 +136,13 @@ def wrapper(*args):
         return test(*args)
     return wrapper
 
+def cet_only(test):
+    def wrapper(*args):
+        if ET is pyET:
+            raise unittest.SkipTest('only for the C version')
+        return test(*args)
+    return wrapper
+
 # --------------------------------------------------------------------
 # element tree tests
 
@@ -2229,6 +2236,32 @@ def close(self):
             ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
              'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
 
+    @cet_only  # PyET does not look up the attributes in XMLParser().__init__()
+    def test_builder_lookup_errors(self):
+        class RaisingBuilder(object):
+            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.assertRaises(ValueError):
+                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'<file><line>22</line></file>'
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 000000000000..9ea3599ee0b0
--- /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 574559c6313b..7f0e60934003 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -2510,6 +2510,18 @@ expat_unknown_encoding_handler(XMLParserObject *self, const XML_Char *name,
 /* -------------------------------------------------------------------- */
 /* constructor and destructor */
 
+static int
+ignore_attribute_error(PyObject *value)
+{
+    if (value == NULL) {
+        if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+            return -1;
+        }
+        PyErr_Clear();
+    }
+    return 0;
+}
+
 static PyObject*
 xmlparser(PyObject* self_, PyObject* args, PyObject* kw)
 {
@@ -2578,14 +2590,33 @@ xmlparser(PyObject* self_, PyObject* args, PyObject* kw)
     self->target = target;
 
     self->handle_xml = PyObject_GetAttrString(target, "xml");
+    if (ignore_attribute_error(self->handle_xml)) {
+        return NULL;
+    }
     self->handle_start = PyObject_GetAttrString(target, "start");
+    if (ignore_attribute_error(self->handle_start)) {
+        return NULL;
+    }
     self->handle_data = PyObject_GetAttrString(target, "data");
+    if (ignore_attribute_error(self->handle_data)) {
+        return NULL;
+    }
     self->handle_end = PyObject_GetAttrString(target, "end");
+    if (ignore_attribute_error(self->handle_end)) {
+        return NULL;
+    }
     self->handle_comment = PyObject_GetAttrString(target, "comment");
+    if (ignore_attribute_error(self->handle_comment)) {
+        return NULL;
+    }
     self->handle_pi = PyObject_GetAttrString(target, "pi");
+    if (ignore_attribute_error(self->handle_pi)) {
+        return NULL;
+    }
     self->handle_close = PyObject_GetAttrString(target, "close");
-
-    PyErr_Clear();
+    if (ignore_attribute_error(self->handle_close)) {
+        return NULL;
+    }
 
     /* configure parser */
     EXPAT(SetUserData)(self->parser, self);



More information about the Python-checkins mailing list