From 46222ec2f71984b7e08b664919151178752e477f Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Mon, 12 Oct 2026 11:58:40 +0200 Subject: [PATCH] doc: warn that argument comparisons don't ignore the upper 32 bits The kernel's seccomp filter sees the raw 64-bit argument registers, while syscalls with 32-bit argument types (e.g. the socket(2) domain) ignore the upper 32 bits. A rule such as SCMP_A0(SCMP_CMP_EQ, AF_NETLINK) is therefore bypassed by socket(0x100000000|AF_NETLINK, ...). The SCMP_CMP32/SCMP_A{0-5}_32 macros don't help, as they only truncate the datum and mask values and still compare the full 64-bit argument. Document this in seccomp_rule_add(3) and in the SCMP_CMP32 header comment, and recommend SCMP_CMP_MASKED_EQ with a 0xffffffff mask for arguments the kernel treats as 32-bit. Signed-off-by: Ricardo Branco --- doc/man/man3/seccomp_rule_add.3 | 33 +++++++++++++++++++++++++++++++++ include/seccomp.h.in | 6 ++++++ 2 files changed, 39 insertions(+) diff --git a/doc/man/man3/seccomp_rule_add.3 b/doc/man/man3/seccomp_rule_add.3 index 280b9c5e..7ae19e13 100644 --- a/doc/man/man3/seccomp_rule_add.3 +++ b/doc/man/man3/seccomp_rule_add.3 @@ -131,6 +131,39 @@ macros and use the variants which are explicitly 32 or 64-bit. This should help eliminate problems caused by an unwanted sign extension of negative datum values. .P +Note that none of the variants, 32-bit or 64-bit, ignore the upper 32 bits of +a syscall argument. The 32-bit variants only truncate the mask and datum +values, and all of the generated comparisons are still performed against the +full 64-bit syscall argument as seen by the kernel. Many syscall arguments are +declared as 32-bit types (e.g. the +.I domain +argument of +.BR socket (2)), +and the kernel ignores the upper 32 bits of such arguments. The upper 32 bits +of the register are nevertheless not guaranteed to be zero, and a caller can set +them to arbitrary values. A rule such as +.sp +SCMP_A0(SCMP_CMP_EQ, AF_NETLINK) +.sp +(or the equivalent +.BR SCMP_A0_64 () +or +.BR SCMP_A0_32 () +forms) will therefore not match +.BR socket (2) +being called with 0x100000000|AF_NETLINK, which the kernel treats the same as +AF_NETLINK, and a filter relying on it to deny the syscall can be bypassed. +When filtering on an argument that is a 32-bit type in the kernel, +use +.B SCMP_CMP_MASKED_EQ +with a mask of 0xffffffff instead of +.BR SCMP_CMP_EQ , +example: +.sp +SCMP_CMP32(0, SCMP_CMP_MASKED_EQ, 0xffffffff, AF_NETLINK) +.sp +The same consideration applies to the other comparison operators. +.P If syscall argument comparisons are included in the filter rule, all of the comparisons must be true for the rule to match. .P diff --git a/include/seccomp.h.in b/include/seccomp.h.in index f685480e..e6376834 100644 --- a/include/seccomp.h.in +++ b/include/seccomp.h.in @@ -273,6 +273,12 @@ struct scmp_arg_cmp { /** * Specify a 32-bit argument comparison struct for use in declaring rules + * + * Note that this only truncates the datum values; the comparison is still done + * against the full 64-bit argument, as with SCMP_CMP64. To ignore the upper 32 + * bits of an argument the kernel treats as 32-bit, use SCMP_CMP_MASKED_EQ with + * a mask of 0xffffffff. + * * @param arg the argument number, starting at 0 * @param op the comparison operator, e.g. SCMP_CMP_* * @param datum_a dependent on comparison (32-bits)