From 1ed3015ffca834e0161d84cc54336506c01fff19 Mon Sep 17 00:00:00 2001 From: Brandon Barrante Date: Sun, 6 Sep 2026 18:15:59 -0400 Subject: [PATCH] normalize_utf32: skip out-of-range codepoints in the compose loop (fixes #288) utf8proc_normalize_utf32's UTF8PROC_COMPOSE branch reads each buffer[rpos] and passes it to unsafe_get_property, guarded only by if (current_char < 0) continue; (a skip meant for the grapheme-break sentinel written by utf8proc_decompose_char, not general invalid input). unsafe_get_property indexes utf8proc_stage1table[uc >> 8], and utf8proc_stage1table is const utf8proc_uint16_t[4352]. So a UTF-32 codepoint >= 0x110000 in the input buffer produces uc >> 8 >= 4352 and reads past the table. Add the missing bound to the existing skip -- the same uc < 0 || uc >= 0x110000 shape the public wrapper utf8proc_get_property already uses (utf8proc.c:243) and that utf8proc_decompose_char rejects with UTF8PROC_ERROR_NOTASSIGNED (utf8proc.c:456). Invalid codepoints are dropped the same way grapheme-break sentinels already are. Reproduced under ASan against master 0075ed7d with a 3-codepoint buffer { 0x0041, 0x0301, 0x110000 } and UTF8PROC_COMPOSE: global-buffer-overflow READ of size 2 at unsafe_get_property, 0 bytes after utf8proc_stage1table (size 8704). Clean after this patch. Fixes: #288 Reported-by: shuangxiangkan (github.com/shuangxiangkan) Signed-off-by: Brandon Barrante --- utf8proc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utf8proc.c b/utf8proc.c index 8afb11a..23fd39e 100644 --- a/utf8proc.c +++ b/utf8proc.c @@ -662,8 +662,9 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_normalize_utf32(utf8proc_int32_t *b utf8proc_ssize_t wpos = 0; for (rpos = 0; rpos < length; rpos++) { utf8proc_int32_t current_char = buffer[rpos]; - if (current_char < 0) { - /* skip grapheme break */ + if (current_char < 0 || current_char >= 0x110000) { + /* skip grapheme-break sentinel or out-of-range codepoint; + unsafe_get_property would OOB on utf8proc_stage1table (idx = uc >> 8) */ continue; } const utf8proc_property_t *current_property = unsafe_get_property(current_char);