From f2c17c4f3557cdcfcfe18df43c917302ab5b97fc Mon Sep 17 00:00:00 2001 From: Armaan Sandhu Date: Fri, 4 Sep 2026 11:40:48 -0400 Subject: [PATCH] strv: measure rebroken lines with utf8_char_console_width() too strv_rebreak_lines() was the last place left that decoded with the permissive utf8_encoded_to_unichar() and then asked unichar_console_width() how wide the result is, so it accepted and measured overlong encodings, surrogates, noncharacters and the obsolete five and six byte forms that utf8_console_width() now refuses. That matters because callers mix the two within a single width computation. sd-varlink-idl.c sizes its indent with utf8_console_width() and then breaks the body with strv_rebreak_lines(); verbs.c does the same. One half of the sum refused a sequence that the other half happily assigned a width to. Use utf8_char_console_width(), which does the validation and the width in one step, and advance with utf8_next_char() rather than the decoded length. The function already refused invalid UTF-8, so the only change is which sequences count as invalid; it now matches utf8_is_valid(), as everywhere else. (cherry picked from commit 7cc0257ba80317121faf97fda6d69d0e5b2e43af) --- src/basic/strv.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/basic/strv.c b/src/basic/strv.c index 443ae9028d..faf413e8ba 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -8,6 +8,7 @@ #include "escape.h" #include "extract-word.h" #include "fileio.h" +#include "gunicode.h" #include "hashmap.h" #include "log.h" #include "memory-util.h" @@ -1281,13 +1282,14 @@ int strv_rebreak_lines(char **l, size_t width, char ***ret) { in_prefix = false; } - char32_t c; - int n = utf8_encoded_to_unichar(p, &c); - if (n < 0) - return log_debug_errno(n, "Line to break contains invalid UTF-8, refusing: %m"); - - int cw = unichar_console_width(c); - assert(cw >= 0); + /* Measure with the same helper utf8_console_width() uses. Our callers mix the + * two: sd-varlink-idl.c and verbs.c size their indent with utf8_console_width() + * and then break the body here, so if we accepted and measured what that one + * refuses we'd be computing a single width from two ideas of what valid UTF-8 + * is. */ + int cw = utf8_char_console_width(p); + if (cw < 0) + return log_debug_errno(cw, "Line to break contains invalid UTF-8, refusing: %m"); w += cw; @@ -1312,7 +1314,7 @@ int strv_rebreak_lines(char **l, size_t width, char ***ret) { continue; } - p += n; + p = utf8_next_char(p); } /* Process rest of the line, except if all that's left after the last newline we already