From c09ccd9c429bdfac85ee427d9d0def32af47492d Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 31 Mar 2026 00:06:52 +0800 Subject: [PATCH] [3.13] gh-146250: Fix memory leak in re-initialization of `SyntaxError` (GH-146251) (#146519) * [3.13] gh-146250: Fix memory leak in re-initialization of `SyntaxError` (GH-146251) (cherry picked from commit 0de4e08a5990e4692feb1b1ea01c303e468a2894) Co-authored-by: Brij Kapadia <97006829+bkap123@users.noreply.github.com> * Minimize the changes * Minimize the changes * Minimize the changes --------- Co-authored-by: Brij Kapadia <97006829+bkap123@users.noreply.github.com> --- Lib/test/test_exceptions.py | 24 +++++++++++++++++++ ...-03-21-11-55-16.gh-issue-146250.ahl3O2.rst | 1 + Objects/exceptions.c | 23 +++++++++--------- 3 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-03-21-11-55-16.gh-issue-146250.ahl3O2.rst diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 939783956d62..04c7e973f5ae 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2436,6 +2436,30 @@ class SyntaxErrorTests(unittest.TestCase): args = ("bad.py", 1, 2, "abcdefg", 1) self.assertRaises(TypeError, SyntaxError, "bad bad", args) + def test_syntax_error_memory_leak(self): + # gh-146250: memory leak with re-initialization of SyntaxError + e = SyntaxError("msg", ("file.py", 1, 2, "txt", 2, 3)) + e.__init__("new_msg", ("new_file.py", 2, 3, "new_txt", 3, 4)) + self.assertEqual(e.msg, "new_msg") + self.assertEqual(e.args, ("new_msg", ("new_file.py", 2, 3, "new_txt", 3, 4))) + self.assertEqual(e.filename, "new_file.py") + self.assertEqual(e.lineno, 2) + self.assertEqual(e.offset, 3) + self.assertEqual(e.text, "new_txt") + self.assertEqual(e.end_lineno, 3) + self.assertEqual(e.end_offset, 4) + + e = SyntaxError("msg", ("file.py", 1, 2, "txt", 2, 3)) + e.__init__("new_msg", ("new_file.py", 2, 3, "new_txt")) + self.assertEqual(e.msg, "new_msg") + self.assertEqual(e.args, ("new_msg", ("new_file.py", 2, 3, "new_txt"))) + self.assertEqual(e.filename, "new_file.py") + self.assertEqual(e.lineno, 2) + self.assertEqual(e.offset, 3) + self.assertEqual(e.text, "new_txt") + self.assertIsNone(e.end_lineno) + self.assertIsNone(e.end_offset) + class TestInvalidExceptionMatcher(unittest.TestCase): def test_except_star_invalid_exception_type(self): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-21-11-55-16.gh-issue-146250.ahl3O2.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-21-11-55-16.gh-issue-146250.ahl3O2.rst new file mode 100644 index 000000000000..fff07b31ec21 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-21-11-55-16.gh-issue-146250.ahl3O2.rst @@ -0,0 +1 @@ +Fixed a memory leak in :exc:`SyntaxError` when re-initializing it. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index db2774d3d6a3..6c1807715c0f 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2466,22 +2466,23 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds) return -1; } - self->end_lineno = NULL; - self->end_offset = NULL; + PyObject *filename, *lineno, *offset, *text; + PyObject *end_lineno = NULL; + PyObject *end_offset = NULL; if (!PyArg_ParseTuple(info, "OOOO|OO", - &self->filename, &self->lineno, - &self->offset, &self->text, - &self->end_lineno, &self->end_offset)) { + &filename, &lineno, + &offset, &text, + &end_lineno, &end_offset)) { Py_DECREF(info); return -1; } - Py_INCREF(self->filename); - Py_INCREF(self->lineno); - Py_INCREF(self->offset); - Py_INCREF(self->text); - Py_XINCREF(self->end_lineno); - Py_XINCREF(self->end_offset); + Py_XSETREF(self->filename, Py_NewRef(filename)); + Py_XSETREF(self->lineno, Py_NewRef(lineno)); + Py_XSETREF(self->offset, Py_NewRef(offset)); + Py_XSETREF(self->text, Py_NewRef(text)); + Py_XSETREF(self->end_lineno, Py_XNewRef(end_lineno)); + Py_XSETREF(self->end_offset, Py_XNewRef(end_offset)); Py_DECREF(info); if (self->end_lineno != NULL && self->end_offset == NULL) { -- 2.47.3