Skip to content

BUG: load the syscall number in the first syscall that is emitted - #500

Open
saschagrunert wants to merge 1 commit into
seccomp:mainfrom
saschagrunert:bpf-load-syscall-first-emitted
Open

saschagrunert wants to merge 1 commit into
seccomp:mainfrom
saschagrunert:bpf-load-syscall-first-emitted

Conversation

@saschagrunert

@saschagrunert saschagrunert commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

_gen_bpf_syscalls() emits the load of the syscall number only in the block of the head of the sorted syscall list, but the loop omits the head when _skip_syscall() says so: an entry that only carries a priority, which seccomp_syscall_priority() leaves for a syscall without a rule, and since v2.5.0 (ad873bd) a pseudo-syscall. No block loads the number then, every rule of that architecture compares against whatever the accumulator holds, and every call gets the default action. The head-only load dates from v2.0.0 (90882e7).

This takes a filter whose first syscall by priority is such an entry, on an architecture whose syscall blocks load the number (all but a lone x86_64 or x32). A pseudo-syscall heads the list when every unconditional rule names a syscall the architecture lacks, which the socketcall-multiplexing architectures avoid by dropping such a rule in seccomp_rule_add; a priority-only entry heads it on any architecture. Container runtimes load such filters from user configuration. The PFC export shows the rules as intended, so it cannot be used to find the problem. Reproducer on x86_64 with 2.5.5, 2.6.1 and main:

ctx = seccomp_init(SCMP_ACT_ALLOW);
seccomp_arch_add(ctx, SCMP_ARCH_X32);
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 1,
                 SCMP_A0(SCMP_CMP_EQ, AF_VSOCK));
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socketcall), 0);
seccomp_load(ctx);
socket(AF_VSOCK, SOCK_STREAM, 0);   /* succeeds, expected EPERM */

The exported BPF has no ld [0] after the x86_64 architecture check; without SCMP_ARCH_X32 the call fails with EPERM. The same happens with seccomp_syscall_priority(ctx, SCMP_SYS(getpid), 255) in place of the socketcall rule, on every architecture.

The fix loads the number in the first syscall that is not skipped. The new tests 65-sim-pseudo_syscall_first (x86_64 with x32, aarch64) and 66-sim-priority_only_first (x86, aarch64) fail without it and pass with it; make check passes.

_gen_bpf_syscalls() emits the instruction that loads the syscall number
only in the block of the head of the sorted syscall list, but the loop
omits the head when _skip_syscall() says so: an entry that only carries a
priority, which seccomp_syscall_priority() leaves for a syscall without
a rule, and since ad873bd ("bpf: do not add pseudo-syscalls to the BPF
filter", v2.5.0) a pseudo-syscall. No block loads the number then, every
rule of that architecture compares against whatever the accumulator
holds after the architecture check, and every call gets the default
action. The head-only load dates from 90882e7 ("bpf: fix a problem when
creating filters that include syscall arguments", v2.0.0).

This takes a filter whose first syscall by priority is such an entry, on
an architecture whose syscall blocks load the number, which is every one
but a lone x86_64 or x32. A pseudo-syscall heads the list when every
unconditional rule names a syscall the architecture lacks, which the
architectures with socketcall multiplexing avoid by dropping such a rule
in seccomp_rule_add; a priority-only entry heads it on any architecture.
With a permissive default action every denying rule of the architecture
is disabled. The PFC export shows the rules as intended, so it cannot
be used to find the problem. Reproducer on x86_64 with 2.5.5, 2.6.1 and
main:

	ctx = seccomp_init(SCMP_ACT_ALLOW);
	seccomp_arch_add(ctx, SCMP_ARCH_X32);
	seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 1,
			 SCMP_A0(SCMP_CMP_EQ, AF_VSOCK));
	seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socketcall), 0);
	seccomp_load(ctx);
	socket(AF_VSOCK, SOCK_STREAM, 0);	/* succeeds, expected EPERM */

The exported BPF has no "ld [0]" after the x86_64 architecture check;
without SCMP_ARCH_X32 the call fails with EPERM. The same happens with
seccomp_syscall_priority(ctx, SCMP_SYS(getpid), 255) in place of the
socketcall rule, on every architecture.

Load the number in the first syscall that is not skipped instead of in
the head. The new tests 65-sim-pseudo_syscall_first and
66-sim-priority_only_first fail without the change and pass with it;
the rest of the suite still passes.

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
@saschagrunert
saschagrunert force-pushed the bpf-load-syscall-first-emitted branch from 29e7b2f to a04b816 Compare October 1, 2026 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant