diff --git a/src/codegen.c b/src/codegen.c index 3e13fe2..5d88a36 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -54,6 +54,9 @@ #define SECCOMP_RET_USER_NOTIF 0x7fc00000U #endif +/* __X32_SYSCALL_BIT from arch/x86/include/uapi/asm/unistd.h */ +#define X32_SYSCALL_BIT 0x40000000U + #define CURRENT_LOC (ctxt->buffer.len - 1) #define LOC_TO_JUMP(loc) (CURRENT_LOC - (loc)) @@ -686,6 +689,7 @@ static void reverse_instruction_buffer(struct codegen_ctxt *ctxt) { static int compile_policy_for_archs(struct codegen_ctxt *ctxt, struct kafel_ctxt *kafel_ctxt, + uint32_t audit_arch, uint32_t target_archs) { ASSERT(ctxt != NULL); ASSERT(kafel_ctxt != NULL); @@ -704,6 +708,17 @@ static int compile_policy_for_archs(struct codegen_ctxt *ctxt, int next = generate_rules(ctxt, rules); range_rules_destroy(&rules); if (next > begin) { + if (audit_arch == AUDIT_ARCH_X86_64) { + /* + x32 syscalls also report AUDIT_ARCH_X86_64 and are only told apart by + X32_SYSCALL_BIT in nr, so they would otherwise slip past the x86_64 + rules (e.g. a DENY list with DEFAULT ALLOW). Treat them like any other + untargeted architecture, but let nr == -1 through: tracers use it to + skip a syscall and filters are re-run after tracing (see seccomp(2)). + */ + int x32 = add_jump(ctxt, BPF_JEQ, UINT32_MAX, next, -ACTION_KILL); + next = add_jump_ge(ctxt, X32_SYSCALL_BIT, x32, next); + } begin = next = ADD_INSTR(BPF_LOAD_SYSCALL); } else { next = -kafel_ctxt->default_action; @@ -759,7 +774,8 @@ static int compile_policy_impl(struct codegen_ctxt *ctxt, int next = -ACTION_KILL; for (int i = 0; i < archs_len; ++i) { - int policy = compile_policy_for_archs(ctxt, kafel_ctxt, archs[i].target_archs); + int policy = compile_policy_for_archs(ctxt, kafel_ctxt, archs[i].audit_arch, + archs[i].target_archs); if (policy != -ACTION_KILL) { next = add_jump(ctxt, BPF_JEQ, archs[i].audit_arch, policy, next); } diff --git a/test/basic.c b/test/basic.c index 127e06a..fecdcf2 100644 --- a/test/basic.c +++ b/test/basic.c @@ -259,3 +259,20 @@ TEST_CASE(logic_negation) { SYSCALL_ERRNO_SPEC(1)); TEST_POLICY_BLOCKS_SYSCALL(SYSCALL_SPEC3(__NR_read, 5, 0, 0)); } + +#if defined(__x86_64__) +#define X32_SYSCALL_BIT 0x40000000 + +TEST_CASE(x32_abi_rejected) { + TEST_POLICY( + "POLICY a {\n" + " ERRNO(1) { getpid }\n" + "} USE a DEFAULT ALLOW"); + TEST_POLICY_ALLOWS_SYSCALL(SYSCALL_SPEC0(__NR_getpid), + SYSCALL_ERRNO_SPEC(1)); + TEST_POLICY_BLOCKS_SYSCALL(SYSCALL_SPEC0(X32_SYSCALL_BIT | __NR_getpid)); + TEST_POLICY_BLOCKS_SYSCALL(SYSCALL_SPEC0(X32_SYSCALL_BIT | __NR_read)); + TEST_POLICY_ALLOWS_SYSCALL(SYSCALL_SPEC0(-1), + SYSCALL_EXECUTED_SPEC(-1, ENOSYS)); +} +#endif