From c66e5f01d3880c9a242d0b7ac9eb25844e63b8e0 Mon Sep 17 00:00:00 2001 From: Brij Kapadia <97006829+bkap123@users.noreply.github.com> Date: Fri, 27 Mar 2026 09:59:21 -0400 Subject: [PATCH 1/4] [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> --- Lib/test/test_exceptions.py | 24 +++++++++++++++++++ ...-03-21-11-55-16.gh-issue-146250.ahl3O2.rst | 1 + Objects/exceptions.c | 24 +++++++++---------- 3 files changed, 37 insertions(+), 12 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 939783956d62d6..04c7e973f5ae70 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2436,6 +2436,30 @@ def test_incorrect_constructor(self): 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 00000000000000..fff07b31ec21c4 --- /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 db2774d3d6a3e8..5d74c281bc4484 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) { @@ -3932,4 +3933,3 @@ _PyException_AddNote(PyObject *exc, PyObject *note) Py_XDECREF(r); return res; } - From 5f4a0635cafc83aa29894deb9d6196529b4214a7 Mon Sep 17 00:00:00 2001 From: AN Long Date: Fri, 27 Mar 2026 22:30:54 +0800 Subject: [PATCH 2/4] Minimize the changes --- Objects/exceptions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 5d74c281bc4484..110b699ee74e36 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -3932,4 +3932,4 @@ _PyException_AddNote(PyObject *exc, PyObject *note) int res = r == NULL ? -1 : 0; Py_XDECREF(r); return res; -} +} \ No newline at end of file From 92a4fb4b42a6ef4bd0bd26f459b5db9cc7740bf7 Mon Sep 17 00:00:00 2001 From: AN Long Date: Fri, 27 Mar 2026 22:33:17 +0800 Subject: [PATCH 3/4] Minimize the changes --- Objects/exceptions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 110b699ee74e36..5d74c281bc4484 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -3932,4 +3932,4 @@ _PyException_AddNote(PyObject *exc, PyObject *note) int res = r == NULL ? -1 : 0; Py_XDECREF(r); return res; -} \ No newline at end of file +} From 70e214443fa66c6654877443e75e188e8dc7d739 Mon Sep 17 00:00:00 2001 From: AN Long Date: Fri, 27 Mar 2026 23:11:48 +0800 Subject: [PATCH 4/4] Minimize the changes --- Objects/exceptions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 5d74c281bc4484..6c1807715c0f14 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -3933,3 +3933,4 @@ _PyException_AddNote(PyObject *exc, PyObject *note) Py_XDECREF(r); return res; } +