https://github.com/python/cpython/commit/f40043e0953323675843a3c275511596f30c80e9
commit: f40043e0953323675843a3c275511596f30c80e9
branch: main
author: tonghuaroot (童话) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-17T16:02:53+03:00
summary:
gh-153603: Fix out-of-bounds read in the ISO-2022 decoder for an unknown
charset (GH-153604)
The designation-table scan compiled its terminator only under Py_DEBUG, so a
release build walked off the table for an unknown charset set via setstate().
Make the terminator unconditional and report the byte as undecodable.
files:
A Misc/NEWS.d/next/Library/2026-07-11-13-24-49.gh-issue-153603.YbKlry.rst
M Lib/test/test_multibytecodec.py
M Modules/cjkcodecs/_codecs_iso2022.c
diff --git a/Lib/test/test_multibytecodec.py b/Lib/test/test_multibytecodec.py
index d7a233377bdb023..6b032fc8604eefd 100644
--- a/Lib/test/test_multibytecodec.py
+++ b/Lib/test/test_multibytecodec.py
@@ -306,6 +306,24 @@ def test_setstate_validates_input(self):
self.assertRaises(TypeError, decoder.setstate, (b"1234", "invalid"))
self.assertRaises(UnicodeDecodeError, decoder.setstate, (b"123456789",
0))
+ def test_setstate_invalid_designation(self):
+ # gh-153603: an unknown charset designation in the state must not crash
+ # the decoder. 0xff is not a registered charset mark and 0x21 ('!') is
+ # a GL byte that triggers the designation lookup.
+ for name in ('iso-2022-jp', 'iso-2022-kr'):
+ with self.subTest(codec=name):
+ decoder = codecs.getincrementaldecoder(name)()
+ decoder.setstate((b'', 0xff))
+ with self.assertRaises(UnicodeDecodeError) as cm:
+ decoder.decode(b'!', final=True)
+ self.assertEqual(cm.exception.reason,
+ 'illegal multibyte sequence')
+ self.assertEqual((cm.exception.start, cm.exception.end), (0,
1))
+ # One illegal byte is reported, so error handlers still work.
+ decoder = codecs.getincrementaldecoder(name)(errors='replace')
+ decoder.setstate((b'', 0xff))
+ self.assertEqual(decoder.decode(b'!', final=True), '\ufffd')
+
class Test_StreamReader(unittest.TestCase):
def test_bug1728403(self):
try:
diff --git
a/Misc/NEWS.d/next/Library/2026-07-11-13-24-49.gh-issue-153603.YbKlry.rst
b/Misc/NEWS.d/next/Library/2026-07-11-13-24-49.gh-issue-153603.YbKlry.rst
new file mode 100644
index 000000000000000..5c60a302883931c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-11-13-24-49.gh-issue-153603.YbKlry.rst
@@ -0,0 +1,3 @@
+Fix a crash in the ISO-2022 decoders when decoding a byte after an unknown
+charset designation is set via the decoder's ``setstate`` method.
+Patch by tonghuaroot.
diff --git a/Modules/cjkcodecs/_codecs_iso2022.c
b/Modules/cjkcodecs/_codecs_iso2022.c
index b1984df2695b172..e79e6e768445552 100644
--- a/Modules/cjkcodecs/_codecs_iso2022.c
+++ b/Modules/cjkcodecs/_codecs_iso2022.c
@@ -533,15 +533,16 @@ DECODER(iso2022)
dsg = dsgcache;
else {
for (dsg = CONFIG_DESIGNATIONS;
- dsg->mark != charset
-#ifdef Py_DEBUG
- && dsg->mark != '\0'
-#endif
- ; dsg++)
+ dsg->mark != charset && dsg->mark != '\0';
+ dsg++)
{
/* noop */
}
- assert(dsg->mark != '\0');
+ if (dsg->mark == '\0') {
+ /* Unknown charset designation from a corrupt
+ setstate(); no width to trust, report one byte. */
+ return 1;
+ }
dsgcache = dsg;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]