While running VM validation for the Protection opt-out work in #17, I hit a second kernel-version gate that prevents sandlock from running on RHEL 9 / Ubuntu 22.04 even with the v6 Landlock scopes opted out.
Symptom
On stock Rocky Linux 9.6 (kernel 5.14.0-570.17.1.el9_6, Landlock ABI v5):
$ sandlock run --fs-read /usr --fs-read /lib64 --fs-read /etc \
--disable signal-scope --disable abstract-unix-scope-socket \
-- /usr/bin/true
sandlock child: seccomp install: Invalid argument (os error 22)
Error: process error: child process error: read notif fd from child: pipe closed
exit=1
Reproduces identically on Rocky 9.7 (5.14.0-611.5.1.el9_7) and Ubuntu 22.04 (5.15.0-179-generic).
Root cause
crates/sandlock-core/src/seccomp/bpf.rs:154:
let flags = SECCOMP_FILTER_FLAG_NEW_LISTENER | SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV;
let fd = seccomp(SECCOMP_SET_MODE_FILTER, flags, &fprog as *const _ as *const _)?;
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV was added in Linux 5.19. Passing it on an older kernel returns EINVAL, and the Rust path has no fallback.
This is a Rust-port regression
The pre-Rust Python implementation handled this correctly — commit 50d5eb9 ("Enable SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV for reliable notifications"):
# src/sandlock/_notif.py (pre-rewrite)
flags = SECCOMP_FILTER_FLAG_NEW_LISTENER | SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV
fd = _libc.syscall(..., ctypes.c_uint(flags), ...)
if fd < 0:
# Fall back without WAIT_KILLABLE_RECV on older kernels
fd = _libc.syscall(..., ctypes.c_uint(SECCOMP_FILTER_FLAG_NEW_LISTENER), ...)
The Rust rewrite (ae8fbd1 — "rewrite sandlock in Rust with full feature parity") dropped the fallback. WAIT_KILLABLE_RECV is a robustness flag (it prevents signals from aborting in-flight notifications) — losing it on older kernels degrades only reliability of supervised syscalls, not the security boundary, which matches the original Python intent.
Verified fix
Removing the WAIT_KILLABLE_RECV bit from the flags on Rocky 9.6 makes the full pipeline succeed (exit=0 after --disable signal-scope --disable abstract-unix-scope-socket). Porting the Python try-with-flag → fallback-without-flag pattern into install_filter should be sufficient.
Why this matters for #17
This gate compounds with #17: even after the Protection opt-out lets users select v5 Landlock semantics on RHEL 9 / Ubuntu 22.04, the seccomp install fails earlier in the same Sandbox::run() pipeline. sandlock check doesn't surface this — users only discover it at runtime. The two need to be fixed together for the LTS use case to actually work end-to-end.
Happy to take the PR if the fallback approach is acceptable in principle.
While running VM validation for the Protection opt-out work in #17, I hit a second kernel-version gate that prevents sandlock from running on RHEL 9 / Ubuntu 22.04 even with the v6 Landlock scopes opted out.
Symptom
On stock Rocky Linux 9.6 (kernel
5.14.0-570.17.1.el9_6, Landlock ABI v5):Reproduces identically on Rocky 9.7 (5.14.0-611.5.1.el9_7) and Ubuntu 22.04 (5.15.0-179-generic).
Root cause
crates/sandlock-core/src/seccomp/bpf.rs:154:SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECVwas added in Linux 5.19. Passing it on an older kernel returnsEINVAL, and the Rust path has no fallback.This is a Rust-port regression
The pre-Rust Python implementation handled this correctly — commit
50d5eb9("Enable SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV for reliable notifications"):The Rust rewrite (
ae8fbd1— "rewrite sandlock in Rust with full feature parity") dropped the fallback.WAIT_KILLABLE_RECVis a robustness flag (it prevents signals from aborting in-flight notifications) — losing it on older kernels degrades only reliability of supervised syscalls, not the security boundary, which matches the original Python intent.Verified fix
Removing the
WAIT_KILLABLE_RECVbit from the flags on Rocky 9.6 makes the full pipeline succeed (exit=0after--disable signal-scope --disable abstract-unix-scope-socket). Porting the Python try-with-flag → fallback-without-flag pattern intoinstall_filtershould be sufficient.Why this matters for #17
This gate compounds with #17: even after the Protection opt-out lets users select v5 Landlock semantics on RHEL 9 / Ubuntu 22.04, the seccomp install fails earlier in the same
Sandbox::run()pipeline.sandlock checkdoesn't surface this — users only discover it at runtime. The two need to be fixed together for the LTS use case to actually work end-to-end.Happy to take the PR if the fallback approach is acceptable in principle.