bpo-31499, xml.etree: Fix xmlparser_gc_clear() crash (#3641) · python/cpython@e727d41 (original) (raw)

3 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -65,6 +65,26 @@ def test_trashcan(self):
65 65 del root
66 66 support.gc_collect()
67 67
68 +def test_parser_ref_cycle(self):
69 +# bpo-31499: xmlparser_dealloc() crashed with a segmentation fault when
70 +# xmlparser_gc_clear() was called previously by the garbage collector,
71 +# when the parser was part of a reference cycle.
72 +
73 +def parser_ref_cycle():
74 +parser = cET.XMLParser()
75 +# Create a reference cycle using an exception to keep the frame
76 +# alive, so the parser will be destroyed by the garbage collector
77 +try:
78 +raise ValueError
79 +except ValueError as exc:
80 +err = exc
81 +
82 +# Create a parser part of reference cycle
83 +parser_ref_cycle()
84 +# Trigger an explicit garbage collection to break the reference cycle
85 +# and so destroy the parser
86 +support.gc_collect()
87 +
68 88
69 89 @unittest.skipUnless(cET, 'requires _elementtree')
70 90 class TestAliasWorking(unittest.TestCase):
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 +xml.etree: Fix a crash when a parser is part of a reference cycle.
Original file line number Diff line number Diff line change
@@ -3411,7 +3411,11 @@ xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg)
3411 3411 static int
3412 3412 xmlparser_gc_clear(XMLParserObject *self)
3413 3413 {
3414 -EXPAT(ParserFree)(self->parser);
3414 +if (self->parser != NULL) {
3415 +XML_Parser parser = self->parser;
3416 +self->parser = NULL;
3417 +EXPAT(ParserFree)(parser);
3418 + }
3415 3419
3416 3420 Py_CLEAR(self->handle_close);
3417 3421 Py_CLEAR(self->handle_pi);