From 62e665cacc3885e2418e6a778246e8aac93f3fe3 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 25 Aug 2026 14:38:13 +0200 Subject: [PATCH] core: check that proc_mem.force_override=never is active The kernel silently ignores command line options it doesn't know. So a kernel without CONFIG_PROC_MEM_* would pass the command line check while still allowing FOLL_FORCE memory overwrites. Write one byte through /proc/self/mem into a private read-only anonymous page. That can only succeed with FOLL_FORCE writes supported. Signed-off-by: Christian Brauner (Amutable) --- src/core/bpf-restrict-fsaccess.c | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/core/bpf-restrict-fsaccess.c b/src/core/bpf-restrict-fsaccess.c index dad7be2a17..d0ab080e15 100644 --- a/src/core/bpf-restrict-fsaccess.c +++ b/src/core/bpf-restrict-fsaccess.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "alloc-util.h" #include "bpf-restrict-fsaccess.h" @@ -111,6 +112,38 @@ static int proc_mem_force_override_restricted(void) { return streq(value, "never"); } +/* Unknown command line options are silently ignored, so verify the kernel really refuses FOLL_FORCE + * writes. Returns > 0 if a write through /proc/self/mem overrode a read-only mapping, 0 if it was refused. */ +static int proc_mem_can_force_override(void) { + _cleanup_close_ int fd = -EBADF; + const uint8_t zero = 0; + ssize_t n; + void *p; + int r; + + p = mmap(/* addr= */ NULL, page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, /* fd= */ -1, /* offset= */ 0); + if (p == MAP_FAILED) + return -errno; + + fd = open("/proc/self/mem", O_RDWR|O_CLOEXEC); + if (fd < 0) { + r = -errno; + goto finish; + } + + /* Without FOLL_FORCE access_remote_vm() copies nothing and mem_rw() fails with EIO. */ + n = pwrite(fd, &zero, sizeof(zero), (off_t) (uintptr_t) p); + if (n < 0) + r = errno == EIO ? 0 : -errno; + else + r = n > 0 ? 1 : -EIO; /* Nothing written and no error is not a refusal, fail closed. */ + +finish: + (void) munmap(p, page_size()); + return r; +} + +/* Both /proc//mem gates: the command line says "never", and the kernel honours it. */ static int restrict_fsaccess_check_proc_mem(void) { int r; @@ -125,6 +158,18 @@ static int restrict_fsaccess_check_proc_mem(void) { "command line, or boot with systemd.restrict_filesystem_access=no to disable " "the policy."); + r = proc_mem_can_force_override(); + if (r < 0) + return log_error_errno(r, "bpf-restrict-fsaccess: Failed to probe whether /proc/self/mem overrides " + "memory protections: %m"); + if (r > 0) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), + "bpf-restrict-fsaccess: proc_mem.force_override=never is on the kernel command " + "line, but a write through /proc/self/mem still overrides memory protections. " + "The kernel apparently does not support the option (Linux 6.12 or newer is " + "required). Boot with systemd.restrict_filesystem_access=no to disable the " + "policy."); + return 0; }