Skip to content

Commit e727d41

Browse files
bpo-31499, xml.etree: Fix xmlparser_gc_clear() crash (#3641)
* 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 <[email protected]>
1 parent 9b47af6 commit e727d41

File tree

3 files changed

+26
-1
lines changed

3 files changed

+26
-1
lines changed

Lib/test/test_xml_etree_c.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,26 @@ def test_trashcan(self):
6565
del root
6666
support.gc_collect()
6767

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+
6888

6989
@unittest.skipUnless(cET, 'requires _elementtree')
7090
class TestAliasWorking(unittest.TestCase):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
xml.etree: Fix a crash when a parser is part of a reference cycle.

Modules/_elementtree.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3411,7 +3411,11 @@ xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg)
34113411
static int
34123412
xmlparser_gc_clear(XMLParserObject *self)
34133413
{
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+
}
34153419

34163420
Py_CLEAR(self->handle_close);
34173421
Py_CLEAR(self->handle_pi);

0 commit comments

Comments
 (0)