]> granicus.if.org Git - python/commitdiff
bpo-31499, xml.etree: Fix xmlparser_gc_clear() crash (#3641)
authorVictor Stinner <victor.stinner@gmail.com>
Mon, 18 Sep 2017 12:29:37 +0000 (05:29 -0700)
committerGitHub <noreply@github.com>
Mon, 18 Sep 2017 12:29:37 +0000 (05:29 -0700)
* bpo-31499, xml.etree: Fix xmlparser_gc_clear() crash

xml.etree: xmlparser_gc_clear() now sets self.parser to NULL to prevent a
crash in xmlparser_dealloc() if xmlparser_gc_clear() was called previously
by the garbage collector, because the parser was part of a reference cycle.

Co-Authored-By: Serhiy Storchaka <storchaka@gmail.com>
Lib/test/test_xml_etree_c.py
Misc/NEWS.d/next/Library/2017-09-18-10-57-04.bpo-31499.BydYhf.rst [new file with mode: 0644]
Modules/_elementtree.c

index 171a3f88b9a1f0e1f3ce88e179d3cdb88d5257a4..25517a7269c9c0123886040f75fa60fb2a03b9e1 100644 (file)
@@ -65,6 +65,26 @@ class MiscTests(unittest.TestCase):
         del root
         support.gc_collect()
 
+    def test_parser_ref_cycle(self):
+        # bpo-31499: xmlparser_dealloc() crashed with a segmentation fault when
+        # xmlparser_gc_clear() was called previously by the garbage collector,
+        # when the parser was part of a reference cycle.
+
+        def parser_ref_cycle():
+            parser = cET.XMLParser()
+            # Create a reference cycle using an exception to keep the frame
+            # alive, so the parser will be destroyed by the garbage collector
+            try:
+                raise ValueError
+            except ValueError as exc:
+                err = exc
+
+        # Create a parser part of reference cycle
+        parser_ref_cycle()
+        # Trigger an explicit garbage collection to break the reference cycle
+        # and so destroy the parser
+        support.gc_collect()
+
 
 @unittest.skipUnless(cET, 'requires _elementtree')
 class TestAliasWorking(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2017-09-18-10-57-04.bpo-31499.BydYhf.rst b/Misc/NEWS.d/next/Library/2017-09-18-10-57-04.bpo-31499.BydYhf.rst
new file mode 100644 (file)
index 0000000..22af29f
--- /dev/null
@@ -0,0 +1 @@
+xml.etree: Fix a crash when a parser is part of a reference cycle.
index 28d0181594b846e2fba648c60abbfda47b004387..bddac851d9c708e1b0a5b2dec3237c88009b99d9 100644 (file)
@@ -3411,7 +3411,11 @@ xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg)
 static int
 xmlparser_gc_clear(XMLParserObject *self)
 {
-    EXPAT(ParserFree)(self->parser);
+    if (self->parser != NULL) {
+        XML_Parser parser = self->parser;
+        self->parser = NULL;
+        EXPAT(ParserFree)(parser);
+    }
 
     Py_CLEAR(self->handle_close);
     Py_CLEAR(self->handle_pi);