From f3e6bdafd2804c1bf7d4cb31fed3f983fd5936ba Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Fri, 26 Jun 2026 16:15:21 +0200 Subject: [PATCH] [3.15] gh-152052: Fix misleading `json` error for `\uXXXX` escape at the end of input (GH-152053) (#152283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 588be7af08c04d63c1a920539c64885fbc4c6d38) Co-authored-by: tonghuaroot (童话) Co-authored-by: Stan Ulbrych --- Lib/test/test_json/test_fail.py | 7 +++++++ Lib/test/test_json/test_scanstring.py | 3 +++ .../Library/2026-06-24-12-00-00.gh-issue-152052.yBssDE.rst | 2 ++ Modules/_json.c | 2 +- 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-24-12-00-00.gh-issue-152052.yBssDE.rst diff --git a/Lib/test/test_json/test_fail.py b/Lib/test/test_json/test_fail.py index 79c44af2fbf..7f99a6331d3 100644 --- a/Lib/test/test_json/test_fail.py +++ b/Lib/test/test_json/test_fail.py @@ -144,6 +144,13 @@ def test_truncated_input(self): ('"', 'Unterminated string starting at', 0), ('"spam', 'Unterminated string starting at', 0), ] + # A complete \uXXXX escape at end of input leaves it unterminated. + test_cases += [ + (r'"\u0041', 'Unterminated string starting at', 0), + (r'"\ud834', 'Unterminated string starting at', 0), + (r'"\ud834\udd1e', 'Unterminated string starting at', 0), + (r'{"a": "\u0041', 'Unterminated string starting at', 6), + ] for data, msg, idx in test_cases: with self.assertRaises(self.JSONDecodeError) as cm: self.loads(data) diff --git a/Lib/test/test_json/test_scanstring.py b/Lib/test/test_json/test_scanstring.py index 9a6cdfe12d2..a52b01c7113 100644 --- a/Lib/test/test_json/test_scanstring.py +++ b/Lib/test/test_json/test_scanstring.py @@ -137,6 +137,9 @@ def test_bad_escapes(self): '"\\ud834\\u-123"', '"\\ud834\\u+123"', '"\\ud834\\u1_23"', + # Truncated or non-hex \uXXXX escape at end of input. + '"\\u004', + '"\\uXYZW', ] for s in bad_escapes: with self.assertRaises(self.JSONDecodeError, msg=s): diff --git a/Misc/NEWS.d/next/Library/2026-06-24-12-00-00.gh-issue-152052.yBssDE.rst b/Misc/NEWS.d/next/Library/2026-06-24-12-00-00.gh-issue-152052.yBssDE.rst new file mode 100644 index 00000000000..f9ffbbd342f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-24-12-00-00.gh-issue-152052.yBssDE.rst @@ -0,0 +1,2 @@ +The :mod:`json` C accelerator now correctly reports an unterminated string for a +``\uXXXX`` escape at the end of the input. diff --git a/Modules/_json.c b/Modules/_json.c index 6c4f3883463..858702e6f45 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -574,7 +574,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next c = 0; next++; end = next + 4; - if (end >= len) { + if (end > len) { raise_errmsg("Invalid \\uXXXX escape", pystr, next - 1); goto bail; }