diff options
-rw-r--r-- | Lib/test/test_xml_etree_c.py | 20 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2017-09-18-10-57-04.bpo-31499.BydYhf.rst | 1 | ||||
-rw-r--r-- | Modules/_elementtree.c | 6 |
3 files changed, 26 insertions, 1 deletions
diff --git a/Lib/test/test_xml_etree_c.py b/Lib/test/test_xml_etree_c.py index 171a3f8..25517a7 100644 --- a/Lib/test/test_xml_etree_c.py +++ b/Lib/test/test_xml_etree_c.py @@ -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 index 0000000..22af29f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-18-10-57-04.bpo-31499.BydYhf.rst @@ -0,0 +1 @@ +xml.etree: Fix a crash when a parser is part of a reference cycle. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 28d0181..bddac85 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -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); |