From 9c29e17d1cd32537d7e5aad27722d9a5edea0128 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 6 Jul 2026 13:44:41 +0200 Subject: [PATCH 1/4] Fix UB in `PyBytes_DecodeEscape` --- Objects/bytesobject.c | 3 +++ Tools/ubsan/suppressions.txt | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index f63185e14284b1a..b38fb6f4ad667ae 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1188,6 +1188,9 @@ PyObject *_PyBytes_DecodeEscape2(const char *s, *first_invalid_escape_char = -1; *first_invalid_escape_ptr = NULL; + if (len == 0) { + return PyBytesWriter_FinishWithPointer(writer, p); + } const char *end = s + len; while (s < end) { if (*s != '\\') { diff --git a/Tools/ubsan/suppressions.txt b/Tools/ubsan/suppressions.txt index a00e256b3336182..e3679bc421f4205 100644 --- a/Tools/ubsan/suppressions.txt +++ b/Tools/ubsan/suppressions.txt @@ -20,6 +20,3 @@ pointer-overflow:Modules/_zstd/decompressor.c # Modules/_io/stringio.c:350:24: runtime error: addition of unsigned offset to 0x7fd01ec25850 overflowed to 0x7fd01ec2584c pointer-overflow:Modules/_io/stringio.c - -# Objects/bytesobject.c:1190:25: runtime error: applying zero offset to null pointer -pointer-overflow:Objects/bytesobject.c From e07bc2686bd6a0d9b3d1201c0b6dfaedb395d601 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 6 Jul 2026 14:05:27 +0200 Subject: [PATCH 2/4] Revert "Fix UB in `PyBytes_DecodeEscape`" This reverts commit 9c29e17d1cd32537d7e5aad27722d9a5edea0128. --- Objects/bytesobject.c | 3 --- Tools/ubsan/suppressions.txt | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index b38fb6f4ad667ae..f63185e14284b1a 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -1188,9 +1188,6 @@ PyObject *_PyBytes_DecodeEscape2(const char *s, *first_invalid_escape_char = -1; *first_invalid_escape_ptr = NULL; - if (len == 0) { - return PyBytesWriter_FinishWithPointer(writer, p); - } const char *end = s + len; while (s < end) { if (*s != '\\') { diff --git a/Tools/ubsan/suppressions.txt b/Tools/ubsan/suppressions.txt index e3679bc421f4205..a00e256b3336182 100644 --- a/Tools/ubsan/suppressions.txt +++ b/Tools/ubsan/suppressions.txt @@ -20,3 +20,6 @@ pointer-overflow:Modules/_zstd/decompressor.c # Modules/_io/stringio.c:350:24: runtime error: addition of unsigned offset to 0x7fd01ec25850 overflowed to 0x7fd01ec2584c pointer-overflow:Modules/_io/stringio.c + +# Objects/bytesobject.c:1190:25: runtime error: applying zero offset to null pointer +pointer-overflow:Objects/bytesobject.c From 13bdd5e6d31e6b6af3d53abcff7af8b2fffb8f01 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 6 Jul 2026 14:09:05 +0200 Subject: [PATCH 3/4] Remove invalid test instead --- Lib/test/test_capi/test_bytes.py | 1 + Tools/ubsan/suppressions.txt | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_bytes.py b/Lib/test/test_capi/test_bytes.py index 410ebab729c2cf4..b66eff2049bcb70 100644 --- a/Lib/test/test_capi/test_bytes.py +++ b/Lib/test/test_capi/test_bytes.py @@ -226,6 +226,7 @@ def test_decodeescape(self): self.assertRaises(OverflowError, decodeescape, b'abc', NULL, PY_SSIZE_T_MAX) self.assertRaises(OverflowError, decodeescape, NULL, NULL, PY_SSIZE_T_MAX) + # INVALID decodeescape(NULL) # CRASHES decodeescape(b'abc', NULL, -1) # CRASHES decodeescape(NULL, NULL, 1) diff --git a/Tools/ubsan/suppressions.txt b/Tools/ubsan/suppressions.txt index a00e256b3336182..e3679bc421f4205 100644 --- a/Tools/ubsan/suppressions.txt +++ b/Tools/ubsan/suppressions.txt @@ -20,6 +20,3 @@ pointer-overflow:Modules/_zstd/decompressor.c # Modules/_io/stringio.c:350:24: runtime error: addition of unsigned offset to 0x7fd01ec25850 overflowed to 0x7fd01ec2584c pointer-overflow:Modules/_io/stringio.c - -# Objects/bytesobject.c:1190:25: runtime error: applying zero offset to null pointer -pointer-overflow:Objects/bytesobject.c From 84dfc4347a80d6543a41095fec440cfaff62b6af Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 6 Jul 2026 14:09:59 +0200 Subject: [PATCH 4/4] Actually removing it would be helpful --- Lib/test/test_capi/test_bytes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_capi/test_bytes.py b/Lib/test/test_capi/test_bytes.py index b66eff2049bcb70..c591e986bbce35f 100644 --- a/Lib/test/test_capi/test_bytes.py +++ b/Lib/test/test_capi/test_bytes.py @@ -222,7 +222,6 @@ def test_decodeescape(self): self.assertEqual(decodeescape(br'x\xa\xy', 'replace'), b'x??y') self.assertEqual(decodeescape(br'x\xa\xy', 'ignore'), b'xy') self.assertRaises(ValueError, decodeescape, b'\\', 'spam') - self.assertEqual(decodeescape(NULL), b'') self.assertRaises(OverflowError, decodeescape, b'abc', NULL, PY_SSIZE_T_MAX) self.assertRaises(OverflowError, decodeescape, NULL, NULL, PY_SSIZE_T_MAX)