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.
This commit is contained in:
Yu Watanabe 2023-03-19 15:43:43 +09:00
parent ef9c12b157
commit 6339d3e602
6 changed files with 214 additions and 74 deletions

75
src/basic/getopt-defs.h Normal file
View file

@ -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 }

View 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"
@ -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;

View 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);

View 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) {

View 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,
{}
};

View 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");