Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Lib/test/test_xml_etree_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ def test_trashcan(self):
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):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
xml.etree: Fix a crash when a parser is part of a reference cycle.
6 changes: 5 additions & 1 deletion Modules/_elementtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down