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]

Reply via email to