Skip to content

nvx-rs: check guest-path containment with one function - #634

Open
Pedro Henrique Penna (ppenna) wants to merge 1 commit into
devfrom
nvx-rs-quality/guest-path-containment
Open

Pedro Henrique Penna (ppenna) wants to merge 1 commit into
devfrom
nvx-rs-quality/guest-path-containment

Conversation

@ppenna

Copy link
Copy Markdown
Contributor

Summary

Four checks ask whether a guest path is a directory or lies below it, comparing whole components, so that /runner is not inside /run. filesystem::within answers that question for the guest directories that a host mapping must not cover, but the sandbox's share checks wrote the same expression out three more times: validate_mount_target for the reserved mount targets, targets_overlap as an inside closure applied both ways, and validate_run_share_targets with a second copy of that closure and of targets_overlap's condition. This change makes within visible to the crate, and has the three checks call it, directly or through targets_overlap. The rule now has one definition, so the copies cannot drift apart, for example if one is "simplified" to a plain starts_with, and each call names the rule that it applies.

Changes

All of the code changes are in nvx-rs/src/host/filesystem.rs, nvx-rs/src/host/sandbox/mount.rs, and nvx-rs/src/host/sandbox/options.rs.

  • within (host::filesystem) is now pub(crate) instead of private. Its documentation states the whole-component rule that its callers rely on. These sites now share it:
    • filesystem::plan, for RESERVED_TARGETS, as before.
    • sandbox::mount::validate_mount_target, for each of RESERVED_MOUNT_TARGETS, in place of the expression that it spelled out.
    • sandbox::mount::targets_overlap, which is now within(left, right) || within(right, left), in place of its inside closure.
  • targets_overlap (host::sandbox::mount) is unchanged in signature and visibility. These sites now share it:
    • validate_mounts, as before.
    • sandbox::options::validate_run_share_targets, in place of its own copy of the closure and the condition.
  • guest_paths_are_within_themselves_and_their_ancestors (new test in filesystem.rs) pins the rule directly. A path is within itself and its ancestors. /runner is not within /run, /run is not within /run/nvx, and /srv/run is not within /run.

Behavior

  • Same results. Each replaced expression is the body of the function that replaces it, or the disjunction of two such bodies. None has side effects, so every check accepts and refuses exactly the targets that it did.
  • Same order and messages. Each loop and its message are unchanged. validate_mount_target still checks the reserved trees in order, after the form of the target and before the exact reservations. validate_run_share_targets still compares each target with the aggregate target and then with the earlier targets, and still names them as --mount targets {other} and {target} overlap; a share cannot hide another.
  • Same interfaces. within stays private to the crate, and host::filesystem and host::sandbox are compiled under the same features, so nothing is added to the --no-default-features or guest builds. The public API, the protocol bytes, the golden fixtures, and the guest binaries' command lines, statuses, and output are all unchanged.
  • Existing coverage. tests/fixtures/sandbox.json and tests/fixtures/sandbox_options.json pin the messages of validate_mount_target, validate_mounts, and validate_run_share_targets, including their seeded random corpora. The unit tests mount_target_validation_rejects_unsafe_and_reserved_targets, launch_rejects_invalid_share_sets, and run_share_targets_bind_from_an_aggregate cover them as well.
  • Differential check. A throwaway test, kept out of the change, compared the old expressions with within and targets_overlap on all 1,863,225 ordered pairs of the 1,365 strings of up to five characters from /, a, ., and é. Of these pairs, 4,551 overlap. The results were identical for every pair.

Follow-ups

  • The guest programs repeat one entry point: nine main functions call sys::restore_sigpipe() and then return ExitStatus::from(run(&sys::args(), ...)).into(). Four of them pass the same three arguments: time, time_probe, ingress, and console_pending. A helper in guest/status.rs that takes the run function could serve them all. device_io.rs and managed_agent/mod.rs, which hold two of the nine, are changed by open pull requests (nvx-rs: print the nvx-device-io result line once for both benchmarks #633 and nvx-rs: add a neutral guest spawn API and launch managed workloads through it #629), so this run left the helper to a later one.
  • MountAccess::from_name, MountOwner::from_name, and MountKind::from_name in sandbox/mount.rs differ only in their names and in the noun of their message, unsupported sandbox mount {mode|owner|kind} ...; choose A or B. A private generic helper could share them, keeping each message.
  • Test helpers: sandbox/tests.rs and sandbox/mount/tests.rs each define the same policy(denied, allowed, writable) builder of a MountPolicy. tests/cli.rs repeats its stop-and-deprovision loop in both Ctrl-C tests.
  • No suspected bugs were found in the code that this change touches.

Validation

  • BASE: 4a6c3b6d21ccddbbf8f5c78d997adc3bb687b228 (origin/dev when the run started). nvx-rs: make the guest failure reporters return their CommandError #614 and a performance-baseline update have merged since; neither changes these files.
  • TREE: d4aff861e52aecb73327f306a15fa71569244377, the tree of this pull request's commit 58bd8756848477c75dd2491aa5c0e98e1d2c1dc0.
  • Release and OpenVMM: the hosts installed v0.1.0-dev.3ddd1ea9e603 with nvx.py download --hypervisor BACKEND. Its OpenVMM provenance is d0bdbed8f6e3b86271277dc22658109e2e633b2d, which matches the pinned gitlink.
  • Guest artifacts were rebuilt from TREE on prometheus30 with build-guest-binaries --output and test-guest-binaries --binaries (34 passed). The Alpine initramfs was then built with NVX_GUEST_BINARIES=… build-initramfs. The initramfs (54364e73…), its package manifest (2f04a9fa…), and its provenance (c8ae99f2…) were copied to the other two hosts with matching SHA-256 digests.
  • nvx-rs CLI: each host rebuilt it from TREE with cargo build --release --locked --features cli and recorded its provenance with nvx.py record-nvx-rs-provenance, so the microVM scenarios ran this change rather than the release's binary.
  • Local checks (Windows, Rust 1.95.0) on TREE:
    • rustfmt, Clippy with -D warnings (all features, and the library without default features), the --all-features and default tests, and rustdoc with -D warnings.
    • MSRV 1.89 check for nvx-rs and aci_edge_sandboxes, on behalf of the WHP host, which has no 1.89 toolchain.
    • The aarch64-apple-darwin check for both crates, on behalf of the KVM host, which lacks that target.
    • The differential test.
Host profile Backend Host type check-nvx-rs (no hypervisor) check-aci-edge-sandboxes (no hypervisor) Guest binaries + initramfs test-aci-edge-sandboxes test-microvm (default suite)
prometheus32 kvm baremetal Pass: Linux steps, including check-guest-binaries (320 passed); tests 876 passed, 2 ignored Pass: Linux steps, including agent bundling; tests 242 passed, 62 ignored Not run (unshare --user is denied) Pass, 18/18 Pass, 35 scenarios (41 runs)
prometheus30 mshv baremetal Not run (the Linux steps run on the KVM host) Not run Pass: builder (build, 34 tests, initramfs) Pass, 18/18 Pass, 35 scenarios (41 runs)
prometheus28 whp baremetal Pass: Windows steps, including artifact bundling; tests 597 passed, 2 ignored Pass: Windows steps, including agent bundling; tests 242 passed, 62 ignored Not run Pass, 18/18 Pass, 35 scenarios (41 runs)

The macOS checks for the KVM host's steps and the MSRV checks for the WHP host's steps ran locally, as listed above. test-microvm ran the 35 scenarios of its default suite, 41 runs with the processor counts of console-exit and smp.

Left to CI:

  • The virtual-machine runner matrix.
  • test-openvmm-unit and test-openvmm.
  • The test-microvm scenarios that run only when named: smp-lapic, managed-exec-config, and sandbox-lifecycle.
  • The Ubuntu and Azure Linux guests.
  • The Docker guest build and guest determinism.
  • Benchmarks and performance gates.
  • check-quality and validate-nvx. This change touches no script, Markdown, or file layout.

Four checks asked whether a guest path is a directory or lies below it,
comparing whole components, so that /runner is not inside /run.
filesystem::within answered it for the guest directories that a host
mapping must not cover, and the sandbox's share checks wrote the same
expression out three more times: validate_mount_target for the reserved
mount targets, targets_overlap as an `inside` closure applied both ways,
and validate_run_share_targets with a second copy of that closure and
of targets_overlap's condition instead of a call to targets_overlap.

- within is pub(crate), and its documentation states the rule, which
  its callers rely on.
- validate_mount_target calls within for each reserved target.
- targets_overlap is within applied in either direction.
- validate_run_share_targets calls targets_overlap, as validate_mounts
  already does.

The rule now has one definition, so the checks cannot drift apart, and
each call names the rule that it applies.

Each replaced expression is the body of the function that replaces it,
or the disjunction of two such bodies, and none has side effects, so
every check accepts and refuses the same targets, in the same order,
with the same messages. The golden fixtures of the sandbox shares and
options, sandbox.json and sandbox_options.json, pin those messages, and
a new filesystem test pins the whole-component rule. A throwaway
differential test compared the old expressions with within and
targets_overlap on all 1,863,225 ordered pairs of the 1,365 strings of
up to five characters from `/`, `a`, `.`, and `é`, and found no
difference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 20, 2026 11:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The refactor preserves the existing predicates, validation order, and diagnostics, with no unresolved findings.

No open issues 🎉

What changed in this PR

Centralizes guest-path containment checks in nvx-rs, removing duplicated logic while preserving validation behavior and diagnostics.

Changes:

  • Reuses within and targets_overlap across filesystem and sandbox checks.
  • Documents and directly tests whole-component containment.
File Description
nvx-rs/​src/​host/​sandbox/​options.rs Reuses targets_overlap for run-share validation.
nvx-rs/​src/​host/​sandbox/​mount.rs Uses within for reserved targets and overlap checks.
nvx-rs/​src/​host/​filesystem.rs Makes within crate-visible and adds documentation and tests.

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants