Skip to content

openvmm: pin the rollback as the only way to run a quiesced microVM - #1430

Merged
Pedro Henrique Penna (ppenna) merged 2 commits into
devfrom
agents/issue-1170-snapshot-resume-bypass
Nov 10, 2026
Merged

Pedro Henrique Penna (ppenna) merged 2 commits into
devfrom
agents/issue-1170-snapshot-resume-bypass

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1170

What and why

An ordinary resume could run a microVM again after the VM worker had quiesced
it for a guest-requested snapshot and handed its state to the snapshot
controller, without the snapshot's rollback (ResumeAfterFailedSnapshot), in
two ways:

  1. ReleaseSnapshotBoundary after QuiesceForSnapshot released the boundary
    of the quiesced VM: it resumed host input, completed the guest's snapshot
    transaction, and dropped the vCPU stop guard of the stopped VM, after which
    an ordinary Resume started the VM again.
  2. QuiesceForSnapshot stopped and saved a running VM that had requested no
    snapshot, and so held no boundary, and an ordinary Resume started it again
    at once.

Root cause: in OpenVMM's VM worker
(openvmm/openvmm_core/src/worker/dispatch/microvm.rs), the snapshot boundary
is what refuses an ordinary resume, since the boundary's RPC filter refuses
Resume while the boundary's vCPU stop guard is held, and two requests could
leave a quiesced VM without one: release_snapshot_boundary checked only that
a boundary was active, and quiesce_for_snapshot only that the VM ran. NVX's
controller, the VM controller of openvmm_entry, sends neither order: it
quiesces only at a boundary that the worker reported, and after a quiesce it
ends the source or rolls the snapshot back. But the capture's guarantee, that
a source runs again only through the rollback of a failed capture
(Commit Point), rested
on that order alone.

microsoft/openvmm#5010 has the worker hold the order itself, in two commits
on 46bfe02ed, the head of feature/microvm/dev:

  • b5d20bf84, petri: let a microVM test be its snapshot controller: petri
    can now give a microVM's VM worker the snapshot boundary's channels and let
    a VMM test send the controller's requests, in any order. A test takes the
    controller's own two ways out of a boundary.
  • 4c2697aef, openvmm_core: run a quiesced microVM again only through its rollback: release_snapshot_boundary refuses the boundary of a quiesced
    VM, before it resumes input or changes the boundary, so only the rollback,
    which starts the VM's state units first, releases it, and the boundary keeps
    refusing an ordinary resume until then; and quiesce_for_snapshot rejects,
    with SnapshotQuiesceError::Rejected and without stopping it, a VM that
    holds no boundary. VMM tests reproduce both of the issue's paths, and the
    OpenVMM snapshot guide states the rule.

Commits:

  • openvmm: pin the rollback as the only way to run a quiesced microVM moves
    the gitlink, and nothing else, from 46bfe02ed, which openvmm: pin input gating that keeps devices open to the vCPUs #1425 pins, to
    4c2697aef28d7fa69f788ce4e65b6554c1cfb935, the head of
    [DO NOT REVIEW] openvmm_core: run a quiesced microVM again only through its rollback openvmm#5010; its message carries the host evidence below. NVX
    needs no coordinated change: the control-session vectors, source excerpts,
    contract versions, and CPU profiles that nvx-rs copies from OpenVMM match
    the revision's.
  • doc: map the rollback-only resume in the OpenVMM roadmap maps positions
    239 and 240, unassigned, 109 commits past the mapped tip, with the mapped
    work that they change: the snapshot boundary that PR17's positions 072 and
    076 add, and the petri microVM support of PR15's position 065. They conflict
    with upstream main in every file but petri's start of the VM, so they follow
    PR15 and PR17 upstream.

Decisions:

Landing order: land microsoft/openvmm#5010 first, by fast-forwarding
feature/microvm/dev to 4c2697aef28d7fa69f788ce4e65b6554c1cfb935, since
OpenVMM's merge button squashes and would leave the pin off the branch; then
merge this pull request.

Validation

The head submitted is d4878c222, two commits on dev's b6ad70074, pinning
4c2697aef28d7fa69f788ce4e65b6554c1cfb935, the head of
microsoft/openvmm#5010, two commits on 46bfe02ed.

Reproduction before and after, with cargo xflowey vmm-tests-run selecting
the three new VMM tests and phase_1_lifecycle. "Before" is the pin with the
worker change reverted, a local scratch commit; "after" is the pin.

Profile Backend Host type Before After
nvx-kvm-test kvm virtual machine the 2 regression tests fail; the other 2 pass 4 passed
nvx-mshv-test mshv virtual machine the 2 regression tests fail; the other 2 pass 4 passed
nvx-whp-test whp virtual machine the 2 regression tests fail; the other 2 pass 4 passed
local (Windows 11) whp workstation the 2 regression tests fail; the other 2 pass 4 passed

Before, on every host, snapshot_quiesce_resumes_only_on_rollback (path 1)
fails with "the boundary's release returned Ok(()), the ordinary resume
returned Ok(()), the guest's continuation within 5s was Ok(Ok(())), and the
partition is running", and snapshot_quiesce_requires_a_boundary (path 2)
with "a snapshot quiesce outside a snapshot boundary returned Ok([...]), and
left the partition stopped", the saved state of all 14 state units.

NVX's snapshot scenarios, with OpenVMM built by nvx.py build-openvmm from
this change's code over the release artifacts that nvx-rs download
installed, v0.1.0-dev.995230d0ebb4, the executable's hash changing each
time: nvx.py test-microvm --scenario snapshot-core --scenario snapshot-rollback --scenario snapshot-tiers --processors 1 4 passed on
nvx-kvm-test (kvm), nvx-mshv-test (mshv), and nvx-whp-test (whp). They cover a
capture, a release without a capture, a rollback after a failed publication,
and the gated restore's acknowledgement. The builds were of fee6cf1bb (kvm)
and 634cfdc1a (mshv, whp), earlier versions of the pin's second commit that
differ from it only in vmm_tests.

Local, on Windows, with Rust 1.97.1:

  • OpenVMM, at the pin: cargo clippy --all-targets for openvmm_core,
    petri, and vmm_tests, and cargo doc --no-deps for the three with
    warnings denied, without warnings; cargo nextest run --profile agent -p openvmm_core -p petri, 152 passed; cargo xtask fmt, clean.
  • NVX: python scripts/nvx.py verify, passed. python scripts/local_gates.py --base origin/dev --run: test-changes, markdown-links, test-layout,
    openvmm-vectors, and openvmm-cpu-profiles passed; nvx-cli failed with 7
    errors, each a 10-second timeout of a shell script that
    test_host_loopback_allow_script_checks_publication_and_traffic or
    test_private_network_ingress_script_checks_policy_and_traffic runs through
    Git's sh.exe on this loaded machine. The same two tests fail the same way
    at origin/dev there, and this change touches no file under scripts/.
    python scripts/openvmm_pin.py check-roadmap --worktree and
    check-branch --topic --target origin/dev, passed;
    python scripts/check_test_changes.py --base origin/dev, no change to an
    existing test; openvmm_pin.py land (dry run), a fast-forward.
  • Review: a code-review pass found that the path-2 test's evidence that the
    VM still ran depended on timing; both regression tests now read the state of
    the partition's state unit through inspect.

CI run 42785929040
of this head, d4878c222, passed, with OpenVMM built at the pin: every
microVM suite on KVM, MSHV, and WHP, the NVX CLI tests on Linux and Windows,
the performance regression gate, Required status check, and OpenVMM CI of the pinned revision, once OpenVMM's CI of the pin,
run 42785708766,
had passed the 6 jobs that check-openvmm-ci requires. Its run vmm-tests
jobs on x64-linux-amd-kvm, x64-linux-intel-mshv, x64-windows-intel, and
x64-windows-amd ran the three new tests and phase_1_lifecycle, which all
passed. Its x64-windows-amd-snp job failed in 5 Hyper-V OpenHCL SEV-SNP TPM
tests, which NVX does not use and this change does not touch. The merge group
tests this pull request again, merged into the latest dev.

An ordinary resume could run a microVM again after the VM worker had
quiesced it for a snapshot and handed its state to the snapshot
controller, without the snapshot's rollback (#1170). The worker
released the snapshot boundary of a quiesced VM, after which nothing
refused a resume, and it quiesced a running VM that held no snapshot
boundary, so that none refused one. NVX's controller sends neither
order, but the capture's guarantee that a source runs again only
through the rollback of a failed capture rested on that alone.

Pin 4c2697aef28d7fa69f788ce4e65b6554c1cfb935, the head of
microsoft/openvmm#5010, two commits on 46bfe02ed, the head of
feature/microvm/dev:

- b5d20bf84 petri: let a microVM test be its snapshot controller
- 4c2697aef openvmm_core: run a quiesced microVM again only through
  its rollback

The revision has the worker refuse to release the boundary of a
quiesced VM, before it resumes host input or changes the boundary, so
that only the rollback, which starts the VM's state units first,
releases it; and reject a quiesce outside a guest-requested boundary
without stopping the VM. Its VMM tests take the snapshot controller's
part, which petri can now do.

Evidence: with the worker change reverted on the revision, both new
VMM tests fail on nvx-kvm-test (KVM), nvx-mshv-test (MSHV), and
nvx-whp-test (WHP), all virtual machines, and locally on WHP: the
release of a quiesced VM's boundary and an ordinary resume succeed,
the partition runs, and the guest continues; and a quiesce outside a
boundary returns the saved state of all 14 state units and leaves the
partition stopped. At the revision they pass on all four, as do the
test of the controller's own orders and phase_1_lifecycle. NVX's
snapshot-core, snapshot-rollback, and snapshot-tiers scenarios, with 1
and 4 vCPUs, pass on the three hosts with OpenVMM built from the
revision's code.

NVX needs no coordinated change: the control-session vectors, the
source excerpts, the contract versions, and the CPU profiles that
nvx-rs copies from OpenVMM match the revision's.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Maps NVX's new pin, 4c2697aef, the head of microsoft/openvmm#5010,
at position 240, after position 239, b5d20bf84, its other commit,
which follows position 238, 46bfe02ed, the head of feature/microvm/dev,
which microsoft/openvmm#5005 merged after microsoft/openvmm#5004 and
microsoft/openvmm#5003. The pin is 109 commits past the mapped tip, and
positions 132-240 stay unassigned.

Positions 239 and 240 change the snapshot boundary of PR17. Position
239 lets a VMM test be the snapshot controller: it gives the boundary's
channels to petri's handle of the snapshot-request port, which PR17's
position 076 adds without one, and to petri's launch of the VM worker,
whose boundary parameters position 072 adds, keeps them in the petri
microVM support of PR15's position 065, and adds a test beside position
065's cold-boot test. Position 240 has the worker of position 072
refuse to release the boundary of a quiesced VM and reject a quiesce
outside a boundary, extends the snapshot guide's paragraph that
position 076 adds, and adds tests beside position 239's. Replaying them
on upstream main conflicts in every file but petri's start of the VM,
since upstream main lacks the petri microVM support of position 065
and the boundary of positions 072 and 076, so they follow PR15 and PR17
upstream.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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.

🔵 Needs a closer look

The snapshot commit/rollback safety change requires exact-head KVM, MSHV, and WHP CI and upstream fast-forward verification, while OpenVMM #5010 remains open with required runtime checks incomplete.

No open issues 🎉

What changed in this PR

This PR pins the OpenVMM fix for #1170, preventing ordinary resume from restarting a microVM quiesced for a snapshot without rollback. The roadmap records the new revision and its upstream dependencies.

Changes:

  • Updates the documented pin to 4c2697aef and extends the chain through position 240.
  • Maps the snapshot-controller test support and rollback-only resume checks to their prerequisite work in PR15 and PR17.
File Description
doc/​openvmm-upstream-roadmap.md Updates the pin, commit counts, and mappings for positions 239–240, including upstream dependencies and replay conflicts.

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) added this pull request to the merge queue Nov 10, 2026
Merged via the queue into dev with commit 656af07 Nov 10, 2026
48 checks passed
@ppenna
Pedro Henrique Penna (ppenna) deleted the agents/issue-1170-snapshot-resume-bypass branch November 10, 2026 10:25
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.

openvmm: snapshot quiesce and boundary release allow ordinary Resume to bypass rollback

2 participants