aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvi Halachmi (:avih) <avihpit@yahoo.com>2026-03-22 15:58:31 +0200
committerRon Yorston <rmy@pobox.com>2026-03-25 14:53:20 +0000
commit4e7a8bfc75b129b3a671229544f9e5c4168daabf (patch)
treef90ba5d38df63bf143e6c0921f1ab71706f18e0a
parent8d97f41766c905bf527ee1b08d021fc4ae9d5af3 (diff)
downloadbusybox-w32-4e7a8bfc75b129b3a671229544f9e5c4168daabf.tar.gz
busybox-w32-4e7a8bfc75b129b3a671229544f9e5c4168daabf.tar.bz2
busybox-w32-4e7a8bfc75b129b3a671229544f9e5c4168daabf.zip
win32: UTF8_OUTPUT: reject overlong encoding
Overlong UTF8 encoding means that a codepoint is encoded using more bytes than necessary, and it's disallowed. Until now we didn't reject it, e.g. 2-bytes sequence of 0xc0 0x9b was incorrectly decoded as codepoint 0x1b (ESC), but it's overlong because 1-byte 0x1b is enough for this codepoint. Now we reject such sequences and print '?' (CONFIG_SUBST_WCHAR). Additionally, we now also reject 4-bytes sequences which end up above the maximum valid codepoint value (0x10ffff). No such issue with 1/2/3 bytes UTF-8 because technically only 4 can encode a too big value.
-rw-r--r--win32/winansi.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/win32/winansi.c b/win32/winansi.c
index 53f55d09a..213620991 100644
--- a/win32/winansi.c
+++ b/win32/winansi.c
@@ -1458,6 +1458,7 @@ static int writeCon_utf8(int fd, const char *u8buf, size_t u8siz)
1458{ 1458{
1459 // state during/between calls 1459 // state during/between calls
1460 static int state = 0; // 0-3: remaining cp bytes (0: done/new) 1460 static int state = 0; // 0-3: remaining cp bytes (0: done/new)
1461 static int tail = 0; // init like state, used for overlong rejection
1461 static uint32_t codepoint = 0; // accumulated from up to 4 UTF8 bytes 1462 static uint32_t codepoint = 0; // accumulated from up to 4 UTF8 bytes
1462 1463
1463 // wbuf is not a state, but it's kept between calls to avoid repeated 1464 // wbuf is not a state, but it's kept between calls to avoid repeated
@@ -1488,15 +1489,25 @@ static int writeCon_utf8(int fd, const char *u8buf, size_t u8siz)
1488 codepoint = c; 1489 codepoint = c;
1489 1490
1490 } else if (state > 0 && topbits == 1) { 1491 } else if (state > 0 && topbits == 1) {
1491 // valid continuation byte 1492 // valid continuation/final byte (2/3/4 bytes UTF8)
1493 // min value for 1/2/3/4 bytes UTF-8 (cpmin[0] is unused)
1494 static const uint32_t cpmin[] = {0, 0x80, 0x800, 0x10000};
1495
1492 codepoint = (codepoint << 6) | (c & 0x3f); 1496 codepoint = (codepoint << 6) | (c & 0x3f);
1493 if (--state) 1497 if (--state)
1494 continue; 1498 continue;
1495 1499
1500 // done. ensure codepoint is not too small (overlong
1501 // with 2/3/4 bytes), and not too big (with 4 bytes).
1502 // can be optimized further, with longer explanation:
1503 // if ((codepoint - cpmin[tail]) & ~0xfffffu) ...
1504 if (codepoint < cpmin[tail] || codepoint > 0x10ffff)
1505 codepoint = CONFIG_SUBST_WCHAR;
1506
1496 } else if (state == 0 && topbits >= 2 && topbits <= 4) { 1507 } else if (state == 0 && topbits >= 2 && topbits <= 4) {
1497 // valid UTF8 lead of 2/3/4 bytes codepoint 1508 // valid UTF8 lead of 2/3/4 bytes codepoint
1498 codepoint = c & (0x7f >> topbits); 1509 codepoint = c & (0x7f >> topbits);
1499 state = topbits - 1; // remaining bytes after lead 1510 tail = state = topbits - 1; // expected bytes after lead
1500 continue; 1511 continue;
1501 1512
1502 } else { 1513 } else {