shared/seccomp: add seccomp_restrict_ptrace()

PTRACE_POKETEXT and PTRACE_POKEDATA use FOLL_FORCE to forcibly write
into a tracee's memory. That includes executable read-only pages. If we
restrict execution to trusted binaries PID 1 must enforce that it's
impossible to abuse this to get untrusted code execution. Note, that
usermodehelpers are not subject to PID 1's seccomp filters.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner
2026-08-31 23:33:40 +02:00
parent 9bb5d21c15
commit 76d0ec8b97
2 changed files with 47 additions and 0 deletions
+42
View File
@@ -9,6 +9,7 @@
#include <sched.h>
#include <sys/mman.h>
#include <sys/prctl.h> /* IWYU pragma: keep */
#include <sys/ptrace.h>
#include <sys/shm.h>
#include <sys/stat.h>
@@ -2152,6 +2153,47 @@ int seccomp_filter_set_add(Hashmap *filter, bool add, const SyscallFilterSet *se
return 0;
}
/* Block PTRACE_POKE{TEXT,DATA} FOLL_FORCE writes. A missing filter is a bypass, so unlike the other helpers
* this fails when the native architecture cannot be filtered. A secondary architecture the kernel refuses a
* filter for is skipped as everywhere else: it cannot run binaries either. */
int seccomp_restrict_ptrace(void) {
uint32_t arch;
int r;
r = dlopen_libseccomp(LOG_DEBUG);
if (r < 0)
return r;
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(ptrace),
1, SCMP_A0(SCMP_CMP_EQ, PTRACE_POKETEXT));
if (r < 0)
return r;
r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(ptrace),
1, SCMP_A0(SCMP_CMP_EQ, PTRACE_POKEDATA));
if (r < 0)
return r;
r = sym_seccomp_load(seccomp);
if (r < 0) {
if (ERRNO_IS_NEG_SECCOMP_FATAL(r) || arch == sym_seccomp_arch_native())
return log_debug_errno(r, "Failed to apply ptrace restrictions for architecture %s: %m",
seccomp_arch_to_string(arch));
log_debug_errno(r, "Failed to apply ptrace restrictions for architecture %s, skipping: %m",
seccomp_arch_to_string(arch));
}
}
return 0;
}
int seccomp_lock_personality(unsigned long personality) {
uint32_t arch;
int r;
+5
View File
@@ -122,6 +122,7 @@ static inline int seccomp_restrict_realtime(void) {
return seccomp_restrict_realtime_full(EPERM);
}
int seccomp_memory_deny_write_execute(void);
int seccomp_restrict_ptrace(void);
int seccomp_lock_personality(unsigned long personality);
int seccomp_protect_hostname(void);
int seccomp_restrict_suid_sgid(void);
@@ -166,6 +167,10 @@ static inline bool is_seccomp_available(void) {
return false;
}
static inline int seccomp_restrict_ptrace(void) {
return -EOPNOTSUPP;
}
#endif
int dlopen_libseccomp(int log_level) _dlopen_loader_;