From: Zbigniew Jędrzejewski-Szmek Date: Sun, 6 Nov 2022 15:45:48 +0000 (+0100) Subject: basic: move a bunch of cmdline-related funcs to new argv-util.c+h X-Git-Tag: v253-rc1~572^2~3 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ee617a4e5ca5828cabd46f3bb1d9ffc0dd3db9e5;p=thirdparty%2Fsystemd.git basic: move a bunch of cmdline-related funcs to new argv-util.c+h I wanted to move saved_arg[cv] to process-util.c+h, but this causes problems: process-util.h includes format-util.h which includes net/if.h, which conflicts with linux/if.h. So we can't include process-util.h in some files. But process-util.c is very long anyway, so it seems nice to create a new file. rename_process(), invoked_as(), invoked_by_systemd(), and argv_looks_like_help() which lived in process-util.c refer to saved_argc and saved_argv, so it seems reasonable to move them to the new file too. util.c is now empty, so it is removed. util.h remains. --- diff --git a/src/basic/argv-util.c b/src/basic/argv-util.c new file mode 100644 index 00000000000..54649f4bda8 --- /dev/null +++ b/src/basic/argv-util.c @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include +#include + +#include "argv-util.h" +#include "errno-util.h" +#include "missing_sched.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "string-util.h" +#include "strv.h" + +int saved_argc = 0; +char **saved_argv = NULL; + +bool invoked_as(char *argv[], const char *token) { + if (!argv || isempty(argv[0])) + return false; + + if (isempty(token)) + return false; + + return strstr(last_path_component(argv[0]), token); +} + +bool invoked_by_systemd(void) { + int r; + + /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd, + * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */ + const char *e = getenv("SYSTEMD_EXEC_PID"); + if (!e) + return false; + + if (streq(e, "*")) + /* For testing. */ + return true; + + pid_t p; + r = parse_pid(e, &p); + if (r < 0) { + /* We know that systemd sets the variable correctly. Something else must have set it. */ + log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e); + return false; + } + + return getpid_cached() == p; +} + +bool argv_looks_like_help(int argc, char **argv) { + char **l; + + /* Scans the command line for indications the user asks for help. This is supposed to be called by + * tools that do not implement getopt() style command line parsing because they are not primarily + * user-facing. Detects four ways of asking for help: + * + * 1. Passing zero arguments + * 2. Passing "help" as first argument + * 3. Passing --help as any argument + * 4. Passing -h as any argument + */ + + if (argc <= 1) + return true; + + if (streq_ptr(argv[1], "help")) + return true; + + l = strv_skip(argv, 1); + + return strv_contains(l, "--help") || + strv_contains(l, "-h"); +} + +static int update_argv(const char name[], size_t l) { + static int can_do = -1; + + if (can_do == 0) + return 0; + can_do = false; /* We'll set it to true only if the whole process works */ + + /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the + * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is + * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if + * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but + * mmap() is not. */ + if (geteuid() != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "Skipping PR_SET_MM, as we don't have privileges."); + + static size_t mm_size = 0; + static char *mm = NULL; + int r; + + if (mm_size < l+1) { + size_t nn_size; + char *nn; + + nn_size = PAGE_ALIGN(l+1); + nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (nn == MAP_FAILED) + return log_debug_errno(errno, "mmap() failed: %m"); + + strncpy(nn, name, nn_size); + + /* Now, let's tell the kernel about this new memory */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + if (ERRNO_IS_PRIVILEGE(errno)) + return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); + + /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be + * below the desired start address, in which case the kernel may have kicked this back due + * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in + * action). The proper solution would be to have a prctl() API that could set both start+end + * simultaneously, or at least let us query the existing address to anticipate this condition + * and respond accordingly. For now, we can only guess at the cause of this failure and try + * a workaround--which will briefly expand the arg space to something potentially huge before + * resizing it to what we want. */ + log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); + + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { + r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + (void) munmap(nn, nn_size); + return r; + } + + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) + return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); + } else { + /* And update the end pointer to the new end, too. If this fails, we don't really know what + * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, + * and continue. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } + + if (mm) + (void) munmap(mm, mm_size); + + mm = nn; + mm_size = nn_size; + } else { + strncpy(mm, name, mm_size); + + /* Update the end pointer, continuing regardless of any failure. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } + + can_do = true; + return 0; +} + +int rename_process(const char name[]) { + bool truncated = false; + + /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's + * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in + * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; + * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be + * truncated. + * + * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ + + if (isempty(name)) + return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + + size_t l = strlen(name); + + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); + if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ + truncated = true; + + /* Second step, change glibc's ID of the process name. */ + if (program_invocation_name) { + size_t k; + + k = strlen(program_invocation_name); + strncpy(program_invocation_name, name, k); + if (l > k) + truncated = true; + } + + /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but + * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at + * the end. This is the best option for changing /proc/self/cmdline. */ + (void) update_argv(name, l); + + /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if + * it still looks here */ + if (saved_argc > 0) { + if (saved_argv[0]) { + size_t k; + + k = strlen(saved_argv[0]); + strncpy(saved_argv[0], name, k); + if (l > k) + truncated = true; + } + + for (int i = 1; i < saved_argc; i++) { + if (!saved_argv[i]) + break; + + memzero(saved_argv[i], strlen(saved_argv[i])); + } + } + + return !truncated; +} diff --git a/src/basic/argv-util.h b/src/basic/argv-util.h new file mode 100644 index 00000000000..a20a951793a --- /dev/null +++ b/src/basic/argv-util.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "macro.h" + +extern int saved_argc; +extern char **saved_argv; + +static inline void save_argc_argv(int argc, char **argv) { + /* Protect against CVE-2021-4034 style attacks */ + assert_se(argc > 0); + assert_se(argv); + assert_se(argv[0]); + + saved_argc = argc; + saved_argv = argv; +} + +bool invoked_as(char *argv[], const char *token); +bool invoked_by_systemd(void); +bool argv_looks_like_help(int argc, char **argv); + +int rename_process(const char name[]); diff --git a/src/basic/log.c b/src/basic/log.c index 39d08b09289..173643196e7 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -16,6 +16,7 @@ #include "sd-messages.h" #include "alloc-util.h" +#include "argv-util.h" #include "errno-util.h" #include "fd-util.h" #include "format-util.h" diff --git a/src/basic/meson.build b/src/basic/meson.build index c1d970b6a9f..c5dd116964d 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -9,6 +9,8 @@ basic_sources = files( 'alloc-util.h', 'architecture.c', 'architecture.h', + 'argv-util.c', + 'argv-util.h', 'arphrd-util.c', 'arphrd-util.h', 'async.c', @@ -256,7 +258,6 @@ basic_sources = files( 'user-util.h', 'utf8.c', 'utf8.h', - 'util.c', 'util.h', 'virt.c', 'virt.h', diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 6e4a56b0aac..dd913bc3238 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,7 @@ #include "alloc-util.h" #include "architecture.h" +#include "argv-util.h" #include "env-file.h" #include "env-util.h" #include "errno-util.h" @@ -297,151 +297,6 @@ int container_get_leader(const char *machine, pid_t *pid) { return 0; } -static int update_argv(const char name[], size_t l) { - static int can_do = -1; - - if (can_do == 0) - return 0; - can_do = false; /* We'll set it to true only if the whole process works */ - - /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the - * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is - * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if - * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but - * mmap() is not. */ - if (geteuid() != 0) - return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "Skipping PR_SET_MM, as we don't have privileges."); - - static size_t mm_size = 0; - static char *mm = NULL; - int r; - - if (mm_size < l+1) { - size_t nn_size; - char *nn; - - nn_size = PAGE_ALIGN(l+1); - nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) - return log_debug_errno(errno, "mmap() failed: %m"); - - strncpy(nn, name, nn_size); - - /* Now, let's tell the kernel about this new memory */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - if (ERRNO_IS_PRIVILEGE(errno)) - return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); - - /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be - * below the desired start address, in which case the kernel may have kicked this back due - * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in - * action). The proper solution would be to have a prctl() API that could set both start+end - * simultaneously, or at least let us query the existing address to anticipate this condition - * and respond accordingly. For now, we can only guess at the cause of this failure and try - * a workaround--which will briefly expand the arg space to something potentially huge before - * resizing it to what we want. */ - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); - - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { - r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - return r; - } - - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) - return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); - } else { - /* And update the end pointer to the new end, too. If this fails, we don't really know what - * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, - * and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } - - if (mm) - (void) munmap(mm, mm_size); - - mm = nn; - mm_size = nn_size; - } else { - strncpy(mm, name, mm_size); - - /* Update the end pointer, continuing regardless of any failure. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } - - can_do = true; - return 0; -} - -int rename_process(const char name[]) { - bool truncated = false; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - size_t l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ - truncated = true; - - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } - - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ - (void) update_argv(name, l); - - /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if - * it still looks here */ - if (saved_argc > 0) { - if (saved_argv[0]) { - size_t k; - - k = strlen(saved_argv[0]); - strncpy(saved_argv[0], name, k); - if (l > k) - truncated = true; - } - - for (int i = 1; i < saved_argc; i++) { - if (!saved_argv[i]) - break; - - memzero(saved_argv[i], strlen(saved_argv[i])); - } - } - - return !truncated; -} - int is_kernel_thread(pid_t pid) { _cleanup_free_ char *line = NULL; unsigned long long flags; @@ -1627,40 +1482,6 @@ int setpriority_closest(int priority) { return 0; } -bool invoked_as(char *argv[], const char *token) { - if (!argv || isempty(argv[0])) - return false; - - if (isempty(token)) - return false; - - return strstr(last_path_component(argv[0]), token); -} - -bool invoked_by_systemd(void) { - int r; - - /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd, - * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */ - const char *e = getenv("SYSTEMD_EXEC_PID"); - if (!e) - return false; - - if (streq(e, "*")) - /* For testing. */ - return true; - - pid_t p; - r = parse_pid(e, &p); - if (r < 0) { - /* We know that systemd sets the variable correctly. Something else must have set it. */ - log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e); - return false; - } - - return getpid_cached() == p; -} - _noreturn_ void freeze(void) { log_close(); @@ -1682,31 +1503,6 @@ _noreturn_ void freeze(void) { pause(); } -bool argv_looks_like_help(int argc, char **argv) { - char **l; - - /* Scans the command line for indications the user asks for help. This is supposed to be called by - * tools that do not implement getopt() style command line parsing because they are not primarily - * user-facing. Detects four ways of asking for help: - * - * 1. Passing zero arguments - * 2. Passing "help" as first argument - * 3. Passing --help as any argument - * 4. Passing -h as any argument - */ - - if (argc <= 1) - return true; - - if (streq_ptr(argv[1], "help")) - return true; - - l = strv_skip(argv, 1); - - return strv_contains(l, "--help") || - strv_contains(l, "-h"); -} - static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", [CLD_KILLED] = "killed", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index d5986ad6469..83663670ac4 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -71,7 +71,6 @@ void sigterm_wait(pid_t pid); int kill_and_sigcont(pid_t pid, int sig); -int rename_process(const char name[]); int is_kernel_thread(pid_t pid); int getenv_for_pid(pid_t pid, const char *field, char **_value); @@ -191,10 +190,4 @@ int pidfd_get_pid(int fd, pid_t *ret); int setpriority_closest(int priority); -bool invoked_as(char *argv[], const char *token); - -bool invoked_by_systemd(void); - _noreturn_ void freeze(void); - -bool argv_looks_like_help(int argc, char **argv); diff --git a/src/basic/util.c b/src/basic/util.c deleted file mode 100644 index e5e61e66d89..00000000000 --- a/src/basic/util.c +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include -#include -#include - -#include "alloc-util.h" -#include "build.h" -#include "env-file.h" -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "util.h" -#include "virt.h" - -int saved_argc = 0; -char **saved_argv = NULL; diff --git a/src/basic/util.h b/src/basic/util.h index 95f71a66edd..646f2d3613a 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -5,20 +5,6 @@ #include "macro.h" -extern int saved_argc; -extern char **saved_argv; - -static inline void save_argc_argv(int argc, char **argv) { - - /* Protect against CVE-2021-4034 style attacks */ - assert_se(argc > 0); - assert_se(argv); - assert_se(argv[0]); - - saved_argc = argc; - saved_argv = argv; -} - /* Note: log2(0) == log2(1) == 0 here and below. */ #define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0) diff --git a/src/core/execute.c b/src/core/execute.c index b5b7de6d2a0..981bb63440c 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -39,6 +39,7 @@ #if HAVE_APPARMOR #include "apparmor-util.h" #endif +#include "argv-util.h" #include "async.h" #include "barrier.h" #include "bpf-lsm.h" diff --git a/src/core/main.c b/src/core/main.c index 672345368cc..4cf5dd146b3 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "apparmor-setup.h" #include "architecture.h" +#include "argv-util.h" #if HAVE_LIBBPF #include "bpf-lsm.h" #endif diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 85c439815c6..cea5f94b2e9 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -6,6 +6,7 @@ #include #include +#include "argv-util.h" #include "def.h" #include "exit-status.h" #include "fd-util.h" diff --git a/src/shared/main-func.h b/src/shared/main-func.h index 81a5c1813cb..3dc49922821 100644 --- a/src/shared/main-func.h +++ b/src/shared/main-func.h @@ -5,12 +5,12 @@ #include "sd-daemon.h" +#include "argv-util.h" #include "pager.h" #include "selinux-util.h" #include "spawn-ask-password-agent.h" #include "spawn-polkit-agent.h" #include "static-destruct.h" -#include "util.h" #define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \ int main(int argc, char *argv[]) { \ diff --git a/src/shared/tests.h b/src/shared/tests.h index a2f7f38c41e..cabd659fbdf 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -5,9 +5,9 @@ #include "sd-daemon.h" +#include "argv-util.h" #include "macro.h" #include "static-destruct.h" -#include "util.h" static inline bool manager_errno_skip_test(int r) { return IN_SET(abs(r), diff --git a/src/systemctl/systemctl-switch-root.c b/src/systemctl/systemctl-switch-root.c index 669fa60dfdd..8a7749be91d 100644 --- a/src/systemctl/systemctl-switch-root.c +++ b/src/systemctl/systemctl-switch-root.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "argv-util.h" #include "bus-error.h" #include "bus-locator.h" #include "parse-util.h" diff --git a/src/test/meson.build b/src/test/meson.build index 0c6f2db4283..413dcdec621 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -41,6 +41,8 @@ test_dlopen_c = files('test-dlopen.c') ############################################################ tests += [ + [files('test-argv-util.c')], + [files('test-device-nodes.c')], [files('test-ether-addr-util.c')], diff --git a/src/test/test-argv-util.c b/src/test/test-argv-util.c new file mode 100644 index 00000000000..26f6038534b --- /dev/null +++ b/src/test/test-argv-util.c @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#if HAVE_VALGRIND_VALGRIND_H +# include +#endif + +#include "argv-util.h" +#include "missing_sched.h" +#include "process-util.h" +#include "tests.h" +#include "virt.h" + +static void test_rename_process_now(const char *p, int ret) { + _cleanup_free_ char *comm = NULL, *cmdline = NULL; + int r; + + log_info("/* %s(%s) */", __func__, p); + + r = rename_process(p); + assert_se(r == ret || + (ret == 0 && r >= 0) || + (ret > 0 && r > 0)); + + log_debug_errno(r, "rename_process(%s): %m", p); + + if (r < 0) + return; + +#if HAVE_VALGRIND_VALGRIND_H + /* see above, valgrind is weird, we can't verify what we are doing here */ + if (RUNNING_ON_VALGRIND) + return; +#endif + + assert_se(get_process_comm(0, &comm) >= 0); + log_debug("comm = <%s>", comm); + assert_se(strneq(comm, p, TASK_COMM_LEN-1)); + /* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the + * future. We'd only check the initial part, at least until we recompile, but this will still pass. */ + + r = get_process_cmdline(0, SIZE_MAX, 0, &cmdline); + assert_se(r >= 0); + /* we cannot expect cmdline to be renamed properly without privileges */ + if (geteuid() == 0) { + if (r == 0 && detect_container() > 0) + log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline); + else { + log_info("cmdline = <%s> (expected <%.*s>)", cmdline, (int) strlen("test-process-util"), p); + + bool skip = cmdline[0] == '"'; /* A shortcut to check if the string is quoted */ + + assert_se(strneq(cmdline + skip, p, strlen("test-process-util"))); + assert_se(startswith(cmdline + skip, p)); + } + } else + log_info("cmdline = <%s> (not verified)", cmdline); +} + +static void test_rename_process_one(const char *p, int ret) { + siginfo_t si; + pid_t pid; + + log_info("/* %s(%s) */", __func__, p); + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) { + /* child */ + test_rename_process_now(p, ret); + _exit(EXIT_SUCCESS); + } + + assert_se(wait_for_terminate(pid, &si) >= 0); + assert_se(si.si_code == CLD_EXITED); + assert_se(si.si_status == EXIT_SUCCESS); +} + +TEST(rename_process_invalid) { + assert_se(rename_process(NULL) == -EINVAL); + assert_se(rename_process("") == -EINVAL); +} + +TEST(rename_process_multi) { + pid_t pid; + + pid = fork(); + assert_se(pid >= 0); + + if (pid > 0) { + siginfo_t si; + + assert_se(wait_for_terminate(pid, &si) >= 0); + assert_se(si.si_code == CLD_EXITED); + assert_se(si.si_status == EXIT_SUCCESS); + + return; + } + + /* child */ + test_rename_process_now("one", 1); + test_rename_process_now("more", 0); /* longer than "one", hence truncated */ + (void) setresuid(99, 99, 99); /* change uid when running privileged */ + test_rename_process_now("time!", 0); + test_rename_process_now("0", 1); /* shorter than "one", should fit */ + _exit(EXIT_SUCCESS); +} + +TEST(rename_process) { + test_rename_process_one("foo", 1); /* should always fit */ + test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */ + test_rename_process_one("1234567", 1); /* should always fit */ +} + +static int intro(void) { + log_show_color(true); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-netlink-manual.c b/src/test/test-netlink-manual.c index 49aca68c49b..6543c617c85 100644 --- a/src/test/test-netlink-manual.c +++ b/src/test/test-netlink-manual.c @@ -11,7 +11,6 @@ #include "macro.h" #include "module-util.h" #include "tests.h" -#include "util.h" static int load_module(const char *mod_name) { _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL; diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 990eb1e2b67..a9b2f537ca0 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -524,108 +524,6 @@ TEST(get_process_cmdline_harder) { _exit(EXIT_SUCCESS); } -static void test_rename_process_now(const char *p, int ret) { - _cleanup_free_ char *comm = NULL, *cmdline = NULL; - int r; - - log_info("/* %s(%s) */", __func__, p); - - r = rename_process(p); - assert_se(r == ret || - (ret == 0 && r >= 0) || - (ret > 0 && r > 0)); - - log_debug_errno(r, "rename_process(%s): %m", p); - - if (r < 0) - return; - -#if HAVE_VALGRIND_VALGRIND_H - /* see above, valgrind is weird, we can't verify what we are doing here */ - if (RUNNING_ON_VALGRIND) - return; -#endif - - assert_se(get_process_comm(0, &comm) >= 0); - log_debug("comm = <%s>", comm); - assert_se(strneq(comm, p, TASK_COMM_LEN-1)); - /* We expect comm to be at most 16 bytes (TASK_COMM_LEN). The kernel may raise this limit in the - * future. We'd only check the initial part, at least until we recompile, but this will still pass. */ - - r = get_process_cmdline(0, SIZE_MAX, 0, &cmdline); - assert_se(r >= 0); - /* we cannot expect cmdline to be renamed properly without privileges */ - if (geteuid() == 0) { - if (r == 0 && detect_container() > 0) - log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline); - else { - log_info("cmdline = <%s> (expected <%.*s>)", cmdline, (int) strlen("test-process-util"), p); - - bool skip = cmdline[0] == '"'; /* A shortcut to check if the string is quoted */ - - assert_se(strneq(cmdline + skip, p, strlen("test-process-util"))); - assert_se(startswith(cmdline + skip, p)); - } - } else - log_info("cmdline = <%s> (not verified)", cmdline); -} - -static void test_rename_process_one(const char *p, int ret) { - siginfo_t si; - pid_t pid; - - log_info("/* %s(%s) */", __func__, p); - - pid = fork(); - assert_se(pid >= 0); - - if (pid == 0) { - /* child */ - test_rename_process_now(p, ret); - _exit(EXIT_SUCCESS); - } - - assert_se(wait_for_terminate(pid, &si) >= 0); - assert_se(si.si_code == CLD_EXITED); - assert_se(si.si_status == EXIT_SUCCESS); -} - -TEST(rename_process_invalid) { - assert_se(rename_process(NULL) == -EINVAL); - assert_se(rename_process("") == -EINVAL); -} - -TEST(rename_process_multi) { - pid_t pid; - - pid = fork(); - assert_se(pid >= 0); - - if (pid > 0) { - siginfo_t si; - - assert_se(wait_for_terminate(pid, &si) >= 0); - assert_se(si.si_code == CLD_EXITED); - assert_se(si.si_status == EXIT_SUCCESS); - - return; - } - - /* child */ - test_rename_process_now("one", 1); - test_rename_process_now("more", 0); /* longer than "one", hence truncated */ - (void) setresuid(99, 99, 99); /* change uid when running privileged */ - test_rename_process_now("time!", 0); - test_rename_process_now("0", 1); /* shorter than "one", should fit */ - _exit(EXIT_SUCCESS); -} - -TEST(rename_process) { - test_rename_process_one("foo", 1); /* should always fit */ - test_rename_process_one("this is a really really long process name, followed by some more words", 0); /* unlikely to fit */ - test_rename_process_one("1234567", 1); /* should always fit */ -} - TEST(getpid_cached) { siginfo_t si; pid_t a, b, c, d, e, f, child;