Repository navigation
openvmm: let a platform restore attach an aggregate to a dormant filesystem slot - #948
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The refused-restore integrity check omits the workspace, allowing token overwrites to go undetected; workspace comparison and a regression case for mutation during refusal are needed.
1 open issue
What changed in this PR
This PR integrates OpenVMM support for attaching aggregate filesystem exports to dormant slots during platform snapshot restore, addressing #540 and enabling clone exports planned in #857.
Changes:
- Advances the OpenVMM pin and documents platform-only aggregate attachment rules.
- Adds a default-suite scenario covering mixed directory/file exports, access policies, invalid attachments, and snapshot integrity.
- Adds unit coverage and rebalances the KVM, MSHV, and WHP test shards.
| File | Description |
|---|---|
scripts/test_microvm_tests.py |
Tests scenario arguments, refusals, guest reports, and integrity checks. |
scripts/nvx_tools/microvm_tests.py |
Adds the aggregate restore scenario and untiered-snapshot refusal check. |
scripts/nvx_tools/microvm_test_scripts/platform-aggregate-export.sh |
Checks guest mounts, child types, access policies, and bind mounts. |
scripts/nvx_tools/microvm_shards.json |
Includes the scenario and rebalances platform shards. |
doc/usage.md |
Lists the scenario’s sandbox-control guest requirement. |
doc/run.md |
Documents direct OpenVMM platform aggregate restores. |
doc/openvmm-upstream-roadmap.md |
Records the new pin and its two OpenVMM commits. |
doc/ci.md |
Describes aggregate restore coverage and rejection cases. |
Review effort: Balanced
7c9bcb6 to
4cf5d4a
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
The OpenVMM pin changes snapshot-restore admission and pre-vCPU filesystem-policy enforcement across KVM, MSHV, and WHP, requiring human confirmation of the restore boundary and exact-head platform CI evidence.
No open issues 🎉
1 resolved since last review
Review effort: Balanced
4cf5d4a to
114dbb7
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
Runtime evidence targets d7892ffa8 rather than the reviewed pin 832d5bb48, leaving source equivalence and current-pin KVM/MSHV/WHP restore validation unconfirmed while relevant CI is still running.
No open issues 🎉
Review effort: Balanced
Pin microsoft/openvmm#4775 at 832d5bb48dadd2cc5221f2c8b8905e6b80d13d53, two commits on b92daa350, the previous pin, which is the head of feature/microvm/dev. A platform snapshot captured without a filesystem keeps the fixed virtio-fs slot dormant, and its restore could bind only one directory to it with --mount, although a cold boot attaches several host directories and files as the children of one aggregate. A template could therefore give its clones only part of what cold boots export. The first commit tests that the device takes a dormant slot's state, whose FUSE session the guest never initialized, into the device of an aggregate, which then saves the aggregate. The second lets the restore of a platform snapshot whose slot is dormant attach an aggregate with --mount-aggregate and its --mount-child options, and their --mount-deny, --mount-allow, --mount-write, and --mount-owner policy. The restore builds it with a cold boot's code, so it validates the children, the policy, and the owner as a cold boot does, and refuses what a cold boot refuses, before any vCPU runs. The slot's device and the captured command line, which names no filesystem, do not change, so OpenVMM exposes no layout and the guest learns of the export from its host. The dormant slot of a workload-start, instance-checkpoint, or untiered snapshot still binds only a --mount, a snapshot that predates the dormant slot binds nothing, and a snapshot that captured a filesystem still requires exactly that filesystem. Part of #540 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Record the two commits that the previous commit pins as positions 353 and 354 of the microVM chain, and move the NVX pin to 832d5bb48. Position 353 tests that the virtio-fs device takes the state of a dormant filesystem slot into the device of an aggregate. Position 354 lets a platform restore bind an aggregate to the slot that the snapshot left dormant, with its children, policy paths, and owner, validated as at a cold boot. They add behavior rather than fix a mapped commit, and position 354 changes the dormant-slot restore of PR18's position 113, which position 293 limited to one --mount when it added aggregates, so they stay unassigned, like positions 346-352. Note in the table's remarks that assigning them means revisiting that work. The pin is now 197 commits past the mapped tip. Positions 350-352 are no longer only a proposal: microsoft/openvmm#4773 merged them, so position 352, b92daa350, is the head of feature/microvm/dev. Say so in the scope, and name microsoft/openvmm#4775 as the proposal of positions 353 and 354. Part of #540 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
OpenVMM now lets the restore of a platform snapshot bind an aggregate of host directories and files to the filesystem slot that the capture left dormant, and Run documented only that a snapshot captured without a mapping restores with one new --mount. Add a Platform filesystem exports section beside Platform NIC templates for direct OpenVMM restores. It shows a template's restore with --mount-aggregate, --mount-child options of directories and a file, and a --mount-deny path, and says that OpenVMM validates them as a cold boot's before any vCPU runs; that the slot's device and the guest's command line stay as captured, so the guest learns of the export from its host and mounts and binds it itself; and that other tiers, and snapshots captured with an export, keep their rules. The virtio-fs host mapping section's rule for nvx-rs run still holds, since nvx-rs run restores only snapshots without sandbox blocks, whose dormant slot takes no aggregate; say so, and link the new section. Part of #540 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
No test covered a platform restore that binds an aggregate to the filesystem slot that the capture left dormant, which OpenVMM refused until the pinned change. Add the platform-aggregate-export microVM scenario to the default suite. The scenario captures a platform snapshot with sandbox blocks and without a filesystem, and restores it with the aggregate of the filesystem-shares scenario: a read-write workspace and a read-only tool cache, each with a denied path, a read-write data directory whose root exposes only its allowed paths and whose notes alone are writable, and a read-only and a read-write file, each without its directory. The clone keeps its captured command line, which discovers the slot but names no filesystem, so its guest checks that no virtfs_ or nvx_share= token appeared, mounts the aggregate itself, and finds its five children in the read-only root that only root can enter. It checks each child's kind, mode, and policy through that root, and binds a directory and a file child where it needs them, and the host then requires exactly the writes that the policy admits. Before the guest runs, OpenVMM must refuse the restores whose aggregates a cold boot refuses: overlapping directories, a file inside a directory child, a directory as a file and a file as a directory, a repeated name, a policy path of a file child, one outside every child, and a relative one, and on a Windows host caller ownership. A refused restore must print none of the guest's lines, and no restore may change the snapshot or the shared trees. filesystem-snapshot also requires OpenVMM to refuse an aggregate for the dormant slot of its untiered snapshot, which still binds a --mount, since only a platform snapshot's dormant slot binds an aggregate. The scenario shares the filesystem-shares scenario's trees, so their creation moves into _create_shared_trees. It needs sandbox blocks, so it runs only with the Alpine sandbox-control guest, like platform-filesystem. In the default suite, it took 3.4 s on bare-metal KVM, 4.5 s on MSHV, and 9.1 s on WHP, so each backend's shards are planned again with its time, which CI's duration reports replace once CI has run it. It joins shard 2 on KVM and MSHV and shard 1 on WHP, and the planner moves command-line-limit on KVM, swaps platform-zero-fill and restore-memory on MSHV, and moves virtio-net on WHP. The default suite passed on each of those hosts. With the unpatched OpenVMM of development release v0.1.0-dev.1ee6489241a8, d2338ea22, the capture succeeds and the clone's restore fails at "a dormant-slot snapshot can attach only a --mount on restore". Unit tests cover the guest's checks, the capture and the restore's arguments, each refusal's aggregate against the host's trees, the Windows-only refusal, a missing report, a guest failure, writes beyond the policy, and the snapshot's and trees' integrity. doc/ci.md describes the scenario, and doc/usage.md lists it among the scenarios that need the sandbox-control guest. Part of #540 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
114dbb7 to
c241bbb
Compare

Fixes #540. Pins microsoft/openvmm#4775 at
832d5bb48dadd2cc5221f2c8b8905e6b80d13d53, which lets the restore of a microVM platform snapshot attach an aggregate of host directories and files, with its access policy and owner, to the filesystem slot that the capture left dormant, validated as at a cold boot, before any vCPU runs. It adds a microVM scenario that restores one platform snapshot with such an aggregate on KVM, MSHV, and WHP.Based on
devatba30ff904. microsoft/openvmm#4775, two commits directly onb92daa350, the revision thatdevpins, has landed: its head,832d5bb48, is the head offeature/microvm/dev. This branch's four commits are its whole diff.Change
205233511openvmmopenvmmb92daa350→832d5bb48: the two commits of microsoft/openvmm#4775464b89155docdoc/openvmm-upstream-roadmap.md--mount; moves the NVX pin; and records that microsoft/openvmm#4773 merged positions 350-352 intofeature/microvm/dev3bdcee72ddocdoc/run.mdnvx-rs runrule still holds, links itc241bbbbascriptsscripts/nvx_tools/microvm_tests.py,scripts/nvx_tools/microvm_test_scripts/platform-aggregate-export.sh,scripts/test_microvm_tests.py,scripts/nvx_tools/microvm_shards.json,doc/ci.md,doc/usage.mdplatform-aggregate-export;filesystem-snapshotalso refuses an aggregate on its untiered dormant slot; shards replannedOpenVMM changes
057a2eaa6). A capture that leaves the slot dormant saves a dormant device state, without an attachment, a policy, objects, or an initialized FUSE session, which the device restores into any attached device. A new unit test checks it for an aggregate of a read-write directory and a read-only file, and that the device then saves the aggregate rather than the dormant slot. Only a test changes.832d5bb48). At the restore of a platform snapshot whose slot is dormant,--mount-aggregatewith its--mount-childoptions, and their--mount-deny,--mount-allow,--mount-write, and--mount-ownerpolicy, attach an aggregate. The restore builds it with a cold boot's code, so it validates the children's kinds, modes, names, overlaps, and limits, the policy paths, and the owner as a cold boot does, and refuses what a cold boot refuses, before any vCPU runs. The slot's device keeps the stable ID, tag, MMIO range, interrupt, features, and queues that the snapshot recorded, and the guest keeps its captured command line, which names no filesystem: OpenVMM exposes no layout, and the guest learns of the export over its own control channel (nvx-rs: aggregate exports for clones #857). The restore contract still records the dormant slot. Only the platform tier changes: the dormant slot of a workload-start, instance-checkpoint, or untiered snapshot still binds only a--mount, a snapshot that predates the dormant slot binds nothing, and a snapshot that captured a filesystem still requires exactly that filesystem.--mount, and a restore without an attachment, behave as before.Tests
platform-aggregate-exportcaptures a platform snapshot with sandbox blocks and without a filesystem, and restores it with the aggregate of thefilesystem-sharesscenario: a read-write workspace and a read-only tool cache, each with a denied path, a read-write data directory whose root exposes only its allowed paths and whose notes alone are writable, and a read-only and a read-write file, each without its directory. The clone keeps its captured command line, so its guest checks that novirtfs_ornvx_share=token appeared, mounts the aggregate itself, finds the five children in the read-only root that only root can enter, checks each child's kind, mode, and policy, and binds a directory and a file child; the host then requires exactly the writes that the policy admits. Before the guest runs, OpenVMM must refuse the restores whose aggregates a cold boot refuses: overlapping directories, a file inside a directory child, a directory as a file and a file as a directory, a repeated name, a policy path of a file child, one outside every child, and a relative one, and on a Windows host caller ownership. A refused restore must print none of the guest's lines, and no restore may change the snapshot or the shared trees. It runs with the Alpine sandbox-control guest only, likeplatform-filesystem, and joins shard 2 on KVM and MSHV and shard 1 on WHP, each planned with the scenario's time in the bare-metal default suite below.filesystem-snapshotalso requires OpenVMM to refuse an aggregate for the dormant slot of its untiered snapshot, which still binds a--mount.--mount-owner; for snapshots without the dormant slot and of a single directory; and for the tier rule at every tier.Of #540's acceptance cases, the scenario covers a restore of a dormant slot with an aggregate of directory and file children, with mixed modes and policies, and the refusal of invalid aggregates, on every backend. Snapshots with a captured attachment keep their exact-match rules:
filesystem-sharesandfilesystem-snapshot, which restore captured aggregates and directories, pass unchanged in the default suite, as do OpenVMM's exact-match unit tests. A current OpenVMM cannot capture a snapshot without the dormant slot's version, so OpenVMM's unit tests cover that refusal, and the tier rule's unit test covers workload and instance snapshots, asfilesystem-snapshotdoes for an untiered one.Evidence
At
114dbb7be, this branch before its latest rebase, on the bare-metal hosts prometheus32 (Linux/KVM), prometheus30 (Linux/MSHV), and prometheus28 (Windows/WHP), each in a separate worktree with OpenVMM built there from832d5bb48and the guest of development releasev0.1.0-dev.3beaf1646086on KVM and MSHV orv0.1.0-dev.be07e2f8ada4on WHP, both of which pinb92daa350:platform-aggregate-exportwith the fixfilesystem-snapshotwith the fixWith the unpatched OpenVMM
b92daa350ofv0.1.0-dev.3beaf1646086on KVM, the capture succeeds and the clone's restore fails ata dormant-slot snapshot can attach only a --mount on restore. CI run 40767318100 passed at114dbb7be, whereplatform-aggregate-exportran in shard 2 on KVM and MSHV and in shard 1 on WHP.Before the rebases, the same hosts ran these checks at
02838f0ae, each with OpenVMM built there fromd7892ffa8and the guest of development releasev0.1.0-dev.1ee6489241a8:platform-aggregate-exportwith the fixfilesystem-snapshotwith the fixplatform-aggregate-exportin the default suiteWith that release's unpatched OpenVMM
d2338ea22on MSHV, the capture succeeds and the clone's restore fails ata dormant-slot snapshot can attach only a --mount on restore.In both runs, every clone reported
NVX-POST-RESTORE-OK: tier=platformandNVX-PLATFORM-AGGREGATE-OK, and every refusal failed before the guest ran, with:microVM filesystem host paths must not overlap(overlapping directories, and a file inside a directory child);invalid --mount-child 0, caused by the root's not being a regular file or a plain directory;invalid --mount-aggregate or --mount-child options, caused by a child name that is not unique;--mount-child 4 exposes the file ...;microVM denied path resolves outside the filesystem export roots;with --mount-aggregate, --mount-deny requires an absolute host path: secrets; on WHP,--mount-owner caller requires a Linux host; and forfilesystem-snapshot's untiered snapshot,--mount-aggregate on a dormant filesystem slot requires a platform snapshot.On prometheus28,
nvx-rs downloadfails withcurl: (43) A libcurl function was given a bad argumentunder the host's curl 8.8.0, so in both runs its guest artifacts came fromnvx-rs downloadon another Windows machine, with curl 8.21.0, and their SHA-256 digests matched after the copy.Equivalence and validation
02838f0ae, ondevatd77908efc, andplatform-aggregate-exportandfilesystem-snapshotagain at4cf5d4afe, ondevatdce536f1dwith OpenVMMd7892ffa8, where the scenario passes in 3.4 s on KVM, 4.0 s on MSHV, and 8.8 s on WHP, and those two scenarios again at114dbb7be, as Evidence shows.4cf5d4afediffers from02838f0aein the scenario only by the review fix: the integrity check after the refused restores also compares the workspace. It also differs in the shard partition, which the hosts' unsharded runs don't read and which is planned with their in-suite times, in the scenario commit's message, and in its base:devgained nvx-rs: capture templates into their store, and publish them #887 and tests: capture templates in VMs through the runtime adapter #933, which change none of this branch's files.b92daa350.114dbb7beis4cf5d4aferebased ontodevat1853252ca, and [DONT REVIEW] microVM: let a platform restore attach an aggregate to a dormant filesystem slot openvmm#4775's head832d5bb48isd7892ffa8rebased ontob92daa350. [DO NOT REVIEW] disk_blob: bound HTTP blob requests and retry transient failures openvmm#4773 movedfeature/microvm/dev, anddev's pin, tob92daa350with three commits that change onlydisk_blob, so the gitlink and roadmap commits are rebuilt on that pin, and the roadmap's positions 350 and 351 become 353 and 354.git range-diffshows [DONT REVIEW] microVM: let a platform restore attach an aggregate to a dormant filesystem slot openvmm#4775's two patches, and this branch'srun.mdand scenario patches, unchanged;dev's other new commits changenvx-rs, the runtime adapter, the performance scripts, and the network text ofdoc/run.mdanddoc/usage.md, none of this branch's lines. The scenario commit's message now names the release whose unpatched OpenVMM the negative control ran,v0.1.0-dev.1ee6489241a8, rather than calling it the latest.ba30ff904. This head is114dbb7berebased ontodevatba30ff904, which still pinsb92daa350. guest: write back the host shares before every capture #976 appends a paragraph onfilesystem-snapshot's write-back of shared mappings todoc/ci.mdwhere this branch appends the scenario's, so the scenario's paragraph now precedes it, and changesrun_filesystem_snapshot's live capture, which Git merges with this branch's refusal for the dormant slot; run: pin the refusal of share targets that would break a kernel token #970 and nvx-rs: reject unbalanced sandbox disk path brackets #971 change other lines ofdoc/run.mdanddoc/usage.md. The other patches are unchanged.cargo clippy --all-targetsandcargo doc --no-depsforvirtiofsandopenvmm_entry,cargo nextest run --profile agentfor both (339 passed), andcargo xtask fmt --fix(clean). The protocol vectors and CPU profiles thatnvx-rscopies are unchanged:openvmm_vectorsandopenvmm_cpu_profilespass with--ignored.nvx.py verify, the Markdown link check,ruff check,ruff format --check,compileall,pyrightfor Linux and Windows,test_microvm_tests.py,test_markdown_links.py, andtest_module_diagrams.py(418 tests, 12 skipped), andnvx.py test-microvm --help, which lists the scenario. At114dbb7be, thevalidate-nvxunittest list withouttest_setup_azure.py(1,320 tests, 54 skipped). The Gitsh.exeruns oftest_host_loopback_allow_script_checks_publication_and_trafficexceed their 10 s limit intermittently on this machine, on this branch and ondevalike, and the case passes on a re-run. At321888b05, before the review fix and the rebases: the 18 shell cases oftest_setup_azure.py, which need a Linuxbash, fail the same way ondevhere, andtest_adversarial.py,test_hosts.py, and the other CLI--helpchecks pass.shellcheckandshfmt, which run in Docker, although no checked shell script changed;check-nvx-rs, sincenvx-rsdoesn't change; and the OpenVMM VMM tests, which cannot reach an aggregate through the management RPC. CI runs all of them.Notes
doc/designisn't edited: draft doc: describe the managed layered root and platform-clone activation #591 rewrites those files, and nvx-rs: aggregate exports for clones #857, which this unblocks, makesnvx-rsattach a clone's aggregate at its restore.--restore-network-policyis.Exact NVX head submitted to CI:
c241bbbba97a74a8a8778ed11b92df57d7f9758b.