tree-wide: port to prctl_safe()

This commit is contained in:
Lennart Poettering
2026-07-13 15:39:17 +02:00
parent 2d63905096
commit 3b38a4f240
15 changed files with 164 additions and 127 deletions
+18 -14
View File
@@ -3,7 +3,7 @@
#include <sched.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/prctl.h> /* 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;
+30 -23
View File
@@ -2,7 +2,7 @@
#include <stdatomic.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/prctl.h> /* IWYU pragma: keep */
#include <sys/syscall.h>
#include <unistd.h>
@@ -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);
}
+16 -12
View File
@@ -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;
}