Conversation
Findings
Full transparent behavior on old kernels is difficult. The safest approach is an explicit legacy mode that forbids broker writes into workload memory. Mode Behavior Recommended changes:
The key invariant should be something like: cancellation || task_memory_writes_disabled rather than removing cancellation from validation unconditionally. This would let outbound-oriented workloads run on RHEL 9 while preserving containment. Some server workloads may fail because common accept wrappers request the peer address. Full compatibility requires a distribution backport of WAIT_KILLABLE_RECV; userspace cannot atomically |
johntmyers
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Thanks @mrunalp. I independently checked the signal-cancellation concern against the kernel contract, the broker's accept/getpeername/sendmmsg output-write paths, and the architecture invariant; it is reachable on the proposed plain-listener fallback and remains blocking.
Action required: @akram, preserve cancellation safety for every task-memory output write and update the comments and architecture documentation to describe the selected legacy behavior accurately.
Blocking findings:
GATOR-de00bfcc-01: plain-listener notification cancellation can race privileged writes into workload memory
Carried findings:
- None
Non-blocking suggestions:
- None
Gator metadata
- Validation: Project-valid focused fix for accepted issue #3417
- Docs: Missing;
architecture/sandbox.mdstill saysWAIT_KILLABLE_RECVis mandatory, and the new inline comments reverse the flag semantics - Checks: DCO, vouch, and Trivy pass; Branch Checks and Helm Lint await current-head test dispatch
- E2E:
test:e2eis required for sandbox lifecycle and seccomp enforcement behavior, but dispatch waits for blocking review feedback - Head SHA:
de00bfccfca2d284da266865a6e99c74e9284d02 - Base SHA:
8de26878f9324a822131ff6107861134843f1886 - Merge base SHA:
6d496f540a1a2e5c49687cddd68d56dc82cd5ebb - Patch ID:
8efd98bbfd32d2ebec43ab50ae307293afde088e - Gator payload:
9 - Review mode:
initial - Previous reviewed SHA: none
- Review budget exhausted: no
- Maintainer decision required: no
- Next state:
gator:in-review
|
thank you @mrunalp and @johntmyers , I'am addressing the change request in subsequent commits |
BlockedThanks @akram. I saw your follow-up commit addressing the cancellation-safety request, including the legacy read-only listener mode and the updated architecture documentation. I cannot run the required delta review yet because GitHub reports that this head conflicts with Next action: @akram, rebase or merge Gator metadata
|
| cancellation: notification.wait_killable_recv, | ||
| // Legacy plain listener (< 5.19) disables broker output writes; | ||
| // satisfies the `cancellation || writes_disabled` launch invariant. | ||
| task_memory_writes_disabled: !notification.wait_killable_recv, |
There was a problem hiding this comment.
Under which conditions would cancellation and task_memory_writes_disabled not have different values?
There was a problem hiding this comment.
@elezar you're right: today task_memory_writes_disabled is just !cancellation, so the two are always opposite and the cancellation || task_memory_writes_disabled check is tautological.
Instead of dropping the field, I'd make it a real independent attestation. At qualification the sandbox installs a LegacyReadOnly listener and checks that its own broker guard (write_task_output) actually fails closed with EOPNOTSUPP, then reports that result. It becomes true only when the listener is legacy and the guard is verified, so if the guard ever regressed and started writing again, the field drops to false while cancellation is already false, and validate() rejects the boundary instead of admitting the race. The two can then genuinely diverge, and the invariant has teeth. It's ~20 lines in qualify_runtime (prototype on explore/seccomp-writes-disabled-selftest).
I can fix it in this PR, or in a follow-up PR if you'd rather land the current change first.
Since this touches the cancellation-safety contract you reviewed: @mrunalp @johntmyers, does the direction work for you? The alternative is collapsing to a single ListenerMode, which makes the unsafe state unrepresentable but leaves no confirmation cross-check. I'm leaning toward the attestation since it keeps validate() meaningful.
…RECV is unavailable SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV was added in Linux 5.19. On older kernels (for example RHEL 9.x / 5.14 nodes such as RHCOS on OpenShift) the flag is rejected with EINVAL, which made the capability-free sandbox fail to start during the notification probe with "notification launcher disappeared". Install the notification listener with WAIT_KILLABLE_RECV when the kernel supports it and fall back to a plain NEW_LISTENER on EINVAL. The fallback listener records wait_killable_recv = false: its notification receive is uninterruptible, but the sandbox is otherwise fully functional. Signed-off-by: Akram <akram.benaissi@gmail.com>
The confirmation evidence check required seccomp.cancellation
(WAIT_KILLABLE_RECV, Linux 5.19+). On kernels older than 5.19 the sandbox
falls back to a plain listener and reports cancellation=false, so requiring it
stopped the supervisor from confirming the sandbox there ("confirmation failed:
sandbox confirmation evidence is incomplete or mismatched"). Cancellation is a
robustness property, not a containment prerequisite: containment still holds via
the listener itself. Drop it from the launch gate and update the conformance
test to assert rejection on a containment-critical primitive instead.
Signed-off-by: Akram <akram.benaissi@gmail.com>
…llation invariant Follow-up to the PR review (GATOR-de00bfcc-01 / mrunalp): make the < 5.19 fallback cancellation-safe instead of racing broker writes. - Record an explicit ListenerMode (Killable vs LegacyReadOnly); add writes_disabled()/mode() and emit the selected mode in qualification output (seccomp_listener_mode). - Centralize task-memory output writes behind NotificationListener:: write_task_output; in LegacyReadOnly mode getpeername, accept/accept4 with a non-null address, and sendmmsg length write-backs fail closed with EOPNOTSUPP. accept with a null address, socket/connect/bind/listen/sendto/sendmsg keep working (copied inputs, scalar responses, atomic ADDFD_SEND). - Enforce the launch invariant `cancellation || task_memory_writes_disabled` in SandboxConfirmEvidence::validate() rather than dropping cancellation unconditionally; add task_memory_writes_disabled to SeccompEvidence. - Add per-path fail-closed tests (write_task_output, write_socket_addr, a real plain listener installed on a modern kernel) and confirmation-invariant tests. - Correct the flag-semantics comments and document both modes plus the reduced legacy syscall compatibility in architecture/sandbox.md. Signed-off-by: Akram <akram.benaissi@gmail.com>
dc85fde to
2370e50
Compare
|
Label |
|
/ok to test 2370e50 |
PR Review StatusThanks @akram. I reviewed the current head after your follow-up on the cancellation-safety request. The new legacy read-only listener mode routes Action required: Please document the kernel-before-5.19 Blocking findings:
Carried findings:
Gator metadata
|
Kernels without SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (< 5.19, e.g. RHEL 9.x / RHCOS 5.14) run the sandbox in a legacy read-only cancellation mode where the broker fails closed with EOPNOTSUPP on the mediated operations that write results back into workload memory (getpeername, accept/accept4 with a non-null address, sendmmsg length write-backs). Document this observable behavior and its syscall limitations in the public Fern docs: the support-matrix kernel requirements and the OpenShift runtime guidance. Signed-off-by: Akram <akram.benaissi@gmail.com>
|
doc added @johntmyers |
|
/ok to test 285085e |
|
Label |
Summary
The capability-free sandbox hard-requires
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV(Linux 5.19+) to install its notification listener. On older kernels (RHEL 9.x / 5.14, i.e. current OpenShift/RHCOS nodes) theseccomp()call returnsEINVAL, the listener thread dies (notification launcher disappeared), and the supervisor fails confirmation — so the sandbox cannot start.This makes the flag optional: attempt it, and gracefully fall back to a plain
NEW_LISTENERwhen the kernel doesn't support it. Zero-cap containment is unchanged (the listener still mediates every syscall); the only thing lost on < 5.19 is the killable-receive semantics.Related Issue
Fixes #3417
Changes
openshell-isolation-interfaceinstall_listener: tryWAIT_KILLABLE_RECV, and onEINVALretry with a plainNEW_LISTENER. The resulting listener recordswait_killable_recv = false.SandboxConfirmEvidence::validate(): stop gating launch on thecancellationevidence (which is exactlyWAIT_KILLABLE_RECV), so confirmation succeeds on < 5.19. Updated the conformance test to assert rejection on a containment-critical primitive (new_listener) instead.Testing
5.14.0-687.35.1.el9_8): thecapability-probereturns{"qualified":true, "seccomp_notification":true, "wait_killable_recv":false}, and a real sandbox created through the Kubernetes driver reachesReady(workload + supervisor pods both Running). Before the change the same path failed withnotification launcher disappeared.WAIT_KILLABLE_RECVis still used;wait_killable_recv = true).Checklist
--signoff(DCO)validate())