From: Yu Watanabe Date: Sun, 19 Mar 2023 06:43:43 +0000 (+0900) Subject: proc-cmdline: filter PID1 arguments when we are running in a container X-Git-Tag: v254-rc1~787^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6339d3e6021f31a8a8907c2613f1aaac279fe745;p=thirdparty%2Fsystemd.git proc-cmdline: filter PID1 arguments when we are running in a container Otherwise, PID1 arguments e.g. "--deserialize 16" may be parsed unexpectedly by generators. Fixes the issue reported at https://github.com/systemd/systemd/issues/24452#issuecomment-1475004433. --- diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h new file mode 100644 index 00000000000..3efeb6df80f --- /dev/null +++ b/src/basic/getopt-defs.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#define SYSTEMD_GETOPT_SHORT_OPTIONS "hDbsz:" + +#define COMMON_GETOPT_ARGS \ + ARG_LOG_LEVEL = 0x100, \ + ARG_LOG_TARGET, \ + ARG_LOG_COLOR, \ + ARG_LOG_LOCATION, \ + ARG_LOG_TIME + +#define SYSTEMD_GETOPT_ARGS \ + ARG_UNIT, \ + ARG_SYSTEM, \ + ARG_USER, \ + ARG_TEST, \ + ARG_NO_PAGER, \ + ARG_VERSION, \ + ARG_DUMP_CONFIGURATION_ITEMS, \ + ARG_DUMP_BUS_PROPERTIES, \ + ARG_BUS_INTROSPECT, \ + ARG_DUMP_CORE, \ + ARG_CRASH_CHVT, \ + ARG_CRASH_SHELL, \ + ARG_CRASH_REBOOT, \ + ARG_CONFIRM_SPAWN, \ + ARG_SHOW_STATUS, \ + ARG_DESERIALIZE, \ + ARG_SWITCHED_ROOT, \ + ARG_DEFAULT_STD_OUTPUT, \ + ARG_DEFAULT_STD_ERROR, \ + ARG_MACHINE_ID, \ + ARG_SERVICE_WATCHDOGS + +#define SHUTDOWN_GETOPT_ARGS \ + ARG_EXIT_CODE, \ + ARG_TIMEOUT + +#define COMMON_GETOPT_OPTIONS \ + { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, \ + { "log-target", required_argument, NULL, ARG_LOG_TARGET }, \ + { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, \ + { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, \ + { "log-time", optional_argument, NULL, ARG_LOG_TIME } + +#define SYSTEMD_GETOPT_OPTIONS \ + { "unit", required_argument, NULL, ARG_UNIT }, \ + { "system", no_argument, NULL, ARG_SYSTEM }, \ + { "user", no_argument, NULL, ARG_USER }, \ + { "test", no_argument, NULL, ARG_TEST }, \ + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, \ + { "help", no_argument, NULL, 'h' }, \ + { "version", no_argument, NULL, ARG_VERSION }, \ + { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, \ + { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES }, \ + { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT }, \ + { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, \ + { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, \ + { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, \ + { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, \ + { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, \ + { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, \ + { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, \ + { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT }, \ + { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, \ + { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, \ + { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, \ + { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS } + +#define SHUTDOWN_GETOPT_OPTIONS \ + { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, \ + { "timeout", required_argument, NULL, ARG_TIMEOUT } diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index 010bb762c42..39e9f2c668c 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -7,6 +7,7 @@ #include "efivars.h" #include "extract-word.h" #include "fileio.h" +#include "getopt-defs.h" #include "initrd-util.h" #include "macro.h" #include "parse-util.h" @@ -16,6 +17,66 @@ #include "strv.h" #include "virt.h" +int proc_cmdline_filter_pid1_args( + char **argv, /* input, may be reordered by this function. */ + char ***ret) { + + enum { + COMMON_GETOPT_ARGS, + SYSTEMD_GETOPT_ARGS, + SHUTDOWN_GETOPT_ARGS, + }; + + static const struct option options[] = { + COMMON_GETOPT_OPTIONS, + SYSTEMD_GETOPT_OPTIONS, + SHUTDOWN_GETOPT_OPTIONS, + {} + }; + + int saved_optind, saved_opterr, saved_optopt, argc; + char *saved_optarg; + char **filtered; + size_t idx; + + assert(argv); + assert(ret); + + /* Backup global variables. */ + saved_optind = optind; + saved_opterr = opterr; + saved_optopt = optopt; + saved_optarg = optarg; + + /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long() + * that checks for GNU extensions in optstring ('-' or '+' at the beginning). Here, we do not use + * the GNU extensions, but might be used previously. Hence, we need to always reset it. */ + optind = 0; + + /* Do not print an error message. */ + opterr = 0; + + /* Filter out all known options. */ + argc = strv_length(argv); + while (getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL) >= 0) + ; + + idx = optind; + + /* Restore global variables. */ + optind = saved_optind; + opterr = saved_opterr; + optopt = saved_optopt; + optarg = saved_optarg; + + filtered = strv_copy(strv_skip(argv, idx)); + if (!filtered) + return -ENOMEM; + + *ret = filtered; + return 0; +} + int proc_cmdline(char **ret) { const char *e; @@ -40,7 +101,7 @@ int proc_cmdline(char **ret) { return read_one_line_file("/proc/cmdline", ret); } -int proc_cmdline_strv(char ***ret) { +static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) { const char *e; int r; @@ -51,9 +112,20 @@ int proc_cmdline_strv(char ***ret) { if (e) return strv_split_full(ret, e, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE); - if (detect_container() > 0) - return get_process_cmdline_strv(1, /* flags = */ 0, ret); - else { + if (detect_container() > 0) { + _cleanup_strv_free_ char **args = NULL; + + r = get_process_cmdline_strv(1, /* flags = */ 0, &args); + if (r < 0) + return r; + + if (filter_pid1_args) + return proc_cmdline_filter_pid1_args(args, ret); + + *ret = TAKE_PTR(args); + return 0; + + } else { _cleanup_free_ char *s = NULL; r = read_one_line_file("/proc/cmdline", &s); @@ -64,6 +136,10 @@ int proc_cmdline_strv(char ***ret) { } } +int proc_cmdline_strv(char ***ret) { + return proc_cmdline_strv_internal(ret, /* filter_pid1_args = */ false); +} + static char *mangle_word(const char *word, ProcCmdlineFlags flags) { char *c; @@ -140,7 +216,7 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineF } } - r = proc_cmdline_strv(&args); + r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true); if (r < 0) return r; @@ -249,7 +325,7 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value) return -EINVAL; - r = proc_cmdline_strv(&args); + r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true); if (r < 0) return r; diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h index d64d7182b80..a64d7757bcc 100644 --- a/src/basic/proc-cmdline.h +++ b/src/basic/proc-cmdline.h @@ -14,6 +14,8 @@ typedef enum ProcCmdlineFlags { typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data); +int proc_cmdline_filter_pid1_args(char **argv, char ***ret); + int proc_cmdline(char **ret); int proc_cmdline_strv(char ***ret); diff --git a/src/core/main.c b/src/core/main.c index 88bef6740cc..23ba22f0031 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -49,6 +49,7 @@ #include "fileio.h" #include "format-util.h" #include "fs-util.h" +#include "getopt-defs.h" #include "hexdecoct.h" #include "hostname-setup.h" #include "ima-setup.h" @@ -818,62 +819,13 @@ static void set_manager_settings(Manager *m) { static int parse_argv(int argc, char *argv[]) { enum { - ARG_LOG_LEVEL = 0x100, - ARG_LOG_TARGET, - ARG_LOG_COLOR, - ARG_LOG_LOCATION, - ARG_LOG_TIME, - ARG_UNIT, - ARG_SYSTEM, - ARG_USER, - ARG_TEST, - ARG_NO_PAGER, - ARG_VERSION, - ARG_DUMP_CONFIGURATION_ITEMS, - ARG_DUMP_BUS_PROPERTIES, - ARG_BUS_INTROSPECT, - ARG_DUMP_CORE, - ARG_CRASH_CHVT, - ARG_CRASH_SHELL, - ARG_CRASH_REBOOT, - ARG_CONFIRM_SPAWN, - ARG_SHOW_STATUS, - ARG_DESERIALIZE, - ARG_SWITCHED_ROOT, - ARG_DEFAULT_STD_OUTPUT, - ARG_DEFAULT_STD_ERROR, - ARG_MACHINE_ID, - ARG_SERVICE_WATCHDOGS, + COMMON_GETOPT_ARGS, + SYSTEMD_GETOPT_ARGS, }; static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, - { "log-time", optional_argument, NULL, ARG_LOG_TIME }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "user", no_argument, NULL, ARG_USER }, - { "test", no_argument, NULL, ARG_TEST }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, - { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES }, - { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT }, - { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, - { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, - { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, - { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, - { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, - { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, - { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, - { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT }, - { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, - { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, - { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, - { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS }, + COMMON_GETOPT_OPTIONS, + SYSTEMD_GETOPT_OPTIONS, {} }; @@ -886,7 +838,7 @@ static int parse_argv(int argc, char *argv[]) { if (getpid_cached() == 1) opterr = 0; - while ((c = getopt_long(argc, argv, "hDbsz:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL)) >= 0) switch (c) { diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 5dee1b3a926..a8248901ce6 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -25,6 +25,7 @@ #include "exec-util.h" #include "fd-util.h" #include "fileio.h" +#include "getopt-defs.h" #include "initrd-util.h" #include "killall.h" #include "log.h" @@ -50,23 +51,13 @@ static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; static int parse_argv(int argc, char *argv[]) { enum { - ARG_LOG_LEVEL = 0x100, - ARG_LOG_TARGET, - ARG_LOG_COLOR, - ARG_LOG_LOCATION, - ARG_LOG_TIME, - ARG_EXIT_CODE, - ARG_TIMEOUT, + COMMON_GETOPT_ARGS, + SHUTDOWN_GETOPT_ARGS, }; static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, - { "log-time", optional_argument, NULL, ARG_LOG_TIME }, - { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, + COMMON_GETOPT_OPTIONS, + SHUTDOWN_GETOPT_OPTIONS, {} }; diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index 943eb3513ce..6df2bca7876 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -6,7 +6,9 @@ #include "initrd-util.h" #include "log.h" #include "macro.h" +#include "nulstr-util.h" #include "proc-cmdline.h" +#include "process-util.h" #include "special.h" #include "string-util.h" #include "strv.h" @@ -264,6 +266,48 @@ TEST(proc_cmdline_key_startswith) { assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx")); } +#define test_proc_cmdline_filter_pid1_args_one(nulstr, expected) \ + ({ \ + _cleanup_strv_free_ char **a = NULL, **b = NULL; \ + const char s[] = (nulstr); \ + \ + /* This emulates get_process_cmdline_strv(). */ \ + assert_se(a = strv_parse_nulstr_full(s, ELEMENTSOF(s), \ + /* drop_trailing_nuls = */ true)); \ + assert_se(proc_cmdline_filter_pid1_args(a, &b) >= 0); \ + assert_se(strv_equal(b, expected)); \ + }) + +TEST(proc_cmdline_filter_pid1_args) { + test_proc_cmdline_filter_pid1_args_one("systemd\0", + STRV_MAKE_EMPTY); + + test_proc_cmdline_filter_pid1_args_one("systemd\0" + "hoge\0" + "-x\0" + "foo\0" + "--aaa\0" + "var\0", + STRV_MAKE("hoge", "foo", "var")); + + test_proc_cmdline_filter_pid1_args_one("/usr/lib/systemd/systemd\0" + "--switched-root\0" + "--system\0" + "--deserialize\030\0" /* followed with space */ + "--deserialize=31\0" /* followed with '=' */ + "--exit-code=42\0" + "\0\0\0" + "systemd.log_level=debug\0" + "--unit\0foo.target\0" + " ' quoted '\0" + "systemd.log_target=console\0" + "\t\0" + " arg with space \0" + "3\0" + "\0\0\0", + STRV_MAKE("", "", "", "systemd.log_level=debug", " ' quoted '", "systemd.log_target=console", "\t", " arg with space ", "3")); +} + static int intro(void) { if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) return log_tests_skipped("can't read /proc/cmdline");