Skip to content

Commit 600f99d

Browse files
authored
gh-159739: Preserve non-BMP characters in multibyte encoders (#159987)
1 parent cbf64e8 commit 600f99d

5 files changed

Lines changed: 20 additions & 3 deletions

File tree

‎Lib/test/test_multibytecodec.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ def test_str_decode(self):
4343
for enc in ALL_CJKENCODINGS:
4444
self.assertEqual('abcd'.encode(enc), b'abcd')
4545

46+
def test_nonbmp_pair_modifier(self):
47+
cases = [('big5hkscs', '\u00ca', '\u0304')]
48+
cases.extend((encoding, '\u304b', '\u309a') for encoding in (
49+
'euc_jis_2004', 'euc_jisx0213', 'shift_jis_2004', 'shift_jisx0213',
50+
'iso2022_jp_2004', 'iso2022_jp_3'))
51+
for encoding, base, modifier in cases:
52+
with self.subTest(encoding=encoding):
53+
pair = base + modifier
54+
self.assertEqual(pair.encode(encoding).decode(encoding), pair)
55+
nonbmp = base + chr(0x10000 + ord(modifier))
56+
self.assertRaises(UnicodeEncodeError, nonbmp.encode, encoding)
57+
4658
def test_errorcallback_longindex(self):
4759
dec = codecs.getdecoder('euc-kr')
4860
myreplace = lambda exc: ('', sys.maxsize+1)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix multibyte encoders silently consuming a non-BMP character when looking up
2+
a two-character mapping.

‎Modules/cjkcodecs/_codecs_hk.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ ENCODER(big5hkscs)
6262
else
6363
c2 = 0;
6464

65-
if (inlen - *inpos >= 2 &&
65+
if (inlen - *inpos >= 2 && c2 <= 0xffff &&
6666
((c & 0xffdf) == 0x00ca) &&
6767
((c2 & 0xfff7) == 0x0304)) {
6868
code = big5hkscs_pairenc_table[

‎Modules/cjkcodecs/_codecs_iso2022.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ jisx0213_encoder(const MultibyteCodec *codec, const Py_UCS4 *data,
804804

805805
case 2: /* second character of unicode pair */
806806
if (data[1] != 0) { /* Don't consume null char as part of pair */
807-
coded = find_pairencmap((ucs2_t)data[0], (ucs2_t)data[1],
807+
coded = find_pairencmap((ucs2_t)data[0], data[1],
808808
jisx0213_pair_encmap, JISX0213_ENCPAIRS);
809809
if (coded != DBCINV) {
810810
return coded;

‎Modules/cjkcodecs/cjkcodecs.h‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,13 @@ register_maps(PyObject *module)
406406

407407
#ifdef USING_BINARY_PAIR_SEARCH
408408
static DBCHAR
409-
find_pairencmap(ucs2_t body, ucs2_t modifier,
409+
find_pairencmap(ucs2_t body, Py_UCS4 modifier,
410410
const struct pair_encodemap *haystack, int haystacksize)
411411
{
412412
int pos, min, max;
413+
if (modifier > 0xffff) {
414+
return DBCINV;
415+
}
413416
Py_UCS4 value = body << 16 | modifier;
414417

415418
min = 0;

0 commit comments

Comments
 (0)