Expand description
capabilities.shell.staged_runtime: staging a pinned host runtime tree into a capsule root.
§The mechanism, and why it is the inverse of interpreter_runtime
A path-based interpreter — CPython above all — cannot run from an allowlisted binary alone. Its
stdlib lives outside the workdir, at a path the DT_NEEDED closure cannot discover, so
something has to make that tree reachable. There are exactly two ways to do it:
- [
crate::sandbox]’sinterpreter_runtimegrants widen the capsule’s Landlock scope outwards, so the host’s/usr/lib/python3.11stays readable from inside. That works atscoped, and it costs: the capsule is now coupled to one host’s directory layout, host paths stay reachable, and moving to a second host with a different Python build silently changes what the capsule runs. This is why declaring one firesW-SEC-009. staged_runtimemoves the tree inwards. Asealedcapsule already builds a composed root out of read-only binds (crate::sealed); staging adds one more bind, of an operator-named, operator-pinned runtime tree, at the same absolute path it has on the host. Nothing outside the root becomes reachable, and thepinmakes “the same interpreter on both hosts” a claim a human can check rather than assume.
The second supersedes the first for any binary that uses it, which is why the manifest parser
refuses both for one binary rather than merging them (see parse_staged_runtime in
murmur-artifact). Staging is not an additional grant layered onto a widened scope; it is the
thing that makes widening unnecessary.
§What this module contains, and how the mount reaches the launch path
Two independent pieces, with very different reach:
check_staged_runtime_floor— pure, every OS, and live in the launch path. It is the gate that refuses to stage a capsule declaringstaged_runtimebelow an effectivesealedfloor, called fromruntime::stage_sessionnext tocheck_containment_floor.bind_mount_staged_runtimes— Linux-only, and the executable statement of the mount contract: the re-basing rule, the two-call read-only bind, and the failure taxonomy. It is built and proven here in isolation, against a throwaway directory tree inside a mount namespace its own test creates.
The staging mounts are performed on every sealed launch that declares a grant — but not
by calling that function. sandbox::resolve_staged_runtime_dirs collects each grant’s
source_path into ShellEnforcement::staged_runtime_dirs, build_sealed_root passes them to
[crate::sealed::plan_composed_root]’s staged_runtime_read_only parameter, and they are
planned as required RootOp::Bind steps ahead of every other step — and so ahead of
pivot_root. A grant whose source is missing fails at the real mount(2) with ENOENT,
aborting the construction; that reaches the operator as
RuntimeError::SealedRootConstructionFailed (E-RUN-014) and is session-fatal.
The function itself stays uncalled by production code because it allocates, and the composed
root executes inside the forked child’s pre_exec window, which must not — see
bind_mount_staged_runtimes’s own doc comment. It remains valuable as an independently
provable statement of what the planned step must do on a real kernel, without needing a sealed
capsule, a pivot_root or the enforcement pipeline to demonstrate it.
Structs§
- Staged
Runtime Mount Error - One staged-runtime bind mount that could not be established.
Enums§
- Staged
Runtime Mount Stage - Where
bind_mount_staged_runtimesfailed, so the error names a step rather than a bare errno.
Functions§
- bind_
mount_ staged_ runtimes - Bind-mounts every declared
staged_runtimetree read-only intoroot, each at its own absolute host path re-based underroot. Returns the target paths it established, in order. - check_
staged_ runtime_ floor - Refuses a capsule that declares
capabilities.shell.staged_runtimewithout an effectivesealedcontainment floor.