Skip to content

Commit d4a3777

Browse files
committed
gh-153603: Fix out-of-bounds read in the ISO-2022 decoder for an unknown charset
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.
1 parent c22e9c9 commit d4a3777

3 files changed

Lines changed: 28 additions & 6 deletions

File tree

Lib/test/test_multibytecodec.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,24 @@ def test_setstate_validates_input(self):
306306
self.assertRaises(TypeError, decoder.setstate, (b"1234", "invalid"))
307307
self.assertRaises(UnicodeDecodeError, decoder.setstate, (b"123456789", 0))
308308

309+
def test_setstate_invalid_designation(self):
310+
# gh-153603: an unknown charset designation in the state must not crash
311+
# the decoder. 0xff is not a registered charset mark and 0x21 ('!') is
312+
# a GL byte that triggers the designation lookup.
313+
for name in ('iso-2022-jp', 'iso-2022-kr'):
314+
with self.subTest(codec=name):
315+
decoder = codecs.getincrementaldecoder(name)()
316+
decoder.setstate((b'', 0xff))
317+
with self.assertRaises(UnicodeDecodeError) as cm:
318+
decoder.decode(b'!', final=True)
319+
self.assertEqual(cm.exception.reason,
320+
'illegal multibyte sequence')
321+
self.assertEqual((cm.exception.start, cm.exception.end), (0, 1))
322+
# One illegal byte is reported, so error handlers still work.
323+
decoder = codecs.getincrementaldecoder(name)(errors='replace')
324+
decoder.setstate((b'', 0xff))
325+
self.assertEqual(decoder.decode(b'!', final=True), '\ufffd')
326+
309327
class Test_StreamReader(unittest.TestCase):
310328
def test_bug1728403(self):
311329
try:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash in the ISO-2022 decoders when decoding a byte after an unknown
2+
charset designation is set via the decoder's ``setstate`` method.
3+
Patch by tonghuaroot.

Modules/cjkcodecs/_codecs_iso2022.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -533,15 +533,16 @@ DECODER(iso2022)
533533
dsg = dsgcache;
534534
else {
535535
for (dsg = CONFIG_DESIGNATIONS;
536-
dsg->mark != charset
537-
#ifdef Py_DEBUG
538-
&& dsg->mark != '\0'
539-
#endif
540-
; dsg++)
536+
dsg->mark != charset && dsg->mark != '\0';
537+
dsg++)
541538
{
542539
/* noop */
543540
}
544-
assert(dsg->mark != '\0');
541+
if (dsg->mark == '\0') {
542+
/* Unknown charset designation from a corrupt
543+
setstate(); no width to trust, report one byte. */
544+
return 1;
545+
}
545546
dsgcache = dsg;
546547
}
547548

0 commit comments

Comments
 (0)