Repository navigation
nvx-rs: hold the guest port permission for one transaction at a time - #662
Merged
Pedro Henrique Penna (ppenna) merged 2 commits intoOct 21, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The probe lacks sample serialization, and acknowledgement permission failures can incorrectly report restore completion.
2 open issues
What changed in this PR
Scopes guest I/O-port permission to individual transactions, supporting #619’s guest-runtime isolation work.
Changes:
- Adds thread-bound permission ownership, rollback, and revocation tracking.
- Updates time commands and daemon startup to respect permission boundaries.
- Adds unit tests and restored-VM port-access probes.
| File | Description |
|---|---|
| scripts/test_microvm_tests.py | Checks snapshot probe integration. |
| scripts/nvx_tools/microvm_tests.py | Requires the port-access denial marker. |
| scripts/nvx_tools/microvm_test_scripts/snapshot-core.sh | Runs the probe after restore. |
| nvx-rs/src/guest/time/test_mode.rs | Adds port-access test hooks. |
| nvx-rs/src/guest/time/system.rs | Scopes transactions and guards process creation. |
| nvx-rs/src/guest/time/daemon.rs | Guards startup and separates acknowledgement from signalling. |
| nvx-rs/src/guest/time/commands.rs | Uses transaction-scoped permissions. |
| nvx-rs/src/guest/time/checks.rs | Avoids starting threads while permission remains. |
| nvx-rs/src/guest/sys/port.rs | Implements permission ownership and regression tests. |
| nvx-rs/src/guest/sys/mod.rs | Exports permission helpers. |
Review effort: Balanced
Linux keeps the permission that ioperm grants in each thread, and the threads and processes that a holder starts inherit it, across exec too. The time tools took it when a command started and kept it, so the boot checks' threads, the time daemon, and its restore workers held it for their whole lives. A port transaction now takes the permission on the thread that uses the ports, rolls a partial grant back, and revokes the permission when it ends. Until a later revocation succeeds, a failed one keeps the thread from starting a thread or a process, and a command that ends while the thread may still hold the permission fails. The capture request and the packet reads, the restore acknowledgement, each time sample, each event's debug-console write, and the sample and generation-id commands are each one transaction, and none of them reports an event while it holds the permission. A capture that finishes a restore and cannot take the permission for its acknowledgement leaves the restore unfinished, for nvx-snapshot's restore-finish to acknowledge. Unit tests fake the permission to cover a refused grant, a rolled-back partial grant, failed revocations, which stop the time tools before they fork or start a thread, and an acknowledgement that cannot take the permission. In a VM, snapshot-core runs the new nvx-time test port-access hook after each restore: a process that nvx-time starts after a transaction must not reach OpenVMM's ports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b57d5617-d565-4d34-a871-0077d2c11184
Enrique Saurez (esaurez)
force-pushed
the
esaurez/a1-guest-g5-time-20261020
branch
from
October 20, 2026 21:49
680b261 to
4496196
Compare
Enrique Saurez (esaurez)
marked this pull request as ready for review
October 20, 2026 22:05
This was referenced Oct 21, 2026
Enrique Saurez (esaurez)
requested a review
from Pedro Henrique Penna (ppenna)
October 21, 2026 16:09
Pedro Henrique Penna (ppenna)
left a comment
Contributor
There was a problem hiding this comment.
We need some minor changes here.
An event's write to OpenVMM's debug console and the time daemon's discipline poll ended their port transactions without a word when the kernel refused to revoke the permission. The thread then starts nothing until a later transaction revokes it, and the daemon's standard streams are on the null device. Both now write the failure to the kernel log at KERN_ERR, such as "nvx-time: ioperm revoke: Cannot allocate memory". The sample and generation-id commands, and the port-probe test hook, now end their transactions at the end of a scope rather than with an explicit drop. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b57d5617-d565-4d34-a871-0077d2c11184
Pedro Henrique Penna (ppenna)
approved these changes
Oct 21, 2026
Pedro Henrique Penna (ppenna)
deleted the
esaurez/a1-guest-g5-time-20261020
branch
October 21, 2026 20:03
This was referenced Oct 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

nvx-timetook the permission for OpenVMM's I/O ports when a command started and held it until the command ended. Linux keeps that permission in the thread's I/O bitmap, which the processes the thread starts inherit and keep acrossexec, so the time daemon, its restore workers, and anything they started could reach the ports too.This change scopes the permission to each port transaction, as the time-and-reseed item of #619 asks, and changes nothing else.
sys::port: aPortAccesstakes the permission on the calling thread for one transaction, refuses a second one on that thread, rolls back a partial grant, and revokes the permission when the transaction ends. A failed revocation is recorded for the thread, andcheck_releasedkeeps the thread from starting a thread or a process until a later transaction revokes it.nvx-time: the runtime no longer holds the ports. Each violation event writes the debug console in a transaction of its own. A best-of-N sample, the capture request with its restore-packet reads, the generation-ID read, and the restore acknowledgement are one transaction each, and each ends before an event is reported or a file is written. The capture parses the packet's header and body in its transaction, because the header sizes the body's reads, and checks the packet after it.1, sonvx-snapshotfinishes it withrestore-finish --ack, which acknowledges it or fails closed.fork_process, which first checks that the thread holds none of the permission. When a revocation failed, the boot checks run without their threads, the daemon stops before it starts anything, and a command fails at its end.nvx-time: ioperm revoke: Cannot allocate memory. The daemon's standard streams are on the null device.Tests:
snapshot-coreruns a new test hook,nvx-time test port-access, after each restore. It takes a sample under the lock that serializes port-B transactions, as the daemon does, then starts a child that reads the port-B status and snapshot ports without taking the permission. The processor must refuse both reads, so the scenario printsNVX-SNAPSHOT-PORT-ACCESS-denied, which the harness requires. A control child that takes the permission itself must read them.The typed calls of the same item follow in #663, which is stacked on this PR.
Part of #619. Refs #535.
Validation
Local, on a Windows/WHP host, with the Linux steps as root in a Linux VM on it. The Rust and guest checks ran on this head, e079bca, on
devat 3d06d42. The Python checks ran on an earlier revision with the same scripts.python scripts/nvx.py verify.nvx-rs, withNVX_BUNDLE=skip:cargo fmt --all --check;cargo clippy --locked --all-targets --all-features -- -D warningsandcargo clippy --locked --lib --no-default-features -- -D warnings;cargo test --locked --all-featuresandcargo test --locked;bundledartifacts test, andcargo doc --locked --no-deps --all-featureswith-D warnings;cargo +1.89 check --locked --all-targets --all-features, andcargo check --locked --all-targets --all-features --target aarch64-apple-darwin.python3 scripts/nvx.py check-guest-binaries, thenbuild-guest-binariesandtest-guest-binaries.tests/runtime-adapter:cargo fmt,clippy,build,testand the 1.89 check in the default,host, andguest(musl) configurations.ruff check,ruff format --check,pyrightfor Linux and Windows, and the Markdown link check;shellcheckforsnapshot-core.sh.Everything passes except three tests that fail the same way on
dev3d06d42 in this environment:tests/cli.rs'snvx_rs_reproduces_the_recorded_scenariosfails as root, which caller-owned shares refuse.test_adversarial.py's SIGKILL status case fails as root.test_setup_azure.py's shell tests fail on Windows, wherebashis a Linux VM's. They pass on Linux.Real VMs, with
python scripts/nvx.py test-microvm --backend <backend>and its default scenarios, includingguest-boot,snapshot-core,time-abi-conformance,smp-snapshot,snapshot-tiers,restore-processors,restore-memory, andrestore-downtime:snapshot-coreand the four scenarios after it passed in separate runs. Two full runs had stopped atsnapshot-core's check that a restore advances the wall clock and the uptime alike, while NTP stepped the Linux VM's clock forward by about 1.5 s every 34 s; the same check failed on nvx-rs: run the guest time and reseed steps through typed calls #663's head in those conditions.127.0.0.1. The guest's own checks passed in both runs, the scenarios don't involve the time tools, and both reruns passed.snapshot-coreprintedNVX-SNAPSHOT-PORT-ACCESS-deniedafter each restore, in every run.--cpu-profile autoselects (intel.alderlake.v1), and OpenVMM fails withE_PROFILE_UNSUPPORTED, as nested MSHV did withamd.genoa.v1. Those runs therefore passed OpenVMM--cpu-profile host, through a local wrapper of its binary.Stripped static guest binaries (
build-guest-binaries) and the Alpine initramfs, in bytes:dev3d06d42nvx-timenvx-reseedinitramfs.cpio.gzinitramfs.cpio, uncompressedLeft to CI, which passed every pull-request check on this head, merged into
devat 7269dbf: nvx-rs on Linux and Windows as a non-root user, the CLI tests, lint and typing, the shared artifacts, the performance gate, and the KVM, MSHV, and WHP microVM and platform tests with the built-in CPU profiles. Merged intodevat 6f0face, which since checks the guest build's inputs (#710),check-guest-binaries,build-guest-binaries, andtest-guest-binariespass locally.