#include "alloc-util.h"
#include "macro.h"
#include "nsflags.h"
+#include "process-util.h"
#include "seccomp-util.h"
+#include "set.h"
#include "string-util.h"
+#include "strv.h"
#include "util.h"
#include "errno-list.h"
"execve\0"
"exit\0"
"exit_group\0"
+ "futex\0"
+ "get_robust_list\0"
+ "get_thread_area\0"
+ "getegid\0"
+ "getegid32\0"
+ "geteuid\0"
+ "geteuid32\0"
+ "getgid\0"
+ "getgid32\0"
+ "getgroups\0"
+ "getgroups32\0"
+ "getpgid\0"
+ "getpgrp\0"
+ "getpid\0"
+ "getppid\0"
+ "getresgid\0"
+ "getresgid32\0"
+ "getresuid\0"
+ "getresuid32\0"
"getrlimit\0" /* make sure processes can query stack size and such */
+ "getsid\0"
+ "gettid\0"
"gettimeofday\0"
+ "getuid\0"
+ "getuid32\0"
+ "membarrier\0"
"nanosleep\0"
"pause\0"
+ "prlimit64\0"
+ "restart_syscall\0"
"rt_sigreturn\0"
+ "sched_yield\0"
+ "set_robust_list\0"
+ "set_thread_area\0"
+ "set_tid_address\0"
"sigreturn\0"
"time\0"
+ "ugetrlimit\0"
+ },
+ [SYSCALL_FILTER_SET_AIO] = {
+ .name = "@aio",
+ .help = "Asynchronous IO",
+ .value =
+ "io_cancel\0"
+ "io_destroy\0"
+ "io_getevents\0"
+ "io_setup\0"
+ "io_submit\0"
},
[SYSCALL_FILTER_SET_BASIC_IO] = {
.name = "@basic-io",
.help = "Basic IO",
.value =
+ "_llseek\0"
"close\0"
+ "dup\0"
"dup2\0"
"dup3\0"
- "dup\0"
"lseek\0"
"pread64\0"
"preadv\0"
+ "preadv2\0"
"pwrite64\0"
"pwritev\0"
+ "pwritev2\0"
"read\0"
"readv\0"
"write\0"
"writev\0"
},
+ [SYSCALL_FILTER_SET_CHOWN] = {
+ .name = "@chown",
+ .help = "Change ownership of files and directories",
+ .value =
+ "chown\0"
+ "chown32\0"
+ "fchown\0"
+ "fchown32\0"
+ "fchownat\0"
+ "lchown\0"
+ "lchown32\0"
+ },
[SYSCALL_FILTER_SET_CLOCK] = {
.name = "@clock",
.help = "Change the system time",
"fchdir\0"
"fchmod\0"
"fchmodat\0"
- "fcntl64\0"
"fcntl\0"
+ "fcntl64\0"
"fgetxattr\0"
"flistxattr\0"
+ "fremovexattr\0"
"fsetxattr\0"
- "fstat64\0"
"fstat\0"
+ "fstat64\0"
"fstatat64\0"
- "fstatfs64\0"
"fstatfs\0"
- "ftruncate64\0"
+ "fstatfs64\0"
"ftruncate\0"
+ "ftruncate64\0"
"futimesat\0"
"getcwd\0"
- "getdents64\0"
"getdents\0"
+ "getdents64\0"
"getxattr\0"
"inotify_add_watch\0"
+ "inotify_init\0"
"inotify_init1\0"
"inotify_rm_watch\0"
"lgetxattr\0"
"llistxattr\0"
"lremovexattr\0"
"lsetxattr\0"
- "lstat64\0"
"lstat\0"
+ "lstat64\0"
"mkdir\0"
"mkdirat\0"
"mknod\0"
"mknodat\0"
- "mmap2\0"
"mmap\0"
+ "mmap2\0"
"munmap\0"
"newfstatat\0"
+ "oldfstat\0"
+ "oldlstat\0"
+ "oldstat\0"
"open\0"
"openat\0"
"readlink\0"
"readlinkat\0"
"removexattr\0"
"rename\0"
- "renameat2\0"
"renameat\0"
+ "renameat2\0"
"rmdir\0"
"setxattr\0"
- "stat64\0"
"stat\0"
+ "stat64\0"
"statfs\0"
+ "statfs64\0"
+#ifdef __PNR_statx
+ "statx\0"
+#endif
"symlink\0"
"symlinkat\0"
- "truncate64\0"
"truncate\0"
+ "truncate64\0"
"unlink\0"
"unlinkat\0"
+ "utime\0"
"utimensat\0"
"utimes\0"
},
.help = "Event loop system calls",
.value =
"_newselect\0"
- "epoll_create1\0"
"epoll_create\0"
+ "epoll_create1\0"
"epoll_ctl\0"
"epoll_ctl_old\0"
"epoll_pwait\0"
"epoll_wait\0"
"epoll_wait_old\0"
- "eventfd2\0"
"eventfd\0"
+ "eventfd2\0"
"poll\0"
"ppoll\0"
"pselect6\0"
"msgget\0"
"msgrcv\0"
"msgsnd\0"
- "pipe2\0"
"pipe\0"
+ "pipe2\0"
"process_vm_readv\0"
"process_vm_writev\0"
"semctl\0"
"keyctl\0"
"request_key\0"
},
+ [SYSCALL_FILTER_SET_MEMLOCK] = {
+ .name = "@memlock",
+ .help = "Memory locking control",
+ .value =
+ "mlock\0"
+ "mlock2\0"
+ "mlockall\0"
+ "munlock\0"
+ "munlockall\0"
+ },
[SYSCALL_FILTER_SET_MODULE] = {
.name = "@module",
.help = "Loading and unloading of kernel modules",
"chroot\0"
"mount\0"
"pivot_root\0"
- "umount2\0"
"umount\0"
+ "umount2\0"
},
[SYSCALL_FILTER_SET_NETWORK_IO] = {
.name = "@network-io",
.help = "Network or Unix socket IO, should not be needed if not network facing",
.value =
- "accept4\0"
"accept\0"
+ "accept4\0"
"bind\0"
"connect\0"
"getpeername\0"
"get_kernel_syms\0"
"getpmsg\0"
"gtty\0"
+ "idle\0"
"lock\0"
"mpx\0"
"prof\0"
.name = "@privileged",
.help = "All system calls which need super-user capabilities",
.value =
+ "@chown\0"
"@clock\0"
"@module\0"
"@raw-io\0"
+ "_sysctl\0"
"acct\0"
"bpf\0"
"capset\0"
- "chown32\0"
- "chown\0"
"chroot\0"
- "fchown32\0"
- "fchown\0"
- "fchownat\0"
"kexec_file_load\0"
"kexec_load\0"
- "lchown32\0"
- "lchown\0"
"nfsservctl\0"
"pivot_root\0"
"quotactl\0"
"reboot\0"
"setdomainname\0"
- "setfsuid32\0"
"setfsuid\0"
- "setgroups32\0"
+ "setfsuid32\0"
"setgroups\0"
+ "setgroups32\0"
"sethostname\0"
- "setresuid32\0"
"setresuid\0"
- "setreuid32\0"
+ "setresuid32\0"
"setreuid\0"
- "setuid32\0"
+ "setreuid32\0"
"setuid\0"
+ "setuid32\0"
"swapoff\0"
"swapon\0"
- "_sysctl\0"
"vhangup\0"
},
[SYSCALL_FILTER_SET_PROCESS] = {
.help = "Process control, execution, namespaceing operations",
.value =
"arch_prctl\0"
+ "capget\0" /* Able to query arbitrary processes */
"clone\0"
"execveat\0"
"fork\0"
+ "getrusage\0"
"kill\0"
"prctl\0"
+ "rt_sigqueueinfo\0"
+ "rt_tgsigqueueinfo\0"
"setns\0"
"tgkill\0"
+ "times\0"
"tkill\0"
"unshare\0"
"vfork\0"
+ "wait4\0"
+ "waitid\0"
+ "waitpid\0"
},
[SYSCALL_FILTER_SET_RAW_IO] = {
.name = "@raw-io",
.name = "@resources",
.help = "Alter resource settings",
.value =
+ "ioprio_set\0"
+ "mbind\0"
+ "migrate_pages\0"
+ "move_pages\0"
+ "nice\0"
+ "sched_setaffinity\0"
+ "sched_setattr\0"
"sched_setparam\0"
"sched_setscheduler\0"
- "sched_setaffinity\0"
+ "set_mempolicy\0"
"setpriority\0"
"setrlimit\0"
- "set_mempolicy\0"
- "migrate_pages\0"
- "move_pages\0"
- "mbind\0"
- "sched_setattr\0"
- "prlimit64\0"
+ },
+ [SYSCALL_FILTER_SET_SETUID] = {
+ .name = "@setuid",
+ .help = "Operations for changing user/group credentials",
+ .value =
+ "setgid\0"
+ "setgid32\0"
+ "setgroups\0"
+ "setgroups32\0"
+ "setregid\0"
+ "setregid32\0"
+ "setresgid\0"
+ "setresgid32\0"
+ "setresuid\0"
+ "setresuid32\0"
+ "setreuid\0"
+ "setreuid32\0"
+ "setuid\0"
+ "setuid32\0"
+ },
+ [SYSCALL_FILTER_SET_SIGNAL] = {
+ .name = "@signal",
+ .help = "Process signal handling",
+ .value =
+ "rt_sigaction\0"
+ "rt_sigpending\0"
+ "rt_sigprocmask\0"
+ "rt_sigsuspend\0"
+ "rt_sigtimedwait\0"
+ "sigaction\0"
+ "sigaltstack\0"
+ "signal\0"
+ "signalfd\0"
+ "signalfd4\0"
+ "sigpending\0"
+ "sigprocmask\0"
+ "sigsuspend\0"
},
[SYSCALL_FILTER_SET_SWAP] = {
.name = "@swap",
"swapoff\0"
"swapon\0"
},
+ [SYSCALL_FILTER_SET_SYNC] = {
+ .name = "@sync",
+ .help = "Synchronize files and memory to storage",
+ .value =
+ "fdatasync\0"
+ "fsync\0"
+ "msync\0"
+ "sync\0"
+ "sync_file_range\0"
+ "syncfs\0"
+ },
+ [SYSCALL_FILTER_SET_TIMER] = {
+ .name = "@timer",
+ .help = "Schedule operations by time",
+ .value =
+ "alarm\0"
+ "getitimer\0"
+ "setitimer\0"
+ "timer_create\0"
+ "timer_delete\0"
+ "timer_getoverrun\0"
+ "timer_gettime\0"
+ "timer_settime\0"
+ "timerfd_create\0"
+ "timerfd_gettime\0"
+ "timerfd_settime\0"
+ "times\0"
+ },
};
const SyscallFilterSet *syscall_filter_set_find(const char *name) {
return NULL;
}
+static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude);
+
+int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude) {
+ int r;
+
+ assert(seccomp);
+ assert(name);
+
+ if (strv_contains(exclude, name))
+ return 0;
+
+ if (name[0] == '@') {
+ const SyscallFilterSet *other;
+
+ other = syscall_filter_set_find(name);
+ if (!other) {
+ log_debug("Filter set %s is not known!", name);
+ return -EINVAL;
+ }
+
+ r = seccomp_add_syscall_filter_set(seccomp, other, action, exclude);
+ if (r < 0)
+ return r;
+ } else {
+ int id;
+
+ id = seccomp_syscall_resolve_name(name);
+ if (id == __NR_SCMP_ERROR) {
+ log_debug("System call %s is not known, ignoring.", name);
+ return 0;
+ }
+
+ r = seccomp_rule_add_exact(seccomp, action, id, 0);
+ if (r < 0)
+ /* If the system call is not known on this architecture, then that's fine, let's ignore it */
+ log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", name, id);
+ }
+
+ return 0;
+}
+
static int seccomp_add_syscall_filter_set(
scmp_filter_ctx seccomp,
- uint32_t default_action,
const SyscallFilterSet *set,
- uint32_t action) {
+ uint32_t action,
+ char **exclude) {
const char *sys;
int r;
assert(set);
NULSTR_FOREACH(sys, set->value) {
- int id;
-
- if (sys[0] == '@') {
- const SyscallFilterSet *other;
-
- other = syscall_filter_set_find(sys);
- if (!other)
- return -EINVAL;
-
- r = seccomp_add_syscall_filter_set(seccomp, default_action, other, action);
- if (r < 0)
- return r;
- } else {
- id = seccomp_syscall_resolve_name(sys);
- if (id == __NR_SCMP_ERROR)
- return -EINVAL; /* Not known at all? Then that's a real error */
-
- r = seccomp_rule_add_exact(seccomp, action, id, 0);
- if (r < 0)
- /* If the system call is not known on this architecture, then that's fine, let's ignore it */
- log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", sys);
- }
+ r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude);
+ if (r < 0)
+ return r;
}
return 0;
if (r < 0)
return r;
- r = seccomp_add_syscall_filter_set(seccomp, default_action, set, action);
+ r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to add filter set, ignoring: %m");
continue;
_cleanup_free_ char *n = NULL;
n = seccomp_syscall_resolve_num_arch(arch, PTR_TO_INT(id) - 1);
- log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", strna(n));
+ log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", strna(n), PTR_TO_INT(id) - 1);
}
}
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
- int clone_reversed_order = -1;
unsigned i;
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
- switch (arch) {
-
- case SCMP_ARCH_X86_64:
- case SCMP_ARCH_X86:
- case SCMP_ARCH_X32:
- case SCMP_ARCH_PPC64:
- case SCMP_ARCH_PPC64LE:
- clone_reversed_order = 0;
- break;
-
- case SCMP_ARCH_S390:
- case SCMP_ARCH_S390X:
- /* On s390/s390x the first two parameters to clone are switched */
- clone_reversed_order = 1;
- break;
-
- /* Please add more definitions here, if you port systemd to other architectures! */
-
-#if SECCOMP_RESTRICT_NAMESPACES_BROKEN
-# warning "Consider adding the right clone() syscall definitions here!"
-#endif
- }
-
- if (clone_reversed_order < 0) /* we don't know the right order, let's ignore this arch... */
- continue;
-
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
break;
}
- if (clone_reversed_order == 0)
+ /* On s390/s390x the first two parameters to clone are switched */
+ if (!IN_SET(arch, SCMP_ARCH_S390, SCMP_ARCH_S390X))
r = seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
+ if (IN_SET(arch, SCMP_ARCH_X32, SCMP_ARCH_AARCH64))
+ /* No _sysctl syscall */
+ continue;
+
r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
if (r < 0)
return r;
case SCMP_ARCH_X32:
case SCMP_ARCH_ARM:
case SCMP_ARCH_AARCH64:
+ case SCMP_ARCH_PPC64:
+ case SCMP_ARCH_PPC64LE:
/* These we know we support (i.e. are the ones that do not use socketcall()) */
supported = true;
break;
- case SCMP_ARCH_X86:
case SCMP_ARCH_S390:
case SCMP_ARCH_S390X:
case SCMP_ARCH_PPC:
- case SCMP_ARCH_PPC64:
- case SCMP_ARCH_PPC64LE:
+ case SCMP_ARCH_X86:
default:
/* These we either know we don't support (i.e. are the ones that do use socketcall()), or we
* don't know */
if (r < 0)
break;
}
-
if (r < 0) {
log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
continue;
if (r < 0)
break;
}
-
if (r < 0) {
log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
continue;
return 0;
}
+static int add_seccomp_syscall_filter(scmp_filter_ctx seccomp,
+ uint32_t arch,
+ int nr,
+ unsigned int arg_cnt,
+ const struct scmp_arg_cmp arg) {
+ int r;
+
+ r = seccomp_rule_add_exact(seccomp, SCMP_ACT_ERRNO(EPERM), nr, arg_cnt, arg);
+ if (r < 0) {
+ _cleanup_free_ char *n = NULL;
+
+ n = seccomp_syscall_resolve_num_arch(arch, nr);
+ log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m",
+ strna(n),
+ seccomp_arch_to_string(arch));
+ }
+
+ return r;
+}
+
+/* For known architectures, check that syscalls are indeed defined or not. */
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+assert_cc(SCMP_SYS(shmget) > 0);
+assert_cc(SCMP_SYS(shmat) > 0);
+assert_cc(SCMP_SYS(shmdt) > 0);
+#elif defined(__i386__) || defined(__powerpc64__)
+assert_cc(SCMP_SYS(shmget) < 0);
+assert_cc(SCMP_SYS(shmat) < 0);
+assert_cc(SCMP_SYS(shmdt) < 0);
+#endif
+
int seccomp_memory_deny_write_execute(void) {
uint32_t arch;
case SCMP_ARCH_X86:
filter_syscall = SCMP_SYS(mmap2);
block_syscall = SCMP_SYS(mmap);
+ break;
- /* Note that shmat() isn't available on i386, where the call is multiplexed through ipc(). We
- * ignore that here, which means there's still a way to get writable/executable memory, if an
- * IPC key is mapped like this on i386. That's a pity, but no total loss. */
+ case SCMP_ARCH_PPC64:
+ case SCMP_ARCH_PPC64LE:
+ filter_syscall = SCMP_SYS(mmap);
+
+ /* Note that shmat() isn't available, and the call is multiplexed through ipc().
+ * We ignore that here, which means there's still a way to get writable/executable
+ * memory, if an IPC key is mapped like this. That's a pity, but no total loss. */
+
+ break;
+
+ case SCMP_ARCH_ARM:
+ filter_syscall = SCMP_SYS(mmap2); /* arm has only mmap2 */
+ shmat_syscall = SCMP_SYS(shmat);
break;
case SCMP_ARCH_X86_64:
case SCMP_ARCH_X32:
- filter_syscall = SCMP_SYS(mmap);
+ case SCMP_ARCH_AARCH64:
+ filter_syscall = SCMP_SYS(mmap); /* amd64, x32, and arm64 have only mmap */
shmat_syscall = SCMP_SYS(shmat);
break;
/* Please add more definitions here, if you port systemd to other architectures! */
-#if !defined(__i386__) && !defined(__x86_64__)
+#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__)
#warning "Consider adding the right mmap() syscall definitions here!"
#endif
}
if (r < 0)
return r;
- if (filter_syscall != 0) {
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- filter_syscall,
- 1,
- SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE));
- if (r < 0) {
- _cleanup_free_ char *n = NULL;
-
- n = seccomp_syscall_resolve_num_arch(arch, filter_syscall);
- log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m",
- strna(n),
- seccomp_arch_to_string(arch));
- continue;
- }
- }
+ r = add_seccomp_syscall_filter(seccomp, arch, filter_syscall,
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE));
+ if (r < 0)
+ continue;
if (block_syscall != 0) {
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- block_syscall,
- 0);
- if (r < 0) {
- _cleanup_free_ char *n = NULL;
-
- n = seccomp_syscall_resolve_num_arch(arch, block_syscall);
- log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m",
- strna(n),
- seccomp_arch_to_string(arch));
+ r = add_seccomp_syscall_filter(seccomp, arch, block_syscall, 0, (const struct scmp_arg_cmp){} );
+ if (r < 0)
continue;
- }
}
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- SCMP_SYS(mprotect),
- 1,
- SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC));
- if (r < 0) {
- log_debug_errno(r, "Failed to add mprotect() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
+ r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(mprotect),
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC));
+ if (r < 0)
continue;
- }
if (shmat_syscall != 0) {
- r = seccomp_rule_add_exact(
- seccomp,
- SCMP_ACT_ERRNO(EPERM),
- SCMP_SYS(shmat),
- 1,
- SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC));
- if (r < 0) {
- log_debug_errno(r, "Failed to add shmat() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
+ r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(shmat),
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC));
+ if (r < 0)
continue;
- }
}
r = seccomp_load(seccomp);
if (r < 0)
return r;
- return seccomp_load(seccomp);
+ r = seccomp_load(seccomp);
+ if (IN_SET(r, -EPERM, -EACCES))
+ return r;
+ if (r < 0)
+ log_debug_errno(r, "Failed to restrict system call architectures, skipping: %m");
+
+ return 0;
+}
+
+int parse_syscall_archs(char **l, Set **archs) {
+ _cleanup_set_free_ Set *_archs;
+ char **s;
+ int r;
+
+ assert(l);
+ assert(archs);
+
+ r = set_ensure_allocated(&_archs, NULL);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(s, l) {
+ uint32_t a;
+
+ r = seccomp_arch_from_string(*s, &a);
+ if (r < 0)
+ return -EINVAL;
+
+ r = set_put(_archs, UINT32_TO_PTR(a + 1));
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ *archs = _archs;
+ _archs = NULL;
+
+ return 0;
+}
+
+int seccomp_filter_set_add(Set *filter, bool add, const SyscallFilterSet *set) {
+ const char *i;
+ int r;
+
+ assert(set);
+
+ NULSTR_FOREACH(i, set->value) {
+
+ if (i[0] == '@') {
+ const SyscallFilterSet *more;
+
+ more = syscall_filter_set_find(i);
+ if (!more)
+ return -ENXIO;
+
+ r = seccomp_filter_set_add(filter, add, more);
+ if (r < 0)
+ return r;
+ } else {
+ int id;
+
+ id = seccomp_syscall_resolve_name(i);
+ if (id == __NR_SCMP_ERROR) {
+ log_debug("Couldn't resolve system call, ignoring: %s", i);
+ continue;
+ }
+
+ if (add) {
+ r = set_put(filter, INT_TO_PTR(id + 1));
+ if (r < 0)
+ return r;
+ } else
+ (void) set_remove(filter, INT_TO_PTR(id + 1));
+ }
+ }
+
+ return 0;
+}
+
+int seccomp_lock_personality(unsigned long personality) {
+ uint32_t arch;
+ int r;
+
+ if (personality >= PERSONALITY_INVALID)
+ return -EINVAL;
+
+ 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 = seccomp_rule_add_exact(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(personality),
+ 1,
+ SCMP_A0(SCMP_CMP_NE, personality));
+ if (r < 0) {
+ log_debug_errno(r, "Failed to add scheduler rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
+ continue;
+ }
+
+ r = seccomp_load(seccomp);
+ if (IN_SET(r, -EPERM, -EACCES))
+ return r;
+ if (r < 0)
+ log_debug_errno(r, "Failed to enable personality lock for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
+ }
+
+ return 0;
}