From d3c0348c8b73102111ac40801b0e299c89435a3c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 10:12:07 +0200 Subject: [PATCH 1/7] process-util: introduce prctl_safe() --- src/basic/process-util.c | 12 ++++++++++++ src/basic/process-util.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 14e58e21eb..74f0d81b2a 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -2276,3 +2276,15 @@ int read_errno(int errno_fd) { return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received positive errno from child, refusing: %d", r); } + +int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { + + /* prctl(2) is a bit messy: it's a variadic function, defined with "unsigned long" arguments. This + * means that unless people explicitly cast it's quite likely they end up passing a shorter type even + * though unsigned long is required. And most of the time it might even kind of work, but not + * always. Moreover, some calls insist on all unused arguments being zeroed out, others don't + * care. Let's define this wrapper to enforce the right types, and that all arguments are always + * passed, to avoid this confusion. */ + + return RET_NERRNO(prctl(op, arg2, arg3, arg4, arg5)); +} diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 9d16055d5e..e2f834a744 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -255,3 +255,5 @@ int safe_mlockall(int flags); _noreturn_ void report_errno_and_exit(int errno_fd, int error); int read_errno(int errno_fd); + +int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); From 0e077d3eea41747e5d729038749da9909cf301cd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 10:32:41 +0200 Subject: [PATCH 2/7] process-util: introduce proc_set_comm() helper This operation is done at a bunch of places, let's add a type-safe helper for it. --- src/basic/argv-util.c | 6 ++++-- src/basic/process-util.c | 4 ++++ src/basic/process-util.h | 2 ++ src/core/main.c | 2 +- src/shared/async.c | 3 +-- src/test/test-process-util.c | 9 ++++----- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/basic/argv-util.c b/src/basic/argv-util.c index 0602193809..68cdea11ae 100644 --- a/src/basic/argv-util.c +++ b/src/basic/argv-util.c @@ -182,6 +182,7 @@ static int update_argv(const char name[], size_t l) { int rename_process_full(const char *comm, const char *invocation) { bool truncated = false; + int r; /* This is a like a poor man's setproctitle(). It changes the comm field by the name specified by * 'comm', and changes argv[0] and the glibc's internally used names of the process @@ -204,8 +205,9 @@ int rename_process_full(const char *comm, const char *invocation) { /* 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, comm) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); + r = proc_set_comm(comm); + if (r < 0) + log_debug_errno(r, "PR_SET_NAME failed: %m"); if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ truncated = true; diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 74f0d81b2a..1f7bdedc16 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -2288,3 +2288,7 @@ int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg return RET_NERRNO(prctl(op, arg2, arg3, arg4, arg5)); } + +int proc_set_comm(const char *comm) { + return prctl_safe(PR_SET_NAME, (unsigned long) comm, 0, 0, 0); +} diff --git a/src/basic/process-util.h b/src/basic/process-util.h index e2f834a744..0bdd8077b5 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -257,3 +257,5 @@ _noreturn_ void report_errno_and_exit(int errno_fd, int error); int read_errno(int errno_fd); int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); + +int proc_set_comm(const char *comm); diff --git a/src/core/main.c b/src/core/main.c index 2b4036381c..d67146a7ed 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -3567,7 +3567,7 @@ static int run_systemd(int argc, char *argv[]) { * reexecution we are then called 'systemd'. That is confusing, hence let's call us systemd * right-away. */ program_invocation_short_name = systemd; - (void) prctl(PR_SET_NAME, systemd); + (void) proc_set_comm(systemd); /* Save the original command line */ save_argc_argv(argc, argv); diff --git a/src/shared/async.c b/src/shared/async.c index f4843deb78..7aefe62bb1 100644 --- a/src/shared/async.c +++ b/src/shared/async.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include @@ -64,7 +63,7 @@ int asynchronous_fsync(int fd, PidRef *ret_pid) { static int close_func(void *p) { unsigned v = PTR_TO_UINT(p); - (void) prctl(PR_SET_NAME, (unsigned long*) "(sd-close)"); + (void) proc_set_comm("(sd-close)"); /* Note: 💣 This function is invoked in a child process created via glibc's clone() wrapper. In such * children memory allocation is not allowed, since glibc does not release malloc mutexes in diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 63458117cb..e1d16165ba 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -175,7 +174,7 @@ static void test_pid_get_comm_escape_one(const char *input, const char *output) log_debug("input: <%s> — output: <%s>", input, output); - ASSERT_OK_ERRNO(prctl(PR_SET_NAME, input)); + ASSERT_OK(proc_set_comm(input)); ASSERT_OK(pid_get_comm(0, &n)); log_debug("got: <%s>", n); @@ -199,7 +198,7 @@ TEST(pid_get_comm_escape) { test_pid_get_comm_escape_one("xxxxäöüß", "xxxx\\303\\244\\303\\266\\303\\274\\303\\237"); test_pid_get_comm_escape_one("xxxxxäöüß", "xxxxx\\303\\244\\303\\266\\303\\274\\303\\237"); - ASSERT_OK_ERRNO(prctl(PR_SET_NAME, saved)); + ASSERT_OK(proc_set_comm(saved)); } TEST(pid_is_unwaited) { @@ -314,7 +313,7 @@ TEST(pid_get_cmdline_harder) { ASSERT_OK_ERRNO(unlink(path)); - ASSERT_OK_ERRNO(prctl(PR_SET_NAME, "testa")); + ASSERT_OK(proc_set_comm("testa")); ASSERT_ERROR(pid_get_cmdline(0, SIZE_MAX, 0, &line), ENOENT); @@ -477,7 +476,7 @@ TEST(pid_get_cmdline_harder) { args = strv_free(args); ASSERT_OK_ERRNO(ftruncate(fd, 0)); - ASSERT_OK_ERRNO(prctl(PR_SET_NAME, "aaaa bbbb cccc")); + ASSERT_OK(proc_set_comm("aaaa bbbb cccc")); ASSERT_ERROR(pid_get_cmdline(0, SIZE_MAX, 0, &line), ENOENT); From 3ac3632e28870826d8d86f8d60495047ab29ecac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 10:48:05 +0200 Subject: [PATCH 3/7] process-util: introduce trivial proc_set_nnp() helper --- src/basic/process-util.c | 4 ++++ src/basic/process-util.h | 1 + src/core/exec-invoke.c | 8 +++++--- src/core/main.c | 5 +++-- src/import/import-common.c | 11 ++++++----- src/nspawn/nspawn.c | 8 +++++--- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 1f7bdedc16..076d403762 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -2292,3 +2292,7 @@ int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg int proc_set_comm(const char *comm) { return prctl_safe(PR_SET_NAME, (unsigned long) comm, 0, 0, 0); } + +int proc_set_nnp(void) { + return prctl_safe(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); +} diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 0bdd8077b5..0d956c1606 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -259,3 +259,4 @@ int read_errno(int errno_fd); int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); int proc_set_comm(const char *comm); +int proc_set_nnp(void); diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index b232cd015c..81ca984111 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -6479,11 +6479,13 @@ int exec_invoke( } } - if (context_has_no_new_privileges(context)) - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + if (context_has_no_new_privileges(context)) { + r = proc_set_nnp(); + if (r < 0) { *exit_status = EXIT_NO_NEW_PRIVILEGES; - return log_error_errno(errno, "Failed to disable new privileges: %m"); + return log_error_errno(r, "Failed to disable new privileges: %m"); } + } #if HAVE_SECCOMP r = apply_address_families(context, params); diff --git a/src/core/main.c b/src/core/main.c index d67146a7ed..663b6d6f5f 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -2739,9 +2739,10 @@ static int initialize_runtime( } if (arg_no_new_privs) { - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + r = proc_set_nnp(); + if (r < 0) { *ret_error_message = "Failed to disable new privileges"; - return log_struct_errno(LOG_EMERG, errno, + return log_struct_errno(LOG_EMERG, r, LOG_MESSAGE("Failed to disable new privileges: %m"), LOG_MESSAGE_ID(SD_MESSAGE_CORE_DISABLE_PRIVILEGES_STR)); } diff --git a/src/import/import-common.c b/src/import/import-common.c index 41e945f1fb..0168311204 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include "sd-event.h" @@ -81,8 +80,9 @@ int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid) { if (r < 0) log_debug_errno(r, "Failed to drop capabilities, ignoring: %m"); - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) - log_warning_errno(errno, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m"); + r = proc_set_nnp(); + if (r < 0) + log_warning_errno(r, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m"); if (tar_x(pipefd[0], tree_fd, flags) < 0) _exit(EXIT_FAILURE); @@ -141,8 +141,9 @@ int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) { if (r < 0) log_debug_errno(r, "Failed to drop capabilities, ignoring: %m"); - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) - log_warning_errno(errno, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m"); + r = proc_set_nnp(); + if (r < 0) + log_warning_errno(r, "Failed to enable PR_SET_NO_NEW_PRIVS, ignoring: %m"); if (tar_c(tree_fd, pipefd[1], /* filename= */ NULL, flags) < 0) _exit(EXIT_FAILURE); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index f4d85c0418..13d5436415 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3593,9 +3593,11 @@ static int inner_child( if (r < 0) return log_error_errno(r, "Dropping capabilities failed: %m"); - if (arg_no_new_privileges) - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) - return log_error_errno(errno, "Failed to disable new privileges: %m"); + if (arg_no_new_privileges) { + r = proc_set_nnp(); + if (r < 0) + return log_error_errno(r, "Failed to disable new privileges: %m"); + } /* LXC sets container=lxc, so follow the scheme here */ envp[n_env++] = strjoina("container=", arg_container_service_name); From 4d84f8fac34fd4e6de2187f34595f3db6b7fd143 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 11:20:03 +0200 Subject: [PATCH 4/7] process-util: introduce proc_get_timerslack() --- src/basic/process-util.c | 10 ++++++++++ src/basic/process-util.h | 1 + src/core/dbus-manager.c | 3 +-- src/core/varlink-manager.c | 5 ++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 076d403762..e342de287b 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -2296,3 +2296,13 @@ int proc_set_comm(const char *comm) { int proc_set_nnp(void) { return prctl_safe(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); } + +nsec_t proc_get_timerslack(void) { + int r; + + r = prctl_safe(PR_GET_TIMERSLACK, 0, 0, 0, 0); + if (r < 0) + return NSEC_INFINITY; + + return r; +} diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 0d956c1606..7adb94e8b0 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -260,3 +260,4 @@ int prctl_safe(int op, unsigned long arg2, unsigned long arg3, unsigned long arg int proc_set_comm(const char *comm); int proc_set_nnp(void); +nsec_t proc_get_timerslack(void); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 4b584038ba..24f0897da9 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include "alloc-util.h" @@ -66,7 +65,7 @@ static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_version, "s", GIT_VERSION); static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_features, "s", systemd_features); static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_architecture, "s", architecture_to_string(uname_architecture())); static BUS_DEFINE_PROPERTY_GET2(property_get_system_state, "s", Manager, manager_state, manager_state_to_string); -static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_timer_slack_nsec, "t", (uint64_t) prctl(PR_GET_TIMERSLACK)); +static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_timer_slack_nsec, "t", proc_get_timerslack()); static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "u", Hashmap *, hashmap_size); static BUS_DEFINE_PROPERTY_GET_REF(property_get_set_size, "u", Set *, set_size); static BUS_DEFINE_PROPERTY_GET(property_get_default_timeout_abort_usec, "t", Manager, manager_default_timeout_abort_usec); diff --git a/src/core/varlink-manager.c b/src/core/varlink-manager.c index b48d212475..6855ee7553 100644 --- a/src/core/varlink-manager.c +++ b/src/core/varlink-manager.c @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - #include "sd-bus.h" #include "sd-varlink.h" @@ -18,6 +16,7 @@ #include "manager.h" #include "path-util.h" #include "pidref.h" +#include "process-util.h" #include "selinux-access.h" #include "set.h" #include "strv.h" @@ -118,7 +117,7 @@ static int manager_context_build_json(sd_json_variant **ret, const char *name, v JSON_BUILD_PAIR_FINITE_USEC("RuntimeWatchdogPreUSec", manager_get_watchdog(m, WATCHDOG_PRETIMEOUT)), JSON_BUILD_PAIR_STRING_NON_EMPTY("RuntimeWatchdogPreGovernor", m->watchdog_pretimeout_governor), JSON_BUILD_PAIR_STRING_NON_EMPTY("WatchdogDevice", watchdog_get_device()), - JSON_BUILD_PAIR_FINITE_USEC("TimerSlackNSec", (uint64_t) prctl(PR_GET_TIMERSLACK)), + JSON_BUILD_PAIR_FINITE_USEC("TimerSlackNSec", proc_get_timerslack()), JSON_BUILD_PAIR_ENUM("DefaultOOMPolicy", oom_policy_to_string(m->defaults.oom_policy)), SD_JSON_BUILD_PAIR_INTEGER("DefaultOOMScoreAdjust", m->defaults.oom_score_adjust), SD_JSON_BUILD_PAIR_BOOLEAN("DefaultRestrictSUIDSGID", m->defaults.restrict_suid_sgid), From b484f983fcf85a5b5b7b492626a1432c3f7db5f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 10:18:04 +0200 Subject: [PATCH 5/7] memfd-util: port to pid_get_comm() Let's the common function for querying the local thread name. This changes the escaping rules when the therad name is not quite kosher: previously we'd just escape invalid UTF-8 charcaters, now we do what we usually do: also escape control characters and such, and limit us to ASCII. The description generated here is mostly for debug purposes, and process names should normally not require this escaping anyway (it's mostly paranoia), hence I think this change in behaviour should be fine, it's not part of the API in any form. --- src/basic/memfd-util.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c index 3257c1b9dd..54fecd9bdc 100644 --- a/src/basic/memfd-util.c +++ b/src/basic/memfd-util.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include #include -#include #include #include @@ -10,8 +8,8 @@ #include "errno-util.h" #include "fd-util.h" #include "memfd-util.h" +#include "process-util.h" #include "string-util.h" -#include "utf8.h" int memfd_create_wrapper(const char *name, unsigned mode) { unsigned mode_compat; @@ -36,26 +34,23 @@ int memfd_create_wrapper(const char *name, unsigned mode) { int memfd_new_full(const char *name, unsigned extra_flags) { _cleanup_free_ char *g = NULL; + int r; if (!name) { - char pr[TASK_COMM_LEN] = {}; /* If no name is specified we generate one. We include * a hint indicating our library implementation, and * add the thread name to it */ - assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); + _cleanup_free_ char *comm = NULL; + r = pid_get_comm(/* pid= */ 0, &comm); + if (r < 0) + return r; - if (isempty(pr)) + if (isempty(comm)) name = "sd"; else { - _cleanup_free_ char *e = NULL; - - e = utf8_escape_invalid(pr); - if (!e) - return -ENOMEM; - - g = strjoin("sd-", e); + g = strjoin("sd-", comm); if (!g) return -ENOMEM; From 2d639050964b519aa840107d227dcfd4c8a5d1c9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 10:42:28 +0200 Subject: [PATCH 6/7] nsresource: port from PR_GET_NAME to pid_get_comm() SImilar reasons as for the memfd change: let's normalize behaviour and always use pid_get_comm(). This adds escaping here for the first time, which is a good thing. --- src/shared/nsresource.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/shared/nsresource.c b/src/shared/nsresource.c index b783cb80b7..5aa87c48b5 100644 --- a/src/shared/nsresource.c +++ b/src/shared/nsresource.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include "sd-varlink.h" @@ -25,13 +24,15 @@ assert_cc(TASK_COMM_LEN == NAMESPACE_NAME_MAX); static int make_pid_name(char **ret) { - char comm[TASK_COMM_LEN]; static uint64_t counter = 0; + int r; assert(ret); - if (prctl(PR_GET_NAME, comm) < 0) - return -errno; + _cleanup_free_ char *comm = NULL; + r = pid_get_comm(0, &comm); + if (r < 0) + return r; char spid[DECIMAL_STR_MAX(pid_t)]; xsprintf(spid, PID_FMT, getpid_cached()); From 3b38a4f240c2d8fcb99f53d3486af1b97f681ea0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 13 Jul 2026 10:12:46 +0200 Subject: [PATCH 7/7] tree-wide: port to prctl_safe() --- src/basic/argv-util.c | 32 +++++---- src/basic/capability-util.c | 53 ++++++++------- src/basic/process-util.c | 28 ++++---- src/core/exec-invoke.c | 67 +++++++++++-------- src/core/execute.c | 8 ++- src/core/main.c | 10 +-- src/nspawn/nspawn-stub-pid1.c | 13 ++-- src/nspawn/nspawn.c | 20 +++--- src/shared/coredump-util.c | 6 +- src/shared/seccomp-util.c | 7 +- src/test/test-capability-util.c | 26 +++---- src/test/test-execute.c | 6 +- src/test/test-namespace.c | 6 +- src/test/test-thp.c | 5 +- .../tty-ask-password-agent.c | 4 +- 15 files changed, 164 insertions(+), 127 deletions(-) diff --git a/src/basic/argv-util.c b/src/basic/argv-util.c index 68cdea11ae..20587f66ad 100644 --- a/src/basic/argv-util.c +++ b/src/basic/argv-util.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include "argv-util.h" #include "capability-util.h" @@ -133,10 +133,10 @@ static int update_argv(const char name[], size_t l) { 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"); - + r = prctl_safe(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0); + if (ERRNO_IS_NEG_PRIVILEGE(r)) + return log_debug_errno(r, "PR_SET_MM_ARG_START failed: %m"); + if (r < 0) { /* 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 @@ -145,22 +145,25 @@ static int update_argv(const char name[], size_t l) { * 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"); + log_debug_errno(r, "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"); + r = prctl_safe(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0); + if (r < 0) { + log_debug_errno(r, "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"); + r = prctl_safe(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0); + if (r < 0) + return log_debug_errno(r, "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"); + r = prctl_safe(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0); + if (r < 0) + log_debug_errno(r, "PR_SET_MM_ARG_END failed, proceeding without: %m"); } if (mm) @@ -172,8 +175,9 @@ static int update_argv(const char name[], size_t l) { 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"); + r = prctl_safe(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0); + if (r < 0) + log_debug_errno(r, "PR_SET_MM_ARG_END failed, proceeding without: %m"); } can_do = true; diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 29434dc2ea..18942d4c36 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -2,7 +2,7 @@ #include #include -#include +#include /* IWYU pragma: keep */ #include #include @@ -100,18 +100,18 @@ unsigned cap_last_cap(void) { /* Fall back to syscall-probing for pre linux-3.2, or where /proc/ is not mounted */ unsigned long p = (unsigned long) MIN(CAP_LAST_CAP, CAP_LIMIT); - if (prctl(PR_CAPBSET_READ, p) < 0) { + if (prctl_safe(PR_CAPBSET_READ, p, 0, 0, 0) < 0) { /* Hmm, look downwards, until we find one that works */ for (p--; p > 0; p--) - if (prctl(PR_CAPBSET_READ, p) >= 0) + if (prctl_safe(PR_CAPBSET_READ, p, 0, 0, 0) >= 0) break; } else { /* Hmm, look upwards, until we find one that doesn't work */ for (; p < CAP_LIMIT; p++) - if (prctl(PR_CAPBSET_READ, p+1) < 0) + if (prctl_safe(PR_CAPBSET_READ, p+1, 0, 0, 0) < 0) break; } @@ -154,7 +154,10 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { if (!BIT_SET(set, i)) continue; - if (prctl(PR_CAPBSET_READ, (unsigned long) i) != 1) { + r = prctl_safe(PR_CAPBSET_READ, i, 0, 0, 0); + if (r < 0) + return r; + if (r != 1) { log_debug("Ambient capability %s requested but missing from bounding set, suppressing automatically.", capability_to_name(i)); CLEAR_BIT(set, i); @@ -180,16 +183,19 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) { for (unsigned i = 0; i <= cap_last_cap(); i++) if (BIT_SET(set, i)) { /* Add the capability to the ambient set. */ - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) - return -errno; + r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0); + if (r < 0) + return r; } else { /* Drop the capability so we don't inherit capabilities we didn't ask for. */ - r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); + r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); if (r < 0) - return -errno; - if (r > 0) - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0) - return -errno; + return r; + if (r > 0) { + r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0); + if (r < 0) + return r; + } } return 0; @@ -241,13 +247,12 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) { continue; /* Drop it from the bounding set */ - if (prctl(PR_CAPBSET_DROP, i) < 0) { - r = -errno; - + r = prctl_safe(PR_CAPBSET_DROP, i, 0, 0, 0); + if (r < 0) { /* If dropping the capability failed, let's see if we didn't have it in the first * place. If so, continue anyway, as dropping a capability we didn't have in the * first place doesn't really matter anyway. */ - if (prctl(PR_CAPBSET_READ, i) != 0) + if (prctl_safe(PR_CAPBSET_READ, i, 0, 0, 0) != 0) goto finish; } @@ -332,14 +337,16 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { /* Ensure we keep the permitted caps across the setresuid(). Note that we do this even if we actually * don't want to keep any capabilities, since we want to be able to drop them from the bounding set * too, and we can only do that if we have capabilities. */ - if (prctl(PR_SET_KEEPCAPS, 1) < 0) - return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); + r = prctl_safe(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to enable keep capabilities flag: %m"); if (setresuid(uid, uid, uid) < 0) return log_error_errno(errno, "Failed to change user ID: %m"); - if (prctl(PR_SET_KEEPCAPS, 0) < 0) - return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); + r = prctl_safe(PR_SET_KEEPCAPS, 0, 0, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to disable keep capabilities flag: %m"); /* Drop all caps from the bounding set (as well as the inheritable/permitted/effective sets), except * the ones we want to keep */ @@ -404,7 +411,7 @@ bool capability_quintet_mangle(CapabilityQuintet *q) { if (!BIT_SET(combined, i)) continue; - if (prctl(PR_CAPBSET_READ, (unsigned long) i) > 0) + if (prctl_safe(PR_CAPBSET_READ, i, 0, 0, 0) > 0) continue; SET_BIT(drop, i); @@ -515,9 +522,9 @@ int capability_get_ambient(uint64_t *ret) { assert(ret); for (unsigned i = 0; i <= cap_last_cap(); i++) { - r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); + r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); if (r < 0) - return -errno; + return r; if (r > 0) SET_BIT(a, i); } diff --git a/src/basic/process-util.c b/src/basic/process-util.c index e342de287b..ab563c5105 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -104,8 +104,9 @@ int pid_get_comm(pid_t pid, char **ret) { if (!comm) return -ENOMEM; - if (prctl(PR_GET_NAME, comm) < 0) - return -errno; + r = prctl_safe(PR_GET_NAME, (unsigned long) comm, 0, 0, 0); + if (r < 0) + return r; } else { const char *p; @@ -1572,11 +1573,13 @@ int pidref_safe_fork_full( if (!FLAGS_SET(flags, FORK_ALLOW_DLOPEN)) block_dlopen(); - if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) - if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) { - log_full_errno(prio, errno, "Failed to set death signal: %m"); + if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) { + r = prctl_safe(PR_SET_PDEATHSIG, fork_flags_to_signal(flags), 0, 0, 0); + if (r < 0) { + log_full_errno(prio, r, "Failed to set death signal: %m"); _exit(EXIT_FAILURE); } + } if (flags & FORK_RESET_SIGNALS) { r = reset_all_signal_handlers(); @@ -1978,7 +1981,7 @@ int get_process_threads(pid_t pid) { } int is_reaper_process(void) { - int b = 0; + int b = 0, r; /* Checks if we are running in a reaper process, i.e. if we are expected to deal with processes * reparented to us. This simply checks if we are PID 1 or if PR_SET_CHILD_SUBREAPER was called. */ @@ -1986,13 +1989,15 @@ int is_reaper_process(void) { if (getpid_cached() == 1) return true; - if (prctl(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0UL, 0UL, 0UL) < 0) - return -errno; + r = prctl_safe(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0, 0, 0); + if (r < 0) + return r; return b != 0; } int make_reaper_process(bool b) { + int r; if (getpid_cached() == 1) { @@ -2002,10 +2007,9 @@ int make_reaper_process(bool b) { return 0; } - /* Some prctl()s insist that all 5 arguments are specified, others do not. Let's always specify all, - * to avoid any ambiguities */ - if (prctl(PR_SET_CHILD_SUBREAPER, (unsigned long) b, 0UL, 0UL, 0UL) < 0) - return -errno; + r = prctl_safe(PR_SET_CHILD_SUBREAPER, b, 0, 0, 0); + if (r < 0) + return r; return 0; } diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 81ca984111..0faedf3dc1 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include @@ -983,19 +983,20 @@ static int enforce_groups(gid_t gid, const gid_t *supplementary_gids, int ngids) static int set_securebits(unsigned bits, unsigned mask) { unsigned applied; - int current; + int current, r; - current = prctl(PR_GET_SECUREBITS); + current = prctl_safe(PR_GET_SECUREBITS, 0, 0, 0, 0); if (current < 0) - return -errno; + return current; /* Clear all securebits defined in mask and set bits */ applied = ((unsigned) current & ~mask) | bits; if ((unsigned) current == applied) return 0; - if (prctl(PR_SET_SECUREBITS, applied) < 0) - return -errno; + r = prctl_safe(PR_SET_SECUREBITS, applied, 0, 0, 0); + if (r < 0) + return r; return 1; } @@ -1408,7 +1409,8 @@ static int setup_pam( /* Wait until our parent died. This will only work if the above setresuid() succeeds, * otherwise the kernel will not allow unprivileged parents kill their privileged children * this way. We rely on the control groups kill logic to do the rest for us. */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + r = prctl_safe(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0); + if (r < 0) goto child_finish; /* Tell the parent that our setup is done. This is especially important regarding dropping @@ -1714,13 +1716,13 @@ static int apply_memory_deny_write_execute(const ExecContext *c, const ExecParam return 0; /* use prctl() if kernel supports it (6.3) */ - r = prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0, 0, 0); + r = prctl_safe(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0, 0, 0); if (r == 0) { log_debug("Enabled MemoryDenyWriteExecute= with PR_SET_MDWE"); return 0; } - if (r < 0 && errno != EINVAL) - return log_debug_errno(errno, "Failed to enable MemoryDenyWriteExecute= with PR_SET_MDWE: %m"); + if (r < 0 && r != -EINVAL) + return log_debug_errno(r, "Failed to enable MemoryDenyWriteExecute= with PR_SET_MDWE: %m"); /* else use seccomp */ log_debug("Kernel doesn't support PR_SET_MDWE: falling back to seccomp"); @@ -4850,15 +4852,15 @@ static int set_memory_thp(ExecMemoryTHP thp) { return 0; case EXEC_MEMORY_THP_DISABLE: - r = RET_NERRNO(prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0)); + r = prctl_safe(PR_SET_THP_DISABLE, 1, 0, 0, 0); break; case EXEC_MEMORY_THP_MADVISE: - r = RET_NERRNO(prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0)); + r = prctl_safe(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0); break; case EXEC_MEMORY_THP_SYSTEM: - r = RET_NERRNO(prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0)); + r = prctl_safe(PR_SET_THP_DISABLE, 0, 0, 0, 0); break; default: @@ -5658,11 +5660,13 @@ int exec_invoke( return log_error_errno(errno, "Failed to set up IO scheduling priority: %m"); } - if (context->timer_slack_nsec != NSEC_INFINITY) - if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { + if (context->timer_slack_nsec != NSEC_INFINITY) { + r = prctl_safe(PR_SET_TIMERSLACK, context->timer_slack_nsec, 0, 0, 0); + if (r < 0) { *exit_status = EXIT_TIMERSLACK; - return log_error_errno(errno, "Failed to set up timer slack: %m"); + return log_error_errno(r, "Failed to set up timer slack: %m"); } + } if (context->personality != PERSONALITY_INVALID) { r = safe_personality(context->personality); @@ -5672,15 +5676,17 @@ int exec_invoke( } } - if (context->memory_ksm >= 0) - if (prctl(PR_SET_MEMORY_MERGE, context->memory_ksm, 0, 0, 0) < 0) { - if (ERRNO_IS_NOT_SUPPORTED(errno)) - log_debug_errno(errno, "KSM support not available, ignoring."); + if (context->memory_ksm >= 0) { + r = prctl_safe(PR_SET_MEMORY_MERGE, context->memory_ksm, 0, 0, 0); + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + log_debug_errno(r, "KSM support not available, ignoring."); else { *exit_status = EXIT_KSM; - return log_error_errno(errno, "Failed to set KSM: %m"); + return log_error_errno(r, "Failed to set KSM: %m"); } } + } r = set_memory_thp(context->memory_thp); if (r == -EOPNOTSUPP) @@ -6321,9 +6327,10 @@ int exec_invoke( seccomp_allows_drop_privileges(context)) { keep_seccomp_privileges = true; - if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + r = prctl_safe(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (r < 0) { *exit_status = EXIT_USER; - return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); + return log_error_errno(r, "Failed to enable keep capabilities flag: %m"); } /* Save the current bounding set so we can restore it after applying the seccomp @@ -6456,7 +6463,8 @@ int exec_invoke( /* PR_GET_SECUREBITS is not privileged, while PR_SET_SECUREBITS is. So to suppress potential * EPERMs we'll try not to call PR_SET_SECUREBITS unless necessary. Setting securebits * requires CAP_SETPCAP. */ - if (prctl(PR_GET_SECUREBITS) != secure_bits) { + r = prctl_safe(PR_GET_SECUREBITS, 0, 0, 0, 0); + if (r != secure_bits) { /* CAP_SETPCAP is required to set securebits. This capability is raised into the * effective set here. * @@ -6473,9 +6481,11 @@ int exec_invoke( *exit_status = EXIT_CAPABILITIES; return log_error_errno(r, "Failed to gain CAP_SETPCAP for setting secure bits"); } - if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) { + + r = prctl_safe(PR_SET_SECUREBITS, secure_bits, 0, 0, 0); + if (r < 0) { *exit_status = EXIT_SECUREBITS; - return log_error_errno(errno, "Failed to set process secure bits: %m"); + return log_error_errno(r, "Failed to set process secure bits: %m"); } } @@ -6615,9 +6625,10 @@ int exec_invoke( } } - if (prctl(PR_SET_KEEPCAPS, 0) < 0) { + r = prctl_safe(PR_SET_KEEPCAPS, 0, 0, 0, 0); + if (r < 0) { *exit_status = EXIT_USER; - return log_error_errno(errno, "Failed to drop keep capabilities flag: %m"); + return log_error_errno(r, "Failed to drop keep capabilities flag: %m"); } } #endif diff --git a/src/core/execute.c b/src/core/execute.c index f581b5c120..8eb505d4fb 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include #include @@ -1972,9 +1972,11 @@ uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c) { if (c->timer_slack_nsec != NSEC_INFINITY) return c->timer_slack_nsec; - r = prctl(PR_GET_TIMERSLACK); - if (r < 0) + r = prctl_safe(PR_GET_TIMERSLACK, 0, 0, 0, 0); + if (r < 0) { log_debug_errno(r, "Failed to get timer slack, ignoring: %m"); + return 0; + } return (uint64_t) MAX(r, 0); } diff --git a/src/core/main.c b/src/core/main.c index 663b6d6f5f..c952472e73 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include @@ -2795,9 +2795,11 @@ static int initialize_runtime( if (r < 0) log_warning_errno(r, "Failed to reset ambient capability set, ignoring: %m"); - if (arg_timer_slack_nsec != NSEC_INFINITY) - if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) - log_warning_errno(errno, "Failed to adjust timer slack, ignoring: %m"); + if (arg_timer_slack_nsec != NSEC_INFINITY) { + r = prctl_safe(PR_SET_TIMERSLACK, arg_timer_slack_nsec, 0, 0, 0); + if (r < 0) + log_warning_errno(r, "Failed to adjust timer slack, ignoring: %m"); + } if (arg_syscall_archs) { r = enforce_syscall_archs(arg_syscall_archs); diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 6a64aea511..2ed7bcffd4 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include +#include /* IWYU pragma: keep */ #include #include #include @@ -18,15 +18,18 @@ static int reset_environ(const char *new_environment, size_t length) { unsigned long start, end; + int r; start = (unsigned long) new_environment; end = start + length; - if (prctl(PR_SET_MM, PR_SET_MM_ENV_START, start, 0, 0) < 0) - return -errno; + r = prctl_safe(PR_SET_MM, PR_SET_MM_ENV_START, start, 0, 0); + if (r < 0) + return r; - if (prctl(PR_SET_MM, PR_SET_MM_ENV_END, end, 0, 0) < 0) - return -errno; + r = prctl_safe(PR_SET_MM, PR_SET_MM_ENV_END, end, 0, 0); + if (r < 0) + return r; return 0; } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 13d5436415..a2e0d31de7 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include @@ -79,6 +79,7 @@ #include "namespace-util.h" #include "netlink-internal.h" #include "notify-recv.h" +#include "nspawn.h" #include "nspawn-bind-user.h" #include "nspawn-cgroup.h" #include "nspawn-expose-ports.h" @@ -90,14 +91,13 @@ #include "nspawn-settings.h" #include "nspawn-setuid.h" #include "nspawn-stub-pid1.h" -#include "nspawn.h" #include "nsresource.h" -#include "os-util.h" -#include "parse-helpers.h" -#include "osc-context.h" #include "options.h" +#include "os-util.h" +#include "osc-context.h" #include "pager.h" #include "parse-argument.h" +#include "parse-helpers.h" #include "parse-util.h" #include "path-lookup.h" #include "path-util.h" @@ -3579,8 +3579,9 @@ static int inner_child( /* Make sure we keep the caps across the uid/gid dropping, so that we can retain some selected caps * if we need to later on. */ - if (prctl(PR_SET_KEEPCAPS, 1) < 0) - return log_error_errno(errno, "Failed to set PR_SET_KEEPCAPS: %m"); + r = prctl_safe(PR_SET_KEEPCAPS, 1, 0, 0, 0); + if (r < 0) + return log_error_errno(r, "Failed to set PR_SET_KEEPCAPS: %m"); if (uid_is_valid(arg_uid) || gid_is_valid(arg_gid)) r = change_uid_gid_raw(arg_uid, arg_gid, arg_supplementary_gids, arg_n_supplementary_gids, arg_console_mode != CONSOLE_PIPE); @@ -3963,8 +3964,9 @@ static int outer_child( if (r < 0) log_debug_errno(r, "Failed to read os-release from host for container, ignoring: %m"); - if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) - return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); + r = prctl_safe(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + if (r < 0) + return log_error_errno(r, "PR_SET_PDEATHSIG failed: %m"); r = reset_audit_loginuid(); if (r < 0) diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c index d979de1776..894cf3869b 100644 --- a/src/shared/coredump-util.c +++ b/src/shared/coredump-util.c @@ -1,15 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include +#include /* IWYU pragma: keep */ #include "alloc-util.h" #include "coredump-util.h" -#include "errno-util.h" #include "extract-word.h" #include "fileio.h" #include "log.h" #include "parse-util.h" +#include "process-util.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" @@ -18,7 +18,7 @@ int set_dumpable(SuidDumpMode mode) { /* Cast mode explicitly to long, because prctl wants longs but is varargs. */ - return RET_NERRNO(prctl(PR_SET_DUMPABLE, (long) mode)); + return prctl_safe(PR_SET_DUMPABLE, mode, 0, 0, 0); } static const char *const coredump_filter_table[_COREDUMP_FILTER_MAX] = { diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 2a8995666f..cf65e1ca0e 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include @@ -301,12 +301,11 @@ int seccomp_init_for_arch(scmp_filter_ctx *ret, uint32_t arch, uint32_t default_ } static bool is_basic_seccomp_available(void) { - return prctl(PR_GET_SECCOMP, 0, 0, 0, 0) >= 0; + return prctl_safe(PR_GET_SECCOMP, 0, 0, 0, 0) >= 0; } static bool is_seccomp_filter_available(void) { - return prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0) < 0 && - errno == EFAULT; + return prctl_safe(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 0, 0, 0) == -EFAULT; } bool is_seccomp_available(void) { diff --git a/src/test/test-capability-util.c b/src/test/test-capability-util.c index cd536dd4cb..ccf73d2bbf 100644 --- a/src/test/test-capability-util.c +++ b/src/test/test-capability-util.c @@ -3,10 +3,9 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include -#include "pidref.h" #define TEST_CAPABILITY_C @@ -17,6 +16,7 @@ #include "fd-util.h" #include "fileio.h" #include "parse-util.h" +#include "pidref.h" #include "process-util.h" #include "tests.h" @@ -64,13 +64,13 @@ TEST(last_cap_file) { TEST(last_cap_probe) { unsigned long p = (unsigned long)CAP_LAST_CAP; - if (prctl(PR_CAPBSET_READ, p) < 0) { + if (prctl_safe(PR_CAPBSET_READ, p, 0, 0, 0) < 0) { for (p--; p > 0; p--) - if (prctl(PR_CAPBSET_READ, p) >= 0) + if (prctl_safe(PR_CAPBSET_READ, p, 0, 0, 0) >= 0) break; } else { for (;; p++) - if (prctl(PR_CAPBSET_READ, p+1) < 0) + if (prctl_safe(PR_CAPBSET_READ, p+1, 0, 0, 0) < 0) break; } @@ -180,17 +180,17 @@ TEST(have_effective_cap) { } static void test_apply_ambient_caps_impl(void) { - ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 0); + ASSERT_OK_ZERO(prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0)); ASSERT_OK(capability_ambient_set_apply(UINT64_C(1) << CAP_CHOWN, /* also_inherit= */ true)); ASSERT_OK_POSITIVE(have_inheritable_cap(CAP_CHOWN)); - ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 1); + ASSERT_OK_EQ(prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 1); ASSERT_OK(capability_ambient_set_apply(0, /* also_inherit= */ true)); ASSERT_OK_ZERO(have_inheritable_cap(CAP_CHOWN)); - ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 0); + ASSERT_OK_ZERO(prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0)); } TEST(apply_ambient_caps) { @@ -234,10 +234,10 @@ TEST(capability_get_ambient) { ASSERT_OK(capability_get_ambient(&c)); - r = prctl(PR_CAPBSET_READ, CAP_MKNOD); + r = prctl_safe(PR_CAPBSET_READ, CAP_MKNOD, 0, 0, 0); if (r <= 0) return (void) log_tests_skipped("Lacking CAP_MKNOD, skipping getambient test."); - r = prctl(PR_CAPBSET_READ, CAP_LINUX_IMMUTABLE); + r = prctl_safe(PR_CAPBSET_READ, CAP_LINUX_IMMUTABLE, 0, 0, 0); if (r <= 0) return (void) log_tests_skipped("Lacking CAP_LINUX_IMMUTABLE, skipping getambient test."); @@ -303,6 +303,8 @@ TEST(pidref_get_capability) { } static int intro(void) { + int r; + /* Try to set up nobody user/ambient caps for tests that need them. * Not finding nobody is non-fatal — those tests will skip themselves. */ struct passwd *nobody = getpwnam(NOBODY_USER_NAME); @@ -311,10 +313,10 @@ static int intro(void) { test_gid = nobody->pw_gid; } - int r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); + r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); /* There's support for PR_CAP_AMBIENT if the prctl() call succeeded or error code was something else * than EINVAL. The EINVAL check should be good enough to rule out false positives. */ - run_ambient = r >= 0 || errno != EINVAL; + run_ambient = r >= 0 || r != -EINVAL; if (getuid() == 0) show_capabilities(); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 8206452f4c..9725fa0bf5 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include "sd-event.h" @@ -1195,8 +1195,8 @@ static void test_exec_ambientcapabilities(Manager *m) { * the tests only if that's the case. Clearing all ambient * capabilities is fine, since we are expecting them to be unset * in the first place for the tests. */ - r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); - if (r < 0 && IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS)) { + r = prctl_safe(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); + if (r < 0 && IN_SET(r, -EINVAL, -EOPNOTSUPP, -ENOSYS)) { log_notice("Skipping %s, the kernel does not support ambient capabilities", __func__); return; } diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index 41c67a3cf9..b9f4e35f2e 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include #include @@ -363,7 +363,7 @@ TEST(process_is_owned_by_uid) { ASSERT_OK(fully_set_uid_gid(1, 1, NULL, 0)); /* After successfully changing id/gid DEATHSIG is reset, so it has to be set again */ - ASSERT_OK_ERRNO(prctl(PR_SET_PDEATHSIG, SIGKILL)); + ASSERT_OK(prctl_safe(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)); ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1); p[1] = safe_close(p[1]); @@ -401,7 +401,7 @@ TEST(process_is_owned_by_uid) { ASSERT_OK(reset_uid_gid()); /* After successfully changing id/gid DEATHSIG is reset, so it has to be set again */ - ASSERT_OK_ERRNO(prctl(PR_SET_PDEATHSIG, SIGKILL)); + ASSERT_OK(prctl_safe(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)); ASSERT_OK_EQ_ERRNO(write(p[1], &(const char[]) { 'x' }, 1), 1); p[1] = safe_close(p[1]); diff --git a/src/test/test-thp.c b/src/test/test-thp.c index 047bd26e89..843c3c8560 100644 --- a/src/test/test-thp.c +++ b/src/test/test-thp.c @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include +#include /* IWYU pragma: keep */ +#include "process-util.h" #include "tests.h" #define PR_THP_DISABLE_NOT_SET 0 @@ -12,7 +13,7 @@ static const char *arg_mode = NULL; static int intro(void) { int r; - r = prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0); + r = prctl_safe(PR_GET_THP_DISABLE, 0, 0, 0, 0); if (streq_ptr(arg_mode, "no-disable")) { /* Test case: THPs should not be disabled */ if (r != PR_THP_DISABLE_NOT_SET) { diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index d57ade56c9..c725ef66a8 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include /* IWYU pragma: keep */ #include #include #include @@ -560,7 +560,7 @@ static int ask_on_this_console(const char *tty, char **arguments, PidRef *ret) { if (r < 0) return r; if (r == 0) { - assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); + assert_se(prctl_safe(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0) >= 0); STRV_FOREACH(i, arguments) { char *k;