From 00f57157f32f6ed5a68d68986b013c203cd78c37 Mon Sep 17 00:00:00 2001 From: msizanoen1 Date: Wed, 1 Mar 2023 17:35:17 +0700 Subject: [PATCH 1/3] escape: Ensure that output is always valid UTF-8 This ensures that shell string escape operations will not produce output with invalid UTF-8 from the input by escaping invalid UTF-8 data as if they were single byte characters. --- src/basic/escape.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/basic/escape.c b/src/basic/escape.c index e04b435d5b..317e2786d1 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -474,14 +474,20 @@ char* octescape(const char *s, size_t len) { static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) { assert(bad); - for (; *s; s++) - if (char_is_cc(*s)) - t += cescape_char(*s, t); - else { + while (*s) { + int l = utf8_encoded_valid_unichar(s, SIZE_MAX); + + if (char_is_cc(*s) || l < 0) + t += cescape_char(*(s++), t); + else if (l == 1) { if (*s == '\\' || strchr(bad, *s)) *(t++) = '\\'; - *(t++) = *s; + *(t++) = *(s++); + } else { + t = mempcpy(t, s, l); + s += l; } + } return t; } @@ -510,11 +516,16 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) { if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s)) return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */ - for (p = s; *p; p++) - if (char_is_cc(*p) || + for (p = s; *p; ) { + int l = utf8_encoded_valid_unichar(p, SIZE_MAX); + + if (char_is_cc(*p) || l < 0 || strchr(WHITESPACE SHELL_NEED_QUOTES, *p)) break; + p += l; + } + if (!*p) return strdup(s); From 582843ee37fb2de62321085dd3c2f4bfbdbad12e Mon Sep 17 00:00:00 2001 From: msizanoen1 Date: Wed, 1 Mar 2023 21:48:08 +0700 Subject: [PATCH 2/3] test-escape: Add tests for escaping bogus UTF-8 sequences --- src/test/test-escape.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/test-escape.c b/src/test/test-escape.c index b1ab5bab18..f3dd579e56 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -196,6 +196,10 @@ TEST(shell_maybe_quote) { test_shell_maybe_quote_one("głąb\002\003rząd", 0, "\"głąb\\002\\003rząd\""); test_shell_maybe_quote_one("głąb\002\003rząd", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003rząd'"); + + /* Bogus UTF-8 strings */ + test_shell_maybe_quote_one("\250\350", 0, "\"\\250\\350\""); + test_shell_maybe_quote_one("\250\350", SHELL_ESCAPE_POSIX, "$'\\250\\350'"); } static void test_quote_command_line_one(char **argv, const char *expected) { From 0b82a6fa5d2ba94bbc1c3db4a04dfd052e1cd446 Mon Sep 17 00:00:00 2001 From: msizanoen1 Date: Thu, 2 Mar 2023 18:37:02 +0700 Subject: [PATCH 3/3] escape: add missing non-NULL parameter assertions --- src/basic/escape.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/basic/escape.c b/src/basic/escape.c index 317e2786d1..d2cb1c7320 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -473,6 +473,8 @@ char* octescape(const char *s, size_t len) { static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) { assert(bad); + assert(t); + assert(s); while (*s) { int l = utf8_encoded_valid_unichar(s, SIZE_MAX);