seccomp: fall back to NEW_LISTENER on kernels without WAIT_KILLABLE_RECV#63
Merged
congwang-mk merged 1 commit intoMay 26, 2026
Merged
Conversation
The Python implementation (commit 50d5eb9) tried the install with SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV first and fell back to bare NEW_LISTENER on older kernels that rejected the bit with EINVAL. The Rust rewrite passed both flags unconditionally, so seccomp(SET_MODE_FILTER) returns EINVAL on every kernel < 5.19 — blocking sandlock on RHEL 9 (5.14) and Ubuntu 22.04 (5.15) at runtime. Restore the pre-rewrite behaviour: try the preferred flag set, retry with the fallback set only on EINVAL, propagate any other error unchanged. WAIT_KILLABLE_RECV is a robustness flag (stops signals aborting in-flight notifications); dropping it on older kernels does not relax the security boundary, which is what the original Python fallback already relied on. Extract the retry control flow into install_with_einval_fallback so the EINVAL-only retry, success-fast-path, non-EINVAL passthrough, and both-EINVAL terminal cases can be unit-tested without invoking the real seccomp(2) syscall. Verified on Rocky Linux 9.6 (5.14.0-570.17.1.el9_6): sandlock run with no v6 protections succeeds with exit=0 after this change; without it, the same invocation fails at seccomp install before Landlock is attempted. Fixes multikernel#62.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #62.
The Python implementation (commit
50d5eb9, "Enable SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV for reliable notifications") tried the install withWAIT_KILLABLE_RECVfirst and fell back to bareNEW_LISTENERon older kernels that rejected the bit withEINVAL. The Rust rewrite passed both flags unconditionally, soseccomp(SET_MODE_FILTER)returnsEINVALon every kernel < 5.19 — blocking sandlock on RHEL 9 (5.14) and Ubuntu 22.04 (5.15) at runtime, before Landlock is even attempted.Change
Restore the pre-rewrite behaviour:
NEW_LISTENER | WAIT_KILLABLE_RECVfirstEINVAL(and onlyEINVAL), retry withNEW_LISTENERaloneThe retry control flow is extracted into
install_with_einval_fallbackso the success-fast-path, EINVAL-retry, non-EINVAL-passthrough, and both-EINVAL terminal cases can be unit-tested without invoking the realseccomp(2).WAIT_KILLABLE_RECVis a robustness flag (it stops signals from aborting in-flight notifications); dropping it on older kernels does not relax the security boundary, which is what the original Python fallback already relied on.seccomp(SET_MODE_FILTER)also returnsEINVALfor a malformed BPF program (bad instruction count, bad opcode, kernel verifier rejection). In that case both attempts use the samefprog, so the second call fails withEINVALtoo and the error is propagated — the fallback cannot silently paper over a malformed filter. Thefallback_einval_on_both_returns_second_einvaltest pins this behaviour.Tests
seccomp::bpf::tests::fallback_succeeds_first_try_returns_fd_no_retry— happy path, no retryseccomp::bpf::tests::fallback_einval_retries_with_fallback_flags— preferred-set returnsEINVAL, fallback-set succeedsseccomp::bpf::tests::fallback_non_einval_error_propagates_without_retry—EPERMis not retriedseccomp::bpf::tests::fallback_einval_on_both_returns_second_einval— both attempts fail, terminal error surfaces282 lib tests pass.
Verified on a real < 5.19 kernel
On stock Rocky Linux 9.6 (kernel
5.14.0-570.17.1.el9_6, Landlock ABI v5):Before this change:
After this change (same binary, same kernel):
(The Rocky 9.6 run also requires #17 / Protection opt-out to skip the two v6 IPC scopes; the seccomp install passes either way once this fix lands.)