diff --git a/src/gen_bpf.c b/src/gen_bpf.c index 2cb79a5f..9294a5df 100644 --- a/src/gen_bpf.c +++ b/src/gen_bpf.c @@ -1583,7 +1583,7 @@ static int _gen_bpf_syscalls(struct bpf_state *state, unsigned int *blks_added, uint32_t optimize, unsigned int *bintree_levels) { - struct db_sys_list *s_head = NULL, *s_tail = NULL, *s_iter; + struct db_sys_list *s_head = NULL, *s_tail = NULL, *s_iter, *s_first = NULL; unsigned int syscall_cnt, empty_cnt = 0; uint64_t *bintree_hashes = NULL, nxt_hsh; unsigned int *bintree_syscalls = NULL; @@ -1623,6 +1623,18 @@ static int _gen_bpf_syscalls(struct bpf_state *state, */ acc_reset = false; + /* the first syscall in the filter loads the syscall number for the + * ones that follow, so it must be the first syscall that is not + * omitted below; the head of the sorted list may be a pseudo-syscall + * or an entry that only carries a priority, and if we used it, no + * syscall would load the number */ + for (s_iter = s_head; s_iter != NULL; s_iter = s_iter->pri_nxt) { + if (!_skip_syscall(state, s_iter)) { + s_first = s_iter; + break; + } + } + syscall_cnt = 0; /* create the syscall filters and add them to block list group */ @@ -1641,7 +1653,7 @@ static int _gen_bpf_syscalls(struct bpf_state *state, /* build the syscall filter */ state->b_new = _gen_bpf_syscall(state, s_iter, nxt_hsh, - (s_iter == s_head ? + (s_iter == s_first ? acc_reset : false)); if (state->b_new == NULL) goto out; diff --git a/tests/65-sim-pseudo_syscall_first.c b/tests/65-sim-pseudo_syscall_first.c new file mode 100644 index 00000000..5b21243d --- /dev/null +++ b/tests/65-sim-pseudo_syscall_first.c @@ -0,0 +1,85 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2026 Sascha Grunert + * Author: Sascha Grunert + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include + +#include + +#include "util.h" + +int main(int argc, char *argv[]) +{ + int rc; + struct util_options opts; + scmp_filter_ctx ctx = NULL; + + rc = util_getopt(argc, argv, &opts); + if (rc < 0) + goto out; + + ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) + return ENOMEM; + + rc = seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE); + if (rc < 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_X86); + if (rc < 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_X86_64); + if (rc < 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_X32); + if (rc < 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_AARCH64); + if (rc < 0) + goto out; + + /* a pseudo-syscall on every arch above, without conditions so that it + * sorts before the conditional rules and becomes the head of the + * syscall list, where it is omitted from the filter; x86 drops the + * rule when it is added, the others keep it */ + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(arm_fadvise64_64), 0); + if (rc < 0) + goto out; + + /* the real rules must still be applied, which they are only if the + * filter loads the syscall number before testing them */ + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(read), 1, + SCMP_A0(SCMP_CMP_EQ, 1)); + if (rc < 0) + goto out; + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(write), 1, + SCMP_A0(SCMP_CMP_EQ, 1)); + if (rc < 0) + goto out; + + rc = util_filter_output(&opts, ctx); + if (rc) + goto out; + +out: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} diff --git a/tests/65-sim-pseudo_syscall_first.py b/tests/65-sim-pseudo_syscall_first.py new file mode 100644 index 00000000..0260d8ed --- /dev/null +++ b/tests/65-sim-pseudo_syscall_first.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2026 Sascha Grunert +# Author: Sascha Grunert +# + +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of version 2.1 of the GNU Lesser General Public License as +# published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see . +# + +import argparse +import sys + +import util + +from seccomp import * + +def test(args): + f = SyscallFilter(ALLOW) + f.remove_arch(Arch()) + f.add_arch(Arch("x86")) + f.add_arch(Arch("x86_64")) + f.add_arch(Arch("x32")) + f.add_arch(Arch("aarch64")) + # a pseudo-syscall on every arch above, without conditions so that it + # sorts before the conditional rules and becomes the head of the syscall + # list, where it is omitted from the filter; x86 drops the rule when it + # is added, the others keep it + f.add_rule(KILL, "arm_fadvise64_64") + # the real rules must still be applied, which they are only if the + # filter loads the syscall number before testing them + f.add_rule(KILL, "read", Arg(0, EQ, 1)) + f.add_rule(KILL, "write", Arg(0, EQ, 1)) + return f + +args = util.get_opt() +ctx = test(args) +util.filter_output(args, ctx) + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/65-sim-pseudo_syscall_first.tests b/tests/65-sim-pseudo_syscall_first.tests new file mode 100644 index 00000000..092ebaf9 --- /dev/null +++ b/tests/65-sim-pseudo_syscall_first.tests @@ -0,0 +1,39 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2026 Sascha Grunert +# Author: Sascha Grunert +# + +test type: bpf-sim + +# Testname Arch Syscall Arg0 Arg1 Arg2 Arg3 Arg4 Arg5 Result +65-sim-pseudo_syscall_first +x86 read 1 N N N N N KILL +65-sim-pseudo_syscall_first +x86 read 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86 read 2-10 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86 write 1 N N N N N KILL +65-sim-pseudo_syscall_first +x86 write 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86 open 1 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86_64 read 1 N N N N N KILL +65-sim-pseudo_syscall_first +x86_64 read 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86_64 read 2-10 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86_64 write 1 N N N N N KILL +65-sim-pseudo_syscall_first +x86_64 write 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +x86_64 open 1 N N N N N ALLOW +65-sim-pseudo_syscall_first +x32 read 1 N N N N N KILL +65-sim-pseudo_syscall_first +x32 read 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +x32 read 2-10 N N N N N ALLOW +65-sim-pseudo_syscall_first +x32 write 1 N N N N N KILL +65-sim-pseudo_syscall_first +x32 write 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +x32 open 1 N N N N N ALLOW +65-sim-pseudo_syscall_first +aarch64 read 1 N N N N N KILL +65-sim-pseudo_syscall_first +aarch64 read 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +aarch64 read 2-10 N N N N N ALLOW +65-sim-pseudo_syscall_first +aarch64 write 1 N N N N N KILL +65-sim-pseudo_syscall_first +aarch64 write 0 N N N N N ALLOW +65-sim-pseudo_syscall_first +aarch64 openat 1 N N N N N ALLOW + +test type: bpf-valgrind + +# Testname +65-sim-pseudo_syscall_first diff --git a/tests/66-sim-priority_only_first.c b/tests/66-sim-priority_only_first.c new file mode 100644 index 00000000..ab83929d --- /dev/null +++ b/tests/66-sim-priority_only_first.c @@ -0,0 +1,77 @@ +/** + * Seccomp Library test program + * + * Copyright (c) 2026 Sascha Grunert + * Author: Sascha Grunert + */ + +/* + * This library is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License as + * published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ + +#include +#include + +#include + +#include "util.h" + +int main(int argc, char *argv[]) +{ + int rc; + struct util_options opts; + scmp_filter_ctx ctx = NULL; + + rc = util_getopt(argc, argv, &opts); + if (rc < 0) + goto out; + + ctx = seccomp_init(SCMP_ACT_ALLOW); + if (ctx == NULL) + return ENOMEM; + + rc = seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE); + if (rc < 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_X86); + if (rc < 0) + goto out; + rc = seccomp_arch_add(ctx, SCMP_ARCH_AARCH64); + if (rc < 0) + goto out; + + /* a priority without a rule leaves an entry that sorts first and is + * omitted from the filter, on every arch */ + rc = seccomp_syscall_priority(ctx, SCMP_SYS(getpid), 255); + if (rc < 0) + goto out; + + /* the real rules must still be applied, which they are only if the + * filter loads the syscall number before testing them */ + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(read), 1, + SCMP_A0(SCMP_CMP_EQ, 1)); + if (rc < 0) + goto out; + rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(write), 1, + SCMP_A0(SCMP_CMP_EQ, 1)); + if (rc < 0) + goto out; + + rc = util_filter_output(&opts, ctx); + if (rc) + goto out; + +out: + seccomp_release(ctx); + return (rc < 0 ? -rc : rc); +} diff --git a/tests/66-sim-priority_only_first.py b/tests/66-sim-priority_only_first.py new file mode 100644 index 00000000..5ba13b31 --- /dev/null +++ b/tests/66-sim-priority_only_first.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# +# Seccomp Library test program +# +# Copyright (c) 2026 Sascha Grunert +# Author: Sascha Grunert +# + +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of version 2.1 of the GNU Lesser General Public License as +# published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +# for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, see . +# + +import argparse +import sys + +import util + +from seccomp import * + +def test(args): + f = SyscallFilter(ALLOW) + f.remove_arch(Arch()) + f.add_arch(Arch("x86")) + f.add_arch(Arch("aarch64")) + # a priority without a rule leaves an entry that sorts first and is + # omitted from the filter, on every arch + f.syscall_priority("getpid", 255) + # the real rules must still be applied, which they are only if the + # filter loads the syscall number before testing them + f.add_rule(KILL, "read", Arg(0, EQ, 1)) + f.add_rule(KILL, "write", Arg(0, EQ, 1)) + return f + +args = util.get_opt() +ctx = test(args) +util.filter_output(args, ctx) + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/tests/66-sim-priority_only_first.tests b/tests/66-sim-priority_only_first.tests new file mode 100644 index 00000000..748be05d --- /dev/null +++ b/tests/66-sim-priority_only_first.tests @@ -0,0 +1,29 @@ +# +# libseccomp regression test automation data +# +# Copyright (c) 2026 Sascha Grunert +# Author: Sascha Grunert +# + +test type: bpf-sim + +# Testname Arch Syscall Arg0 Arg1 Arg2 Arg3 Arg4 Arg5 Result +66-sim-priority_only_first +x86 read 1 N N N N N KILL +66-sim-priority_only_first +x86 read 0 N N N N N ALLOW +66-sim-priority_only_first +x86 read 2-10 N N N N N ALLOW +66-sim-priority_only_first +x86 write 1 N N N N N KILL +66-sim-priority_only_first +x86 write 0 N N N N N ALLOW +66-sim-priority_only_first +x86 getpid N N N N N N ALLOW +66-sim-priority_only_first +x86 open 1 N N N N N ALLOW +66-sim-priority_only_first +aarch64 read 1 N N N N N KILL +66-sim-priority_only_first +aarch64 read 0 N N N N N ALLOW +66-sim-priority_only_first +aarch64 read 2-10 N N N N N ALLOW +66-sim-priority_only_first +aarch64 write 1 N N N N N KILL +66-sim-priority_only_first +aarch64 write 0 N N N N N ALLOW +66-sim-priority_only_first +aarch64 getpid N N N N N N ALLOW +66-sim-priority_only_first +aarch64 openat 1 N N N N N ALLOW + +test type: bpf-valgrind + +# Testname +66-sim-priority_only_first diff --git a/tests/Makefile.am b/tests/Makefile.am index 280cfa83..2cb0e6a8 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -99,7 +99,9 @@ check_PROGRAMS = \ 61-sim-transactions \ 62-sim-arch_transactions \ 63-live-notify_addfd \ - 64-live-trapx + 64-live-trapx \ + 65-sim-pseudo_syscall_first \ + 66-sim-priority_only_first EXTRA_DIST_TESTPYTHON = \ util.py \ @@ -164,7 +166,9 @@ EXTRA_DIST_TESTPYTHON = \ 61-sim-transactions.py \ 62-sim-arch_transactions.py \ 63-live-notify_addfd.py \ - 64-live-trapx + 64-live-trapx \ + 65-sim-pseudo_syscall_first.py \ + 66-sim-priority_only_first.py EXTRA_DIST_TESTCFGS = \ 01-sim-allow.tests \ @@ -230,7 +234,9 @@ EXTRA_DIST_TESTCFGS = \ 61-sim-transactions.tests \ 62-sim-arch_transactions.tests \ 63-live-notify_addfd.tests \ - 64-live-trapx.tests + 64-live-trapx.tests \ + 65-sim-pseudo_syscall_first.tests \ + 66-sim-priority_only_first.tests EXTRA_DIST_TESTSCRIPTS = \ 38-basic-pfc_coverage.sh 38-basic-pfc_coverage.pfc \