Repository navigation
nvx-rs: return a restore's seed in memory and zero it on drop - #880
Merged
Pedro Henrique Penna (ppenna) merged 1 commit intoOct 28, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Security-sensitive entropy ownership and manual memory erasure warrant final human review, with hypervisor validation still pending.
No open issues 🎉
What changed in this PR
Implements the owned-seed foundation for #842, allowing guest library callers to receive restore entropy in memory while preserving the command tools’ file-based behavior.
Changes:
- Adds in-memory capture requests and one-time seed transfer through
Restore::take_seed(). - Gives
Seedheap-backed ownership and zero-on-drop cleanup; clears packet buffers and reseed payloads. - Adds ownership, cleanup, and capture-mode tests, and updates API documentation.
| File | Description |
|---|---|
nvx-rs/src/guest/time/mod.rs |
Documents in-memory capture and reseeding. |
nvx-rs/src/guest/time/decoders.rs |
Decodes packet entropy into an owned seed. |
nvx-rs/src/guest/time/commands.rs |
Extracts capture argument parsing and tests file-mode compatibility. |
nvx-rs/src/guest/time/api.rs |
Adds entropy delivery modes, seed transfer, buffer cleanup, and tests. |
nvx-rs/src/guest/sys/secret.rs |
Introduces volatile memory erasure and its unit test. |
nvx-rs/src/guest/sys/mod.rs |
Exposes the internal erasure helper. |
nvx-rs/src/guest/sys/ioctl.rs |
Clears the reseed payload on drop and tests cleanup. |
nvx-rs/src/guest/reseed.rs |
Adds heap-backed seed ownership, redacted formatting, and cleanup tests. |
nvx-rs/README.md |
Describes capture modes and seed ownership guarantees. |
Review effort: Balanced
Enrique Saurez (esaurez)
changed the base branch from
dev
to
esaurez/841-pr3-captures-in-vms-20261024
October 27, 2026 21:31
Enrique Saurez (esaurez)
force-pushed
the
esaurez/842-pr1-owned-seed-20261024
branch
from
October 27, 2026 21:31
3c41d28 to
e9a0339
Compare
This was referenced Oct 27, 2026
Enrique Saurez (esaurez)
force-pushed
the
esaurez/841-pr3-captures-in-vms-20261024
branch
from
October 28, 2026 01:20
8c127e4 to
e021475
Compare
A capture wrote the restore packet's fresh entropy to a file, which the caller then read back, and Seed held its 64 bytes inline, so a move could leave a copy of them, and nothing erased them. CaptureRequest::in_memory selects a mode in which TimeAbi::capture writes no file: the Restore holds the entropy as an owned Seed, which Restore::take_seed hands over. CaptureRequest::new keeps the file mode as it was, and nvx-time capture keeps using it. A Seed now keeps its bytes in one heap allocation, which a move leaves in place, overwrites them with volatile writes and a compiler fence when it is dropped, can't be cloned, and formats none of them. The capture decodes the packet's entropy straight into that allocation and zeroes the port buffer that held it, a seed file is read straight into it, and the RNDADDENTROPY payload zeroes its copy once the ioctl returns. The volatile writes live in the guest's sys module, the only one that may use unsafe code, so no dependency is added. Restore and CaptureOutcome are no longer Clone or comparable, since a restore may own a seed, and Seed::from_bytes is no longer a const fn. Part of #842 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: fbebd4aa-2ad7-427d-80ea-49358a645e9a
Enrique Saurez (esaurez)
force-pushed
the
esaurez/842-pr1-owned-seed-20261024
branch
from
October 28, 2026 01:49
e9a0339 to
9b6f035
Compare
Base automatically changed from
esaurez/841-pr3-captures-in-vms-20261024
to
dev
October 28, 2026 02:03
This was referenced Oct 28, 2026
Pedro Henrique Penna (ppenna)
deleted the
esaurez/842-pr1-owned-seed-20261024
branch
October 28, 2026 03:50
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.
Chain: this PR → #936 → #937 → #939 → #940 → #941, on
dev; #879, #881, #885, #886, #877, #932, #884, #887, and #933 have merged.Part of #842 (PR 1 of 3: the owned seed).
Rebased onto #933. With
nvx::guest::clonenow below it, this commit also gives the test-onlyRestore::toaseed: None, and the clone tests compare aRestoreby itsDebugform, since aRestoreis no longerPartialEq.What and why
A capture wrote the restore packet's fresh entropy to a file, which the caller then read back, and
Seedheld its 64 bytes inline, so a move could leave a copy of them, and nothing erased them. This lets a library caller take a restore's entropy in memory, as an ownedSeedthat zeroes its bytes when it is dropped, while the command tools keep the file mode.CaptureRequest::in_memory(scratch, generation_id)selects the in-memory mode:TimeAbi::capturewrites no file, and theRestoreholds the entropy as an ownedSeed, whichRestore::take_seed()hands over (Nonein the file mode, or once taken).CaptureRequest::new(scratch, generation_id, entropy_path)is unchanged and keeps the file mode.Seedkeeps its bytes in one heap allocation, overwrites them withptr::write_volatileand acompiler_fence(SeqCst)when it is dropped, is neitherClonenorCopy, and formats none of its bytes: itsDebugform is nowSeed { .. }(it printed the generation ID;Seed::generation_id()still returns it). No dependency is added: the volatile writes are a safe wrapper in the guest'ssysmodule, the only one that may useunsafe.nvx-time capture 0|1 GENERATION_ID ENTROPY_FILE [--finish]still buildsCaptureRequest::new, with the same flags, mode 0600, order of operations, and diagnostics;nvx-snapshotandnvx-reseedare untouched.How the bytes stay single-owner
RestorePacket::entropyis now aSeed), and the port buffer is zeroed right after decoding.Restore, theCaptureOutcome, and out throughtake_seed(): each move moves its address, not the bytes. In the file mode, the bytes are written to the file, and the seed is then dropped, which zeroes them.Seed::readreads a seed file straight into the allocation; a read that fails zeroes what it read.reseedcopies the seed into the payload thatRNDADDENTROPYtakes, which zeroes its copy when the call returns.Seed::from_byteszeroes its own copy of its argument; the caller owns any copy that it keeps.API changes
CaptureRequest::in_memory,Restore::take_seed, andDropforSeed.RestoreandCaptureOutcomederive onlyDebug(they were alsoClone,PartialEq, andEq), since a restore may own the seed.Seed::from_bytesis no longer aconst fn, since it allocates.Validation
This head is the previous one, e9a0339, rebased without changes onto #933's rebased head, e021475, which brings
dev's changes since, OpenVMM at d2338ea2 andnvx-rs downloadamong them; its per-commit checks and the checks below passed on it, and the tests under all features grew only by the 55 thatnvx-rs downloadadds tohost::cli.On this head:
Linux x86_64:
cargo fmt --check; Clippy with-D warnings,--all-targets --all-features,--lib, and--lib --no-default-features, and with each feature alone;cargo test --all-features(1534 passed),cargo test(802 passed), and the--lib --no-default-featuresbuild and test (258 passed);tests/host_modules.rs(22 passed); the bundled-artifacts test; rustdoc with-D warnings; the MSRV (1.89) check; the aarch64-apple-darwin check; the guest's tests and MSRV check with onlyguest.nvx.py check-guest-binaries(musl Clippy and tests, 848 passed),build-guest-binaries,test-guest-binaries(33 passed),check-static-elf.tests/runtime-adapter: fmt, Clippy, build, tests, and MSRV in its protocol-only, host-only, and musl guest configurations.scripts/check_markdown_links.py.privileged_captures_hand_over_the_entropy_in_their_mode, which runs the realcapture()over the fake privileged operations, in a forked child alone in a mount namespace of its own with a tmpfs at/run, in both modes (two mutations, an in-memory mode that drops the seed and one that also writes it to a file, each made it fail), and the tests ofguest::cloneandguest::reseed(31 passed).Windows (x86_64-pc-windows-msvc):
nvx.py verify; nvx-rs fmt, Clippy (all features,--lib, no default features, and each feature alone), tests (all features 955 passed, default 817, no default features 258),tests/host_modules.rs, rustdoc, MSRV;tests/runtime-adapterin its protocol-only and host configurations; the Markdown links.CI: this PR's own run,
ci.ymlrun 40620699319, since #933's merge retargeted this PR todev, passed: 34 jobs succeeded, among them nvx-rs and the CLI on Linux and Windows, the guest artifacts, OpenVMM's vmm-tests, the NVX microVM tests and the platform jobs on KVM, MSHV, and WHP, and the performance regression gate; 7 were skipped by design: OpenVMM's unit tests, since this PR doesn't change OpenVMM, the debug-kernel microVM tests on MSHV and WHP, which pull requests run on KVM only, and the 4 jobs that onlydevruns.At the top of the chain, #937, which holds this commit:
vm_layered43,vm_template9,vm_restart3,vm_steps3).vm_templatecaptures templates throughnvx::guest::clone, whose capture nvx-rs: repair a clone's restore in the guest #936 switches to this PR's in-memory mode.ci.ymlrun 40617461513, dispatched on the chain's top, nvx-rs: give a clone's failures documented statuses #937, at 5683164, since the PRs above this one don't targetdevand get no PR CI, passed: 36 jobs succeeded, among them nvx-rs and the CLI on Linux and Windows, the guest artifacts, OpenVMM's unit tests and vmm-tests, the NVX microVM tests and the platform jobs on KVM, MSHV, and WHP; the 6 jobs that only pull requests ordevrun were skipped.Tests added:
Seedzeroes its bytes when it is dropped (observed after the volatile writes, before its allocation is freed, so no test reads freed memory), moves without copying them, and formats none of them; theRNDADDENTROPYpayload zeroes its copy;zeroizeitself.nvx-time capturestill builds the file mode.In this PR, the capture of
nvx::guest::clonestill uses the file mode; #936 switches it to the in-memory mode.