]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
proc-cmdline: filter PID1 arguments when we are running in a container 26887/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 19 Mar 2023 06:43:43 +0000 (15:43 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 29 Mar 2023 01:34:41 +0000 (10:34 +0900)
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.

src/basic/getopt-defs.h [new file with mode: 0644]
src/basic/proc-cmdline.c
src/basic/proc-cmdline.h
src/core/main.c
src/shutdown/shutdown.c
src/test/test-proc-cmdline.c

diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h
new file mode 100644 (file)
index 0000000..3efeb6d
--- /dev/null
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <getopt.h>
+
+#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      }
index 010bb762c422692fbb28aa5a055f61e1d31cceb4..39e9f2c668c938a87f3d33efe77865841756cd24 100644 (file)
@@ -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"
 #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;
 
index d64d7182b801e74a6bddfecebe23958d2246b3b4..a64d7757bcc8d1176450d2f1dc2ec538b1c83d05 100644 (file)
@@ -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);
 
index 88bef6740ccf4a54b71c6b090db9625c62136043..23ba22f00312e843c25d533237c817e2ee09dfe9 100644 (file)
@@ -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) {
 
index 5dee1b3a926c00c7916c3e07a11c46353662a073..a8248901ce6c9c41fc25fee55c855966973fc70d 100644 (file)
@@ -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,
                 {}
         };
 
index 943eb3513ce07a40555500e0017a1e459f4048ce..6df2bca7876ea00941350176425933372e075f65 100644 (file)
@@ -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");