From a55e2f18fd588d94a193f1a304176f365ce9ec97 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sun, 26 Oct 2025 14:45:32 +0100 Subject: [PATCH 1/2] [3.14] 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 e34a5e33049ce845de646cf24a498766a2da3586) Co-authored-by: Sebastian Pipping --- Lib/test/test_pyexpat.py | 17 +++++++++++++++++ ...25-10-25-21-26-16.gh-issue-140593.OxlLc9.rst | 3 +++ Modules/pyexpat.c | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-10-25-21-26-16.gh-issue-140593.OxlLc9.rst diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index a0914304638a96..154a5ee8864493 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -668,6 +668,23 @@ def test_change_size_2(self): parser.Parse(xml2, True) 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('''\ + + ]> + + ''').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): def test1(self): xml = b"\0\r\n" diff --git a/Misc/NEWS.d/next/Library/2025-10-25-21-26-16.gh-issue-140593.OxlLc9.rst b/Misc/NEWS.d/next/Library/2025-10-25-21-26-16.gh-issue-140593.OxlLc9.rst new file mode 100644 index 00000000000000..612ad82dc64309 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-25-21-26-16.gh-issue-140593.OxlLc9.rst @@ -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. diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 502ebcdc56eb37..d55ad3cdeef2a8 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -597,7 +597,7 @@ my_ElementDeclHandler(void *userData, PyObject *modelobj, *nameobj; if (PyErr_Occurred()) - return; + goto finally; if (flush_character_buffer(self) < 0) goto finally; From e8e7c47873017b1afbfcd9fab86d7d5d60bf5162 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sun, 26 Oct 2025 15:49:03 +0100 Subject: [PATCH 2/2] Add missing import --- Lib/test/test_pyexpat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index 154a5ee8864493..5c8189b33c3285 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -4,6 +4,7 @@ import os import sys import sysconfig +import textwrap import unittest import traceback from io import BytesIO