seccomp: mask setns() nstype to 32 bits in RestrictNamespaces=

The nstype argument of setns() is an int, but it is passed to the kernel
in a 64-bit register, and the kernel truncates it to the low 32 bits.
The rule that blocks setns() with nstype == 0 compared the full 64-bit
register with SCMP_CMP_EQ, so setting any bit above bit 31 let the call
through, and the kernel then joined whichever namespace the fd referred
to, even one that RestrictNamespaces= is supposed to deny.

Use SCMP_CMP_MASKED_EQ with a 32-bit mask so we match on the same value
the kernel does.

Add a test that issues setns() via syscall() with an upper bit set.

Follow-up for #44134

Signed-off-by: Ricardo Branco <rbranco@suse.de>
(cherry picked from commit da72537164910f9050793055fef030711c00b954)
This commit is contained in:
Ricardo Branco
2026-10-16 17:24:51 +01:00
committed by Luca Boccassi
parent f190b1a631
commit 63051fa2bf
2 changed files with 9 additions and 2 deletions
+3 -2
View File
@@ -1429,13 +1429,14 @@ int seccomp_restrict_namespaces(unsigned long retain) {
0);
else
/* Otherwise, block only the invocations with the appropriate flags in the loop
* below, but also the special invocation with a zero flags argument, right here. */
* below, but also the special invocation with a zero flags argument, right here. The
* kernel takes nstype as an int and truncates it to 32 bits, hence only compare those. */
r = sym_seccomp_rule_add_exact(
seccomp,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(setns),
1,
SCMP_A1(SCMP_CMP_EQ, 0));
SCMP_A1(SCMP_CMP_MASKED_EQ, UINT32_MAX, 0));
if (r < 0) {
log_debug_errno(r, "Failed to add setns() rule for architecture %s, skipping: %m",
seccomp_arch_to_string(arch));
+6
View File
@@ -327,6 +327,12 @@ TEST(restrict_namespace) {
assert_se(setns(0, 0) == -1);
assert_se(errno == EPERM);
/* The kernel truncates nstype to 32 bits, make sure setting upper bits doesn't bypass the filter */
if (sizeof(long) == 8) {
assert_se(syscall(__NR_setns, 0, (unsigned long) (UINT64_C(1) << 32)) == -1);
assert_se(errno == EPERM);
}
pid_t pid = raw_clone(CLONE_NEWNS);
assert_se(pid >= 0);
if (pid == 0)