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); |