aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'seccomp-bpf.c')
-rw-r--r--seccomp-bpf.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/seccomp-bpf.c b/seccomp-bpf.c
new file mode 100644
index 0000000..b56e9e4
--- /dev/null
+++ b/seccomp-bpf.c
@@ -0,0 +1,277 @@
+/*
+ * Generate the bpf rules ahead of time to speed up runtime.
+ *
+ * Copyright 2015-2024 Gentoo Foundation
+ * Distributed under the terms of the GNU General Public License v2
+ *
+ * Copyright 2015-2024 Mike Frysinger - <vapier@gentoo.org>
+ */
+
+const char argv0[] = "seccomp-bpf";
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <seccomp.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static const struct {
+ const char *name;
+ uint32_t arch;
+ const char *ifdef;
+} gen_seccomp_arches[] = {
+#define A(arch, ifdef) { #arch, SCMP_ARCH_##arch, ifdef }
+ A(AARCH64, "defined(__aarch64__)"),
+ A(ARM, "defined(__arm__)"),
+ A(MIPS, "defined(__mips__) && defined(__MIPSEB__) && (_MIPS_SIM == _ABIO32)"),
+ A(MIPS64, "defined(__mips__) && defined(__MIPSEB__) && (_MIPS_SIM == _ABI64)"),
+ A(MIPS64N32, "defined(__mips__) && defined(__MIPSEB__) && (_MIPS_SIM == _ABIN32)"),
+ A(MIPSEL, "defined(__mips__) && defined(__MIPSEL__) && (_MIPS_SIM == _ABIO32)"),
+ A(MIPSEL64, "defined(__mips__) && defined(__MIPSEL__) && (_MIPS_SIM == _ABI64)"),
+ A(MIPSEL64N32, "defined(__mips__) && defined(__MIPSEL__) && (_MIPS_SIM == _ABIN32)"),
+ A(PARISC, "defined(__hppa__) && !defined(__hppa64__)"),
+ A(PARISC64, "defined(__hppa__) && defined(__hppa64__)"),
+ A(PPC, "defined(__powerpc__) && !defined(__powerpc64__) && defined(__BIG_ENDIAN__)"),
+ A(PPC64, "defined(__powerpc__) && defined(__powerpc64__) && defined(__BIG_ENDIAN__)"),
+ A(PPC64LE, "defined(__powerpc__) && defined(__powerpc64__) && !defined(__BIG_ENDIAN__)"),
+ A(RISCV64, "defined(__riscv) && __riscv_xlen == 64"),
+ A(S390, "defined(__s390__) && !defined(__s390x__)"),
+ A(S390X, "defined(__s390__) && defined(__s390x__)"),
+ A(X86, "defined(__i386__)"),
+ A(X32, "defined(__x86_64__) && defined(__ILP32__)"),
+ A(X86_64, "defined(__x86_64__) && !defined(__ILP32__)"),
+#undef A
+};
+
+/* Simple helper to add all of the syscalls in an array. */
+static int gen_seccomp_rules_add(scmp_filter_ctx ctx, const int syscalls[], size_t num)
+{
+ static uint8_t prio;
+ size_t i;
+ for (i = 0; i < num; ++i) {
+ if (seccomp_syscall_priority(ctx, syscalls[i], prio++) < 0) {
+ warn("seccomp_syscall_priority failed");
+ return -1;
+ }
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0) {
+ warn("seccomp_rule_add failed");
+ return -1;
+ }
+ }
+ return 0;
+}
+#define gen_seccomp_rules_add(ctx, syscalls) gen_seccomp_rules_add(ctx, syscalls, ARRAY_SIZE(syscalls))
+
+static void gen_seccomp_dump(scmp_filter_ctx ctx, const char *name)
+{
+ unsigned char buf[32768 * 8];
+ ssize_t i, len;
+ int fd;
+
+ fd = memfd_create("bpf", MFD_CLOEXEC);
+ if (fd < 0)
+ err(1, "memfd_create failed");
+ if (seccomp_export_bpf(ctx, fd) < 0)
+ err(1, "seccomp_export_bpf_mem failed");
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ err(1, "seek failed");
+ len = read(fd, buf, sizeof(buf));
+ if (len <= 0)
+ err(1, "read failed");
+
+ printf("static const unsigned char seccomp_bpf_blks_%s[] = {\n\t", name);
+ for (i = 0; i < len; ++i)
+ printf("%u,", buf[i]);
+ printf("\n};\n");
+}
+
+static void gen_seccomp_program(const char *name)
+{
+ printf(
+ "static const seccomp_bpf_program_t seccomp_bpf_program_%s = {\n"
+ " .cnt = sizeof(seccomp_bpf_blks_%s) / 8,\n"
+ " .bpf = seccomp_bpf_blks_%s,\n"
+ "};\n", name, name, name);
+}
+
+int main(void)
+{
+ /* Order determines priority (first == lowest prio). */
+ static const int base_syscalls[] = {
+ /* We write the most w/scanelf. */
+ SCMP_SYS(write),
+ SCMP_SYS(writev),
+ SCMP_SYS(pwrite64),
+ SCMP_SYS(pwritev),
+
+ /* Then the stat family of functions. */
+ SCMP_SYS(newfstatat),
+ SCMP_SYS(fstat),
+ SCMP_SYS(fstat64),
+ SCMP_SYS(fstatat64),
+ SCMP_SYS(lstat),
+ SCMP_SYS(lstat64),
+ SCMP_SYS(stat),
+ SCMP_SYS(stat64),
+ SCMP_SYS(statx),
+
+ /* Then the fd close func. */
+ SCMP_SYS(close),
+
+ /* Then fd open family of functions. */
+ SCMP_SYS(open),
+ SCMP_SYS(openat),
+
+ /* Then the memory mapping functions. */
+ SCMP_SYS(mmap),
+ SCMP_SYS(mmap2),
+ SCMP_SYS(munmap),
+
+ /* Then the directory reading functions. */
+ SCMP_SYS(getdents),
+ SCMP_SYS(getdents64),
+
+ /* Then the file reading functions. */
+ SCMP_SYS(pread64),
+ SCMP_SYS(read),
+ SCMP_SYS(readv),
+ SCMP_SYS(preadv),
+
+ /* Then the fd manipulation functions. */
+ SCMP_SYS(fcntl),
+ SCMP_SYS(fcntl64),
+
+ /* After this point, just sort the list alphabetically. */
+ SCMP_SYS(access),
+ SCMP_SYS(brk),
+ SCMP_SYS(capget),
+ SCMP_SYS(chdir),
+ SCMP_SYS(dup),
+ SCMP_SYS(dup2),
+ SCMP_SYS(dup3),
+ SCMP_SYS(exit),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(faccessat),
+#ifndef __SNR_faccessat2
+/* faccessat2 is not yet defined in libseccomp-2.5.1 */
+# define __SNR_faccessat2 __NR_faccessat2
+#endif
+ SCMP_SYS(faccessat2),
+ SCMP_SYS(fchdir),
+ SCMP_SYS(getpid),
+ SCMP_SYS(gettid),
+ SCMP_SYS(ioctl),
+ SCMP_SYS(lseek),
+ SCMP_SYS(_llseek),
+ SCMP_SYS(mprotect),
+
+ /* Syscalls listed because of compiler settings. */
+ SCMP_SYS(futex),
+
+ /* Syscalls listed because of sandbox. */
+ SCMP_SYS(readlink),
+ SCMP_SYS(readlinkat),
+ SCMP_SYS(getcwd),
+
+ /* Syscalls listed because of fakeroot. */
+ SCMP_SYS(msgget),
+ SCMP_SYS(msgrcv),
+ SCMP_SYS(msgsnd),
+ SCMP_SYS(semget),
+ SCMP_SYS(semop),
+ SCMP_SYS(semtimedop),
+ /*
+ * Some targets (e.g. ppc & i386) implement the above functions
+ * as ipc() subcalls. #675378
+ */
+ SCMP_SYS(ipc),
+
+ /* glibc-2.34+ uses it as part of mem alloc functions. */
+ SCMP_SYS(getrandom),
+
+ /* glibc-2.35+ uses it when GLIBC_TUNABLES=glibc.malloc.hugetlb=1. */
+ SCMP_SYS(madvise),
+ };
+ static const int fork_syscalls[] = {
+ SCMP_SYS(clone),
+ SCMP_SYS(execve),
+ SCMP_SYS(fork),
+ SCMP_SYS(rt_sigaction),
+ SCMP_SYS(rt_sigprocmask),
+ SCMP_SYS(unshare),
+ SCMP_SYS(vfork),
+ SCMP_SYS(wait4),
+ SCMP_SYS(waitid),
+ SCMP_SYS(waitpid),
+ };
+
+ /* TODO: Handle debug and KILL vs TRAP. */
+
+ scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
+ if (!ctx)
+ err(1, "seccomp_init failed");
+
+ printf("/* AUTO GENERATED FILE. To regenerate run:\n");
+ printf(" * $ $EDITOR seccomp-bpf.c\n");
+ printf(" * $ make seccomp-bpf.h\n");
+ printf(" * See seccomp-bpf.c for details. */\n");
+ printf("#undef SECCOMP_BPF_AVAILABLE\n");
+
+ if (seccomp_arch_remove(ctx, seccomp_arch_native()) < 0)
+ err(1, "seccomp_arch_remove failed");
+
+ for (size_t i = 0; i < ARRAY_SIZE(gen_seccomp_arches); ++i) {
+ uint32_t arch = gen_seccomp_arches[i].arch;
+
+ seccomp_reset(ctx, SCMP_ACT_KILL);
+
+ if (arch != seccomp_arch_native()) {
+ if (seccomp_arch_remove(ctx, seccomp_arch_native()) < 0)
+ err(1, "seccomp_arch_remove failed");
+ if (seccomp_arch_add(ctx, arch) < 0)
+ err(1, "seccomp_arch_add failed");
+ }
+
+ printf("\n#if %s\n", gen_seccomp_arches[i].ifdef);
+ printf("/* %s */\n", gen_seccomp_arches[i].name);
+ printf("#define SECCOMP_BPF_AVAILABLE\n");
+
+ if (gen_seccomp_rules_add(ctx, base_syscalls) < 0)
+ err(1, "seccomp_rules_add failed");
+ gen_seccomp_dump(ctx, "base");
+
+ if (gen_seccomp_rules_add(ctx, fork_syscalls) < 0)
+ err(1, "seccomp_rules_add failed");
+ gen_seccomp_dump(ctx, "fork");
+
+ if (0) {
+ printf("/*\n");
+ fflush(stdout);
+ seccomp_export_pfc(ctx, 1);
+ fflush(stdout);
+ printf("*/\n");
+ }
+
+ printf("#endif\n");
+ }
+
+ printf(
+ "\n"
+ "#ifdef SECCOMP_BPF_AVAILABLE\n"
+ "typedef struct {\n"
+ " uint16_t cnt;\n"
+ " const void *bpf;\n"
+ "} seccomp_bpf_program_t;\n");
+ gen_seccomp_program("base");
+ gen_seccomp_program("fork");
+ printf("#endif\n");
+
+ seccomp_release(ctx);
+
+ return 0;
+}