Skip to content

Commit 8afd7ab

Browse files
miss-islingtonserhiy-storchaka
authored andcommitted
[3.6] bpo-31499, xml.etree: Fix xmlparser_gc_clear() crash (GH-3641) (#3645)
* 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]> (cherry picked from commit e727d41)
1 parent 84c89ef commit 8afd7ab

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
@@ -64,6 +64,26 @@ def test_trashcan(self):
6464
del root
6565
support.gc_collect()
6666

67+
def test_parser_ref_cycle(self):
68+
# bpo-31499: xmlparser_dealloc() crashed with a segmentation fault when
69+
# xmlparser_gc_clear() was called previously by the garbage collector,
70+
# when the parser was part of a reference cycle.
71+
72+
def parser_ref_cycle():
73+
parser = cET.XMLParser()
74+
# Create a reference cycle using an exception to keep the frame
75+
# alive, so the parser will be destroyed by the garbage collector
76+
try:
77+
raise ValueError
78+
except ValueError as exc:
79+
err = exc
80+
81+
# Create a parser part of reference cycle
82+
parser_ref_cycle()
83+
# Trigger an explicit garbage collection to break the reference cycle
84+
# and so destroy the parser
85+
support.gc_collect()
86+
6787

6888
@unittest.skipUnless(cET, 'requires _elementtree')
6989
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
@@ -3367,7 +3367,11 @@ xmlparser_gc_traverse(XMLParserObject *self, visitproc visit, void *arg)
33673367
static int
33683368
xmlparser_gc_clear(XMLParserObject *self)
33693369
{
3370-
EXPAT(ParserFree)(self->parser);
3370+
if (self->parser != NULL) {
3371+
XML_Parser parser = self->parser;
3372+
self->parser = NULL;
3373+
EXPAT(ParserFree)(parser);
3374+
}
33713375

33723376
Py_CLEAR(self->handle_close);
33733377
Py_CLEAR(self->handle_pi);

0 commit comments

Comments
 (0)