[3.13] gh-140593: Fix a memory leak in function my_ElementDeclHandler of pyexpat (GH-140602) (#140630)

[3.13] gh-140593: Fix a memory leak in function `my_ElementDeclHandler` of `pyexpat` (GH-140602)

Ensure that the memory allocated for the content model
passed to `my_ElementDeclHandler` is freed in all error
paths.

(cherry picked from commit e34a5e3304)
This commit is contained in:
Sebastian Pipping 2025-10-26 16:22:20 +01:00 committed by GitHub
parent d64b05b284
commit 7abbf51839
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 1 deletions

View file

@ -5,6 +5,7 @@
import sys import sys
import sysconfig import sysconfig
import unittest import unittest
import textwrap
import traceback import traceback
from io import BytesIO from io import BytesIO
from test import support from test import support
@ -668,6 +669,23 @@ def test_change_size_2(self):
parser.Parse(xml2, True) parser.Parse(xml2, True)
self.assertEqual(self.n, 4) self.assertEqual(self.n, 4)
class ElementDeclHandlerTest(unittest.TestCase):
def test_trigger_leak(self):
# Unfixed, this test would leak the memory of the so-called
# "content model" in function ``my_ElementDeclHandler`` of pyexpat.
# See https://github.com/python/cpython/issues/140593.
data = textwrap.dedent('''\
<!DOCTYPE quotations SYSTEM "quotations.dtd" [
<!ELEMENT root ANY>
]>
<root/>
''').encode('UTF-8')
parser = expat.ParserCreate()
parser.NotStandaloneHandler = lambda: 1.234 # arbitrary float
parser.ElementDeclHandler = lambda _1, _2: None
self.assertRaises(TypeError, parser.Parse, data, True)
class MalformedInputTest(unittest.TestCase): class MalformedInputTest(unittest.TestCase):
def test1(self): def test1(self):
xml = b"\0\r\n" xml = b"\0\r\n"

View file

@ -0,0 +1,3 @@
:mod:`xml.parsers.expat`: Fix a memory leak that could affect users with
:meth:`~xml.parsers.expat.xmlparser.ElementDeclHandler` set to a custom
element declaration handler. Patch by Sebastian Pipping.

View file

@ -566,7 +566,7 @@ my_ElementDeclHandler(void *userData,
PyObject *modelobj, *nameobj; PyObject *modelobj, *nameobj;
if (PyErr_Occurred()) if (PyErr_Occurred())
return; goto finally;
if (flush_character_buffer(self) < 0) if (flush_character_buffer(self) < 0)
goto finally; goto finally;