Repository navigation
nvx-rs: make the guest failure reporters return their CommandError - #614
Merged
Pedro Henrique Penna (ppenna) merged 1 commit intoOct 20, 2026
Merged
Conversation
nvx-console-pending, nvx-mmio-write, and nvx-reseed report each failed system call with a reporter and then fail with CommandError::Operation. Their reporters returned nothing, so ten call sites repeated both steps in a block: the open, FIONREAD, and close sites of console_pending's fail closure, the open, mmap, munmap, and close sites of mmio_write's perror, and the open and read sites of the fail closure of reseed's read_seed. reseed() also wrote read_seed's "nvx-reseed: open PATH: REASON" diagnostic a second time, inline, for /dev/random. Each reporter now returns CommandError::Operation, as ingress::usage and managed_agent::fatal return their failures, so each site is one expression, such as .map_err(|error| perror(err, b"open /dev/mem", error))?, or return Err(file_failure(...)). In reseed, the function file_failure replaces read_seed's closure and also reports reseed()'s failure to open /dev/random, so that diagnostic has one definition. Behavior is unchanged. Every site writes the same bytes with one sys::report write, as before, and fails with the same CommandError::Operation, so the programs' standard error and exit status 1 do not change. Only the guest programs, which build for Linux with the guest feature, change. The unit tests of the three modules check these diagnostics byte for byte: console_pending's open and FIONREAD failures, mmio_write's open and mmap failures, and reseed's open and read failures of the seed file and its failure to open /dev/random, the last two through the same expected-message helper. tests/guest_tools.rs checks the open failures of nvx-console-pending and nvx-mmio-write in the built binaries. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused refactor preserves failure behavior and cleanup order, with no unresolved blocking issues.
No open issues 🎉
What changed in this PR
Simplifies failure handling in three NVX guest tools while preserving diagnostics, returned errors, and exit statuses.
Changes:
- Makes failure reporters return
CommandError::Operation, shortening ten call sites. - Shares reseed file-error reporting between seed reads and opening
/dev/random.
| File | Description |
|---|---|
| nvx-rs/src/guest/reseed.rs | Extracts a shared file-failure reporter. |
| nvx-rs/src/guest/mmio_write.rs | Returns errors directly from perror. |
| nvx-rs/src/guest/console_pending.rs | Returns errors directly from the failure closure. |
Review effort: Balanced
This was referenced Oct 20, 2026
Pedro Henrique Penna (ppenna)
deleted the
nvx-rs-quality/failure-reporters
branch
October 20, 2026 11:11
This was referenced Oct 20, 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.
Summary
nvx-console-pending,nvx-mmio-write, andnvx-reseedreport each failed system call with a reporter and then fail withCommandError::Operation. Their reporters returned nothing, so ten call sites repeated both steps in a block of four lines, andreseed()wroteread_seed'snvx-reseed: open PATH: REASONdiagnostic a second time, inline, for/dev/random.Each reporter now returns the
CommandError::Operationwith which its caller fails, asingress::usageandmanaged_agent::fatalalready return their failures, so each site is one expression:.map_err(|error| perror(err, b"open /dev/mem", error))?, orreturn Err(file_failure(...)). Inreseed.rs, a function,file_failure, replacesread_seed's closure and also reportsreseed()'s failure to open/dev/random, so that diagnostic has one definition. The three programs lose 21 lines.This is the ninth
nvx-rs-quality/pull request. #603, #605, #606, #607, #608, #609, #611, and #613 merged, #613 while this run was underway; all had approving Copilot reviews and no other feedback to follow, and none was closed without merging. Those runs changedprotocol/exec.rs, the managed agent'sexec.rs,loopback.rs, andseam.rs,host/filesystem.rs, the time tools,guest/sys/socket.rsandspawn.rs, andtesting/fake_openvmm.rs. This run surveyed the crate by reading and with a throwaway duplicate-window script, exact and with names and literals abstracted, and skipped every file that an open pull request todevchanged when it started: #613 (nvx-rs/src/testing/fake_openvmm.rs), #610 (aci_edge_sandboxes/README.mdanddoc/project-structure.md), and #585 (aci_edge_sandboxes/src/async_api.rsandtests/async_api.rs).Of the candidates that it found, this one, a follow-up that #609, #611, and #613 listed, simplifies production code of three guest programs that no earlier run touched, and brings them to a convention that two other programs already follow. The argument-vector builders of the program tests, under Follow-ups, would remove somewhat more per changed line, but only from tests; where the choice was close, I took the change to the shipped programs. The other candidates are listed under Follow-ups too.
Changes
All changes are in
nvx-rs/src/guest/: 34 insertions and 55 deletions in three files.console_pending.rs: thefailclosure returnsCommandError::Operation. The sites that now call it in one expression are the open of the device, itsFIONREADioctl throughsys::console_pending, and its close.mmio_write.rs:perrorreturnsCommandError::Operation. The sites are the open,mmap,munmap, and close of/dev/mem.reseed.rs:file_failure(err, operation, path, reason), which reportsnvx-reseed: OPERATION PATH: REASONand returnsCommandError::Operation, replacesread_seed'sfailclosure. The sites that now share it are:reseed()'s open of/dev/random, which wrote the same diagnostic inline;read_seed's open of the seed file, and its end-of-file and read-error failures.reseed()'srandom ioctlfailure, whose diagnostic names no path, and the diagnostics that do not report anerrno, such as the page-boundary check, keep their own code. No signature, public item, message, error kind, exit status, protocol byte, fixture, or other file changes.Behavior
Each site writes the same bytes in the same single
sys::reportwrite, which writesparts.concat()at once, and fails with the sameCommandError::Operation, exit status 1.reseed()'s open failure wrote[b"nvx-reseed: open ", path, b": ", text, b"\n"]and now writes[b"nvx-reseed: ", b"open", b" ", path, b": ", reason, b"\n"], the same bytes.A reporter still runs only on its failure path, and nothing runs between the report and the return.
Only the guest programs, which build for Linux with the
guestfeature, change: no host code, no code that the host and the guest share, and nothing in the--no-default-featuresbuild. The command lines, exit statuses, and byte output of every guest binary are unchanged.Tests that cover the change:
guest::console_pending::tests::reports_failures_with_the_errno_description: the open andFIONREADfailures;guest::mmio_write::tests::reports_failures_as_perror: the open andmmapfailures;guest::reseed::tests::reports_seeds_that_cannot_be_read: the seed file's open, end-of-file, and read failures;guest::reseed::tests::reports_a_failed_reseed: the failure to open/dev/random, with the expected-message helper of the seed file's failures;tests/guest_tools.rs, on the built binaries,console_pending_reports_the_unread_bytes, an open failure, andmmio_write_reports_an_inaccessible_dev_mem, which checks the open failure where/dev/memis inaccessible, as it is to an unprivileged user.They ran and passed in the Linux test run with all features on prometheus32 and in its musl run of
check-guest-binaries, and theguest_toolstests also against the guest binaries built from TREE on prometheus30. The close andmunmapfailures have no test, as before, since a test cannot make them fail.Follow-ups
The survey turned these up, and this change leaves them alone. I found no suspected bug.
errnostill pairsys::reportwithreturn Err(CommandError::Operation):mmio_write's invalid page size and its two page-boundary checks,console_pending's failed write, and, inreseed.rs, therandom ioctlfailure, the three generation-ID checks, and the output andgetrandomfailures. A reporter inguest/status.rsthat returns the failure could serve them, and the time tools' similar sites.console_pending,exit,device_io,mmio_write,reseed,time, andtime_probebuild a program's argument vector ofCStrings in the same way, in about 50 lines. A helper inguest::sys::testscould serve them all now that Ship nvx-rs in NVX releases #598, which changedreseed.rs,time/mod.rs, andtime_probe/mod.rs, has landed.mainof each of the nine guest programs restoresSIGPIPEand converts its outcome withExitStatus::from(...).into(); four of them are identical. One entry helper inguest/status.rscould hold that convention.host/:sandbox/mount.rs,MountAccess,MountOwner, andMountKindrepeatfrom_namewith the same message form, andsandbox/mount/tests.rsandsandbox/tests.rsdefine the samepolicytest helper;session.rs,features,map_host_paths, andnext_exec_eventrepeat the request-ID check,map_host_pathsandnext_exec_eventtruncate an error category to 64 characters the same way,pingandstopdiffer only in their request and reply kinds and their message, and the tests'ScriptedandRespondertransports have identicalreadmethods;process::tests::a_process_runs_until_its_last_thread_exitsandcli::sandbox::identity::tests::a_vm_runs_until_its_last_thread_exitsrepeat the fixture of a Python process whose main thread exits first;filesystem.rs,network/mod.rs, andsandbox/options/tests.rseach define the same option-values test helper;Instance::runtimeandInstance::launchininstance.rsread a record and refuse anotherSTATE_FORMATin two shapes.cgroup::tests::setupanddispatch::tests::direct_systemare identical,dispatch::tests::maps_payloadandmappings::tests::payloadare the sameMAPSencoder, anddispatch::tests::mapping_systembuilds the host-export tree ofmappings::tests::setup;run_fileandrun_networkinguest/device_io.rs, and thedevice_iohelper intests/guest_tools.rs, which repeats the spawn and output collection ofrun;daemon.rs, and the port-window lock failures thatcmd_sampleandcmd_generation_idincommands.rsreport with the same four lines;MAPSdecoders ofprotocol/maps.rs, whose differences nvx-rs: decode the EXEC header once for both payload decoders #603 described, which want a decision before they share code.Validation
BASE:
131786652a20048ed4b951580cc025bdae40238c, which wasorigin/devwhen the run started: a performance-baseline update on top of the merge of nvx-rs: build the plain spawn descriptors from the managed ones #611. Before this pull request was opened,origin/devhad advanced by the merge of nvx-rs: parse fake OpenVMM --mount-child values with one function #613 and another baseline update, neither of which changes the three files, and no open pull request todevchanges them.TREE:
d3ebd6da782964648e30b282e1050c4a9b6d40db, the tree of this commit. Every host applied the patch to BASE and checked thatgit write-treeequals TREE.Release on every host:
v0.1.0-dev.5fd538e726d2, from the merge of setup: integrate reusable runner setup and Azure VMSS bootstrap #522, the nearest first-parent ancestor of BASE that had packages. Its OpenVMM,d0bdbed8f6e3b86271277dc22658109e2e633b2d, is the pinned revision.downloaddid not install the release'snvx-rs, whose sources differ, so each host builtnvx-rsfrom TREE with Cargo and recorded its provenance. Each also pre-built themanaged_lifecycleandopenvmm_e2etest binaries, so that no Cargo build overlapped its hypervisor tests.Guest artifacts were rebuilt from TREE on prometheus30, whose user namespaces let Alpine's apk run its scripts, with these steps:
build-guest-binaries --output;test-guest-binaries --binaries, where 34 tests passed;build-initramfswithNVX_GUEST_BINARIES.initramfs.cpio.gz(SHA-2564ef90a58ebbc2abfaf47474147972eb2d0cfe57d087c4d7af58bcecff4207a00), its packages manifest, and its provenance went through this machine to the other two hosts, where the SHA-256 digests matched.Local checks on Windows with Rust 1.95.0:
-D warnings: forx86_64-unknown-linux-gnu, which builds theguestmodule, with all targets and features and for the library without default features, and for the guest build onx86_64-unknown-linux-musl, ascheck-guest-binarieslints it;aarch64-apple-darwin) checks, for bothnvx-rsandaci_edge_sandboxes.test-aci-edge-sandboxestest-microvm, default suitecheck-guest-binaries(320 tests), 609nvx-rstests with all features and 263 with the default ones, artifact bundling, 157 and 85aci_edge_sandboxestests, and agent bundling; the macOS checks ran locallynvx-rstests with all features and 263 with the default ones, artifact bundling, 157 and 85aci_edge_sandboxestests, and agent bundling; the MSRV checks ran locallyThe Linux gates and tests ran in the foreground of their SSH sessions, with every signal at its default disposition (
SigIgn: 0), so the CLI's Ctrl-C tests ran as intended. Every gate and test passed at its first attempt. No host fell back to a host CPU profile outside thecpu-profile-fallbackscenario, which forces that fallback. The time-ABI evidence lines counted no capture or restore over its CPU-time budget, and no boot over it on prometheus30 and prometheus28. On prometheus32, one boot of 58, that of thefilesystem-read-onlyscenario at 1 vCPU, spent 8,593 µs innvx-time's boot check against KVM's 6,000 µs budget; the suite reports that count without gating on it, and this change does not touch the time tools.Left to CI:
test-openvmm-unitandtest-openvmm;smp-lapic,managed-exec-config, andsandbox-lifecycle;