https://github.com/python/cpython/commit/5f28e315cc08d9a58293a1ae5d91db9b9d8ff0e8
commit: 5f28e315cc08d9a58293a1ae5d91db9b9d8ff0e8
branch: main
author: Gregory P. Smith <[email protected]>
committer: gpshead <[email protected]>
date: 2026-11-01T06:33:48Z
summary:

gh-160187: Reject ISO-2022 designations into the wrong G set (GH-160188)

The ISO-2022 decoders checked only that a designated charset belonged to
the encoding, not that it went to the code element the encoding assigns
it to.

ANT-2026-TH3IZKXQ

files:
A Misc/NEWS.d/next/Library/2026-10-22-00-30-04.gh-issue-160187.9dcj2B.rst
M Lib/test/test_codecencodings_iso2022.py
M Modules/cjkcodecs/_codecs_iso2022.c

diff --git a/Lib/test/test_codecencodings_iso2022.py 
b/Lib/test/test_codecencodings_iso2022.py
index 027dbecc6134df7..117dc6b66d46b69 100644
--- a/Lib/test/test_codecencodings_iso2022.py
+++ b/Lib/test/test_codecencodings_iso2022.py
@@ -15,6 +15,10 @@ class Test_ISO2022_JP(multibytecodec_support.TestBase, 
unittest.TestCase):
     tstring = multibytecodec_support.load_teststring('iso2022_jp')
     codectests = COMMON_CODEC_TESTS + (
         (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'),
+        # there is no G1 in this encoding
+        (b'ab\x1B$)Bdef', 'strict', None),
+        (b'ab\x1B)Jdef', 'strict', None),
+        (b'ab\x1B)Bdef', 'strict', None),
     )
 
 class Test_ISO2022_JP2(multibytecodec_support.TestBase, unittest.TestCase):
@@ -22,6 +26,15 @@ class Test_ISO2022_JP2(multibytecodec_support.TestBase, 
unittest.TestCase):
     tstring = multibytecodec_support.load_teststring('iso2022_jp')
     codectests = COMMON_CODEC_TESTS + (
         (b'ab\x1BNdef', 'replace', 'abdef'),
+        # a charset can only be designated to the G set (G0, G1 or G2)
+        # that RFC 1554 assigns it
+        (b'\x1B.A\x1BNi', 'strict', '\xe9'),
+        (b'\x1B$(C\x30\x21\x1B(B', 'strict', '\uac00'),
+        (b'ab\x1B(Adef', 'strict', None),
+        (b'ab\x1B(Fdef', 'strict', None),
+        (b'ab\x1B.J\x1BNdef', 'strict', None),
+        (b'ab\x1B.Bdef', 'strict', None),
+        (b'ab\x1B$)Cdef', 'strict', None),
     )
 
 class Test_ISO2022_JP3(multibytecodec_support.TestBase, unittest.TestCase):
@@ -75,6 +88,12 @@ class Test_ISO2022_KR(multibytecodec_support.TestBase, 
unittest.TestCase):
     tstring = multibytecodec_support.load_teststring('iso2022_kr')
     codectests = COMMON_CODEC_TESTS + (
         (b'ab\x1BNdef', 'replace', 'ab\x1BNdef'),
+        # KS X 1001 can only be designated to G1
+        (b'\x1B$)C\x0E\x30\x21\x0Fdef', 'strict', '\uac00def'),
+        (b'ab\x1B$(Cdefg', 'strict', None),
+        (b'ab\x1B$Cdefg', 'strict', None),
+        (b'ab\x1B)Bdefg', 'strict', None),
+        (b'ab\x1B$(Cdefg', 'replace', 'ab\ufffddefg'),
     )
 
     # iso2022_kr.txt cannot be used to test "chunk coding": the escape
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-22-00-30-04.gh-issue-160187.9dcj2B.rst 
b/Misc/NEWS.d/next/Library/2026-10-22-00-30-04.gh-issue-160187.9dcj2B.rst
new file mode 100644
index 000000000000000..74ee6c1fa7bbb92
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-22-00-30-04.gh-issue-160187.9dcj2B.rst
@@ -0,0 +1,4 @@
+Fix the ISO-2022 decoders (``iso2022_jp``, ``iso2022_kr`` and related)
+accepting escape sequences that designate a character set to the wrong code
+element (G0, G1 or G2).  Such sequences now raise :exc:`UnicodeDecodeError`
+instead of producing wrong text or :exc:`RuntimeError`.
diff --git a/Modules/cjkcodecs/_codecs_iso2022.c 
b/Modules/cjkcodecs/_codecs_iso2022.c
index 38a8a230c7ae450..f0cba62e8773877 100644
--- a/Modules/cjkcodecs/_codecs_iso2022.c
+++ b/Modules/cjkcodecs/_codecs_iso2022.c
@@ -379,17 +379,22 @@ iso2022processesc(const MultibyteCodec *codec, 
MultibyteCodec_State *state,
         return esclen;
     }
 
-    /* raise error when the charset is not designated for this encoding */
+    /* raise error when the charset is not designated to this plane for
+     * this encoding (ASCII is only ever designated to G0) */
     if (charset != CHARSET_ASCII) {
         const struct iso2022_designation *dsg;
 
         for (dsg = CONFIG_DESIGNATIONS; dsg->mark; dsg++) {
-            if (dsg->mark == charset)
+            if (dsg->mark == charset && dsg->plane == designation) {
                 break;
+            }
         }
         if (!dsg->mark)
             return esclen;
     }
+    else if (designation != 0) {
+        return esclen;
+    }
 
     STATE_SETG(designation, charset);
     *inleft -= esclen;

_______________________________________________
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