]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tree-wide: add basic validation of --background argument
authorDaniel Hast <hast.daniel@protonmail.com>
Fri, 24 Oct 2025 22:47:59 +0000 (18:47 -0400)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 25 Oct 2025 00:56:31 +0000 (09:56 +0900)
Check whether the argument of the `--background` option of
`systemd-run`, `run0`, `systemd-nspawn`, `systemd-vmspawn`, and
`systemd-pty-forward` is either empty or looks like an ANSI color code,
and reject invalid values when parsing arguments.

We consider a string to look like an ANSI color code if it consists of
one or more sequences of ASCII digits separated by semicolons. This
permits every valid ANSI color code, and should reject anything that
results in garbled output.

src/basic/ansi-color.c
src/basic/ansi-color.h
src/nspawn/nspawn.c
src/ptyfwd/ptyfwd-tool.c
src/run/run.c
src/shared/parse-argument.c
src/shared/parse-argument.h
src/test/test-parse-argument.c
src/vmspawn/vmspawn.c

index 839dbc5e91f5a6de9ffbf0f036089334bca1c59b..af2aa296d05871ec620604e00b2125fa87c0b00a 100644 (file)
@@ -100,3 +100,28 @@ static const char* const color_mode_table[_COLOR_MODE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(color_mode, ColorMode, COLOR_24BIT);
+
+/*
+ * Check that the string is formatted like an ANSI color code, i.e. that it consists of one or more
+ * sequences of ASCII digits separated by semicolons. This is equivalent to matching the regex:
+ *      ^[0-9]+(;[0-9]+)*$
+ * This can be used to partially validate escape codes of the form "\x1B[%sm", accepting all valid
+ * ANSI color codes while rejecting anything that would result in garbled output (such as injecting
+ * text or changing the type of escape code).
+ */
+bool looks_like_ansi_color_code(const char *str) {
+        assert(str);
+
+        bool prev_char_was_digit = false;
+
+        for (char c = *str; c != '\0'; c = *(++str)) {
+                if (ascii_isdigit(c))
+                        prev_char_was_digit = true;
+                else if (prev_char_was_digit && c == ';')
+                        prev_char_was_digit = false;
+                else
+                        return false;
+        }
+
+        return prev_char_was_digit;
+}
index a5eea226a93d9086a51526b3ce090798c4862859..c81120d2993dbe32e05fe3718fe2c603d24bc364 100644 (file)
@@ -28,6 +28,8 @@ bool underline_enabled(void);
 
 void reset_ansi_feature_caches(void);
 
+bool looks_like_ansi_color_code(const char *str);
+
 /* Regular colors */
 #define ANSI_BLACK   "\x1B[0;30m" /* Some type of grey usually. */
 #define ANSI_RED     "\x1B[0;31m"
index bc1ed4c9eb5407d9bd68697e908e815484685701..b19a04624b917a3993023d3e0181c49ce188e7f3 100644 (file)
@@ -1529,7 +1529,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_BACKGROUND:
-                        r = free_and_strdup_warn(&arg_background, optarg);
+                        r = parse_background_argument(optarg, &arg_background);
                         if (r < 0)
                                 return r;
                         break;
index 62ef96a3ddd330f9e358dc8553330cc68d434549..dd0c638f74007e3dcce5cf6f9c7b395cc32cd051 100644 (file)
@@ -9,6 +9,7 @@
 #include "fd-util.h"
 #include "log.h"
 #include "main-func.h"
+#include "parse-argument.h"
 #include "pidref.h"
 #include "pretty-print.h"
 #include "process-util.h"
@@ -96,7 +97,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_BACKGROUND:
-                        r = free_and_strdup_warn(&arg_background, optarg);
+                        r = parse_background_argument(optarg, &arg_background);
                         if (r < 0)
                                 return r;
                         break;
index ae2bd4115ec30ab6db71ce87aac3700f705b6d5b..1c01d4485b15f856c09897b06e7fd757d90f34a0 100644 (file)
@@ -686,7 +686,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_BACKGROUND:
-                        r = free_and_strdup_warn(&arg_background, optarg);
+                        r = parse_background_argument(optarg, &arg_background);
                         if (r < 0)
                                 return r;
                         break;
@@ -978,7 +978,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
                         break;
 
                 case ARG_BACKGROUND:
-                        r = free_and_strdup_warn(&arg_background, optarg);
+                        r = parse_background_argument(optarg, &arg_background);
                         if (r < 0)
                                 return r;
 
index 58b914f24143db2c3e0727b17486278d9566b79d..f8cd7f30ff7470872252aa621d4537e4536a67d8 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
+#include "ansi-color.h"
 #include "bus-util.h"
 #include "format-table.h"
 #include "hostname-util.h"
@@ -163,3 +164,10 @@ int parse_machine_argument(const char *s, const char **ret_host, BusTransport *r
 
         return 0;
 }
+
+int parse_background_argument(const char *s, char **arg) {
+        if (!isempty(s) && !looks_like_ansi_color_code(s))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --background= argument: %s", s);
+
+        return free_and_strdup_warn(arg, s);
+}
index f1b2b63178f422c0caf0f749336c9f6480775a44..75098b8eb1aa9513c7d2f13f08a9b7403ebb1bd5 100644 (file)
@@ -9,3 +9,4 @@ int parse_json_argument(const char *s, sd_json_format_flags_t *ret);
 int parse_path_argument(const char *path, bool suppress_root, char **arg);
 int parse_signal_argument(const char *s, int *ret);
 int parse_machine_argument(const char *s, const char **ret_host, BusTransport *ret_transport);
+int parse_background_argument(const char *s, char **arg);
index 843179b666178438b0ea96370eff95543fff5960..b4eb8b1033a79d0df48c5a76e42816ce7cf59d66 100644 (file)
@@ -54,4 +54,37 @@ TEST(parse_signal_argument) {
         assert_se(signal == SIGABRT);
 }
 
+TEST(parse_background_argument) {
+        _cleanup_free_ char *arg_bg_good = NULL;
+
+        /* Should accept empty string */
+        assert_se(parse_background_argument("", &arg_bg_good) >= 0);
+        ASSERT_STREQ(arg_bg_good, "");
+
+        /* Should accept ANSI color codes in palette, 8-bit, or 24-bit format */
+        assert_se(parse_background_argument("42", &arg_bg_good) >= 0);
+        ASSERT_STREQ(arg_bg_good, "42");
+
+        assert_se(parse_background_argument("48;5;219", &arg_bg_good) >= 0);
+        ASSERT_STREQ(arg_bg_good, "48;5;219");
+
+        assert_se(parse_background_argument("48;2;3;141;59", &arg_bg_good) >= 0);
+        ASSERT_STREQ(arg_bg_good, "48;2;3;141;59");
+
+        _cleanup_free_ char *arg_bg_bad = NULL;
+
+        /* Should reject various invalid arguments */
+        assert_se(parse_background_argument("42;", &arg_bg_bad) < 0);
+        ASSERT_NULL(arg_bg_bad);
+
+        assert_se(parse_background_argument(";42", &arg_bg_bad) < 0);
+        ASSERT_NULL(arg_bg_bad);
+
+        assert_se(parse_background_argument("4;;2", &arg_bg_bad) < 0);
+        ASSERT_NULL(arg_bg_bad);
+
+        assert_se(parse_background_argument("4a2", &arg_bg_bad) < 0);
+        ASSERT_NULL(arg_bg_bad);
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 3a2cd919b6a14fc9972de5bd8481df8ff6f00d08..f3ee2c28e704b708b6157f41687b1a98a89ba87f 100644 (file)
@@ -612,7 +612,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_BACKGROUND:
-                        r = free_and_strdup_warn(&arg_background, optarg);
+                        r = parse_background_argument(optarg, &arg_background);
                         if (r < 0)
                                 return r;
                         break;