Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions doc/acceptance-case-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ The adapter's README said what each proved.

| Scenario | Fate | Coverage, or why it retires |
| --- | --- | --- |
| `a_host_program_of_another_runtime_finds_the_vm_mismatched_and_signals_nothing` | rewritten | `nvx-rs/tests/security_checklist.rs::a_saved_vm_runs_until_its_process_exits_and_another_runtime_is_refused_without_a_signal`; `nvx-rs/src/host/vm/tests/reattach.rs::verifying_saved_values_checks_runtime_and_process_without_opening_an_endpoint` |
| `a_host_program_of_another_runtime_finds_the_vm_mismatched_and_signals_nothing` | rewritten | `nvx-rs/src/host/vm/tests/reattach.rs::a_saved_vm_runs_until_its_process_exits_and_another_runtime_is_refused_without_a_signal`; `nvx-rs/src/host/vm/tests/reattach.rs::verifying_saved_values_checks_runtime_and_process_without_opening_an_endpoint` |
| `a_launch_tears_down_a_vm_whose_guest_agent_is_of_another_build` | retired | The adapter's guest agent and the build that its Ready reports |
| `a_restarted_host_program_reattaches_to_the_vm_and_stops_it` | rewritten | `nvx-rs/tests/vm_lifecycle.rs::a_cold_vm_is_reattached_from_saved_values_and_stopped`, which `test-microvm --scenario vm-lifecycle` runs; `nvx-rs/src/host/vm/tests/reattach.rs::reattaching_returns_the_held_owned_child_and_authenticated_connection`; `nvx-rs/src/host/vm/tests.rs::stop_forces_only_its_verified_process_after_grace` |

Expand Down Expand Up @@ -235,7 +235,7 @@ The adapter's README said what each proved.
| `a_forward_whose_port_another_socket_holds_fails_the_clone_as_the_host_s` | rewritten | `test-microvm --scenario platform-network-policy`; `nvx-rs/src/host/restore/tests.rs::restore_failures_attribute_the_existing_policy_free_evidence_classes` |
| `a_generation_is_eligible_by_the_clock_of_its_capture` | rewritten | `nvx-rs/src/host/snapshot/eligibility/tests.rs::within_one_boot_the_downtime_is_the_host_monotonic_clocks`; `nvx-rs/src/host/snapshot/tests.rs::snapshot_accepts_reports_early_and_expired_time_as_unavailable` |
| `a_generation_refuses_clones_of_fewer_processors_than_its_template_booted_with` | rewritten | `nvx-rs/src/host/snapshot/tests.rs::snapshot_accepts_refuses_a_blockless_root_and_each_processor_mismatch`; `test-microvm --scenario restore-processors` |
| `a_host_program_of_another_runtime_ends_the_clone_through_its_verified_identity` | rewritten | `nvx-rs/tests/security_checklist.rs::a_saved_vm_runs_until_its_process_exits_and_another_runtime_is_refused_without_a_signal`; `nvx-rs/src/host/vm/tests.rs::stop_forces_only_its_verified_process_after_grace`. Leases retire (#1121) |
| `a_host_program_of_another_runtime_ends_the_clone_through_its_verified_identity` | rewritten | `nvx-rs/src/host/vm/tests/reattach.rs::a_saved_vm_runs_until_its_process_exits_and_another_runtime_is_refused_without_a_signal`; `nvx-rs/src/host/vm/tests.rs::stop_forces_only_its_verified_process_after_grace`. Leases retire (#1121) |
| `a_run_in_a_template_of_a_network_finds_its_policy_s_proxy` | rewritten | `test-microvm --scenario platform-network-policy`; `test-microvm --scenario untiered-network-policy`, whose runs restore a named snapshot with a NIC under a network policy of their own, though with no proxy. The template's `run` retires (#1121) |
| `a_run_runs_one_execution_in_its_clone_then_tears_the_clone_down` | rewritten | `test-microvm --scenario snapshot-core`, whose `run --restore-snapshot` runs one workload in its restored VM and removes its sandbox. The template's `run` and leases retire (#1121) |
| `a_run_s_execution_finds_its_shares_beneath_the_root` | rewritten | `test-microvm --scenario platform-aggregate-export` |
Expand Down Expand Up @@ -500,7 +500,7 @@ this matrix. The README's own code is no doctest.
| `nvx-rs/src/guest/time/mod.rs - guest::time (line 9)` | kept | SL6.11c removed its capture and restore finish, which became crate-private: `guest::snapshot::capture` runs them |
| `nvx-rs/src/host/sandbox/prepared.rs - host::sandbox::prepared::PreparedLaunch (line 92)` | retired | `PreparedLaunch` leaves with the checked-file handoff (SL6.3, #1122) |
| `nvx-rs/src/host/store/mod.rs - host::store (line 72)` | retired | The sandbox store leaves (SL6.1, #1121) |
| `nvx-rs/src/host/supervisor/mod.rs - host::supervisor (line 32)` | retired | The store-coupled supervisor leaves with the sandbox store (SL6.1, #1121), and `host::supervisor`, the transitional public path of the low-level spawn, with SL6.3 (#1122): `host::vm` spawns VMMs, and the tests of the spawn moved to `nvx-rs/src/host/spawn/tests.rs` and `nvx-rs/tests/process.rs` |
| `nvx-rs/src/host/supervisor/mod.rs - host::supervisor (line 32)` | retired | The store-coupled supervisor leaves with the sandbox store (SL6.1, #1121), and `host::supervisor`, the transitional public path of the low-level spawn, with SL6.3 (#1122): `host::vm` spawns VMMs, and the tests of the spawn moved to `nvx-rs/src/host/spawn/tests.rs` and `nvx-rs/src/host/process/tests/orchestrator.rs` |
| `nvx-rs/src/host/template/clone.rs - host::template::clone::ClonePlan (line 247)` | retired | `ClonePlan` and `CloneRequest` leave with the old clone path (SL6.2b, #1121) |
| `nvx-rs/src/host/template/plan.rs - host::template::plan::TemplatePlan (line 137)` | retired | `TemplatePlan` leaves with the template store (SL6.2a, #1121) |
| `nvx-rs/src/host/template/store.rs - host::template::store::Store (line 473)` | retired | The template store leaves (SL6.2a, #1121) |
Expand Down
84 changes: 45 additions & 39 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ changes only when the module's purpose does.
| [`nvx::host::nvxs`](#nvxhostnvxs) | Compatibility paths for the host NVXS connection and its retained cause details |
| [`nvx::host::transport`](#nvxhosttransport) | A verified VM endpoint connection and the blocking host NVXS driver, with the existing timed transport |
| [`nvx::host::process`](#nvxhostprocess) | Verified references to OpenVMM processes, through which callers wait for them and end them |
| [`nvx::host::platform`, `report`](#nvxhostplatform-report) | Linux and Windows primitives, and OpenVMM's outcome report |
| [`nvx::host::report`](#nvxhostreport) | OpenVMM's outcome report |
| [`nvx::host::sandbox`](#nvxhostsandbox) | The launch profile of `nvx-rs sandbox`: its live shares and option checks; its layer and bindings are private to the CLI |
| [`nvx::guest`](#nvxguest) | The programs that run in Linux guests; see [Guest binaries](#guest-binaries) |
| [`nvx::guest::mappings`](#nvxguestmappings) | `Mapping` and `MappingTable`: host mappings, and the account of a host's mapping table, for any guest init or agent |
Expand Down Expand Up @@ -985,11 +985,29 @@ and another transport failure `Io`. Original I/O and protocol causes are
retained directly; attachment refusal, reset and ended-connection details
remain available as sources.

`Transport` and `TransportHandle` re-export the existing platform types, with
one definition beneath the OS adapters. Timed reads and writes, native-handle
duplication and `unread` are unchanged: Linux reports Unix `SIOCOUTQ` charges,
and Windows reports `None`. The old `session::connect`, private to the crate
like the session, remains a compatibility wrapper for the CLI.
`Transport` and `TransportHandle` re-export the types of the crate's private
platform layer, with one definition beneath the OS adapters. Timed reads and
writes, native-handle duplication and `unread` are unchanged: Linux reports
Unix `SIOCOUTQ` charges, and Windows reports `None`. The old
`session::connect`, private to the crate like the session, remains a
compatibility wrapper for the CLI.

A `Transport` to an OpenVMM control endpoint reads and writes with deadlines,
and tells, through `Transport::unread`, what the endpoint has not read yet of
what it wrote, where the platform tells: on Linux, what `SIOCOUTQ` reports of
its Unix socket, the kernel's charge for the buffers of each write until
OpenVMM read each whole, which a read of part of a buffer leaves as it was, and
which is zero once OpenVMM read everything. A named pipe tells its writer
nothing of the kind, so on Windows, and for a transport that does not tell, it
is `None`.

On Linux and Windows, `Transport::try_clone_handle` optionally returns an
owned native handle to the same peer-verified connection, not a reconnect:
an `OwnedFd` for the Unix socket or an `OwnedHandle` for the overlapped pipe.
An adapter must drop the blocking transport before using or registering the
duplicate with an I/O runtime, or changing the shared blocking mode. The
default is `Ok(None)`, so transports without native handles need not implement
it, and NVX does not depend on an asynchronous I/O runtime.

### `nvx::host::process`

Expand Down Expand Up @@ -1031,35 +1049,19 @@ holds what the process's children read once it reaped them, as the kernel
accounts them to their parent, while Windows counts the process's own reads
alone.

### `nvx::host::platform`, `report`
### `nvx::host::report`

Linux and Windows primitives, and OpenVMM's outcome report. The authenticated
control session, which carries NVX's own control protocol, `NVXC`, one frame
per DATA record of a `HostConnection`, is private to the crate: the CLI and its
restore driver use it, with the `cli` feature.
OpenVMM's outcome report. The Linux and Windows primitives are private to the
crate, in `host::platform`, and so is the authenticated control session, which
carries NVX's own control protocol, `NVXC`, one frame per DATA record of a
`HostConnection`: the CLI and its restore driver use it, with the `cli`
feature.

`report::read` returns `Ok(None)` for a missing report, and an `Io` error for an
unreadable or malformed report, retaining the underlying error. It keeps today's
summary of the outcome category, status code and teardown steps, not free-form
fields.

A `Transport` to an OpenVMM control endpoint reads and writes with deadlines,
and tells, through `Transport::unread`, what the endpoint has not read yet of
what it wrote, where the platform tells: on Linux, what `SIOCOUTQ` reports of
its Unix socket, the kernel's charge for the buffers of each write until
OpenVMM read each whole, which a read of part of a buffer leaves as it was, and
which is zero once OpenVMM read everything. A named pipe tells its writer
nothing of the kind, so on Windows, and for a transport that does not tell, it
is `None`.

On Linux and Windows, `Transport::try_clone_handle` optionally returns an
owned native handle to the same peer-verified connection, not a reconnect:
an `OwnedFd` for the Unix socket or an `OwnedHandle` for the overlapped pipe.
An adapter must drop the blocking transport before using or registering the
duplicate with an I/O runtime, or changing the shared blocking mode. The
default is `Ok(None)`, so transports without native handles need not implement
it, and NVX does not depend on an asynchronous I/O runtime.

### `nvx::host::sandbox`

The launch profile of `nvx-rs sandbox`, with native input validation: the
Expand Down Expand Up @@ -1808,8 +1810,8 @@ components alone, as `Path::components` reads them, with backslashes on
Windows, and resolved by `std::fs::canonicalize`, with the components that do
not exist yet appended. Paths that are not valid Unicode are refused. The
filesystem queries are those of `std::fs`, and the identities of files those
of `nvx::host::platform`: the device and inode on Unix, and the volume serial
number and the 64-bit file index on Windows.
of the crate's private platform layer: the device and inode on Unix, and the
volume serial number and the 64-bit file index on Windows.

Every host path becomes absolute through one function, which joins a relative
path to the current directory without consulting the filesystem, so it
Expand Down Expand Up @@ -2781,11 +2783,13 @@ dependencies: break a cycle by moving the item that a lower child takes from a
higher one down, and re-exporting it at its old path. So the children stack in
layers, each above those that it depends on, which the test prints with
`--nocapture`.
The process and spawn test spawns VMM stand-ins through the low-level spawn,
waits for them through their verified references, finds one again after the
process that spawned it crashed, and checks that a VMM inherits only its
standard streams and the descriptors or handles that it is allowed while
another thread spawns processes.
The process tests, unit tests of `nvx::host::process`, start processes of
their own as an orchestrator does, name them by the start time that the
crate's private platform layer reads, wait for them through their verified
references, and find one again after the process that started it exited; the
spawn's unit tests check that a VMM inherits only its standard streams and the
descriptors or handles that it is allowed while another thread spawns
processes.
On Linux, with the `cli` feature, a loopback test drives
the host's private `ControlSession` through a broker shim into the real managed
agent's session, dispatch, and execution engine, and `nvx.py` also lints and
Expand All @@ -2802,10 +2806,12 @@ The [security checklist](tests/security_checklist.rs) checks, through the
public API alone and without a VM, each security requirement that the host's
documentation states: the guest targets of an export and a read-only path
inside a writable one, the exported objects that each start checks again, a
VM's private directory and its new private log, the exit proof of a saved VM,
the owner of its endpoint, and its recovery from the saved values, and a
snapshot's strict directory. An idle copy of the test binary stands in for a
VM's process. The checks that NVX makes of a restore need a captured snapshot,
VM's private directory and its new private log, and a snapshot's strict
directory. The exit proof of a saved VM, the owner of its endpoint, and its
recovery from the saved values need a process that the test names by its start
time, which only the crate's private platform layer reads, so the unit tests of
`nvx::host::vm` check them, with an idle copy of their test binary as the VM's
process. The checks that NVX makes of a restore need a captured snapshot,
so the unit tests of `nvx::host::vm`, `nvx::host::snapshot`, and
`nvx::host::restore` check them. The checks of a staged image and the claim of
a scratch image, which the CLI keeps in directories that only its own account
Expand Down
11 changes: 0 additions & 11 deletions nvx-rs/public-api/linux.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1839,17 +1839,6 @@ field nvx::host::nvxs::HostConnectionError::Rejected::0
variant nvx::host::nvxs::HostConnectionError::Reset
field nvx::host::nvxs::HostConnectionError::Reset::0
variant nvx::host::nvxs::HostConnectionError::TimedOut
mod nvx::host::platform
const nvx::host::platform::SUPPORTED
trait nvx::host::platform::Transport
method nvx::host::platform::Transport::read
method nvx::host::platform::Transport::try_clone_handle
method nvx::host::platform::Transport::unread
method nvx::host::platform::Transport::write_all
type nvx::host::platform::TransportHandle
fn nvx::host::platform::create_private_dir
fn nvx::host::platform::file_identity
fn nvx::host::platform::process_start_time
mod nvx::host::process
struct nvx::host::process::BootId
method nvx::host::process::BootId::as_bytes
Expand Down
11 changes: 0 additions & 11 deletions nvx-rs/public-api/windows.txt
Original file line number Diff line number Diff line change
Expand Up @@ -567,17 +567,6 @@ field nvx::host::nvxs::HostConnectionError::Rejected::0
variant nvx::host::nvxs::HostConnectionError::Reset
field nvx::host::nvxs::HostConnectionError::Reset::0
variant nvx::host::nvxs::HostConnectionError::TimedOut
mod nvx::host::platform
const nvx::host::platform::SUPPORTED
trait nvx::host::platform::Transport
method nvx::host::platform::Transport::read
method nvx::host::platform::Transport::try_clone_handle
method nvx::host::platform::Transport::unread
method nvx::host::platform::Transport::write_all
type nvx::host::platform::TransportHandle
fn nvx::host::platform::create_private_dir
fn nvx::host::platform::file_identity
fn nvx::host::platform::process_start_time
mod nvx::host::process
struct nvx::host::process::BootId
method nvx::host::process::BootId::as_bytes
Expand Down
8 changes: 4 additions & 4 deletions nvx-rs/src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
//! - [`transport`] verifies a VM's endpoint and runs the host's role of the NVXS session for it,
//! for any application protocol, over a platform transport. The private `session` (feature
//! `cli`) speaks NVX's own control protocol, `NVXC`, over it for NVX's programs. [`nvxs`]
//! retains compatibility paths. [`platform`] holds the primitives of Linux and Windows hosts.
//! Other hosts compile but report NVX as unavailable.
//! retains compatibility paths. The private `platform` module holds the primitives of Linux and
//! Windows hosts. Other hosts compile but report NVX as unavailable.
//! - [`process`] names processes by verified references: their ID, start time, and host boot,
//! through which a pidfd or a process handle holds them, so that waits sleep until a process
//! exits and termination reaches only it, whether or not the caller started it.
Expand All @@ -50,7 +50,7 @@
//! caller allows, and no other descriptor or handle of the caller's.
//!
//! The children of `host` depend on one another without cycles: each builds only on children
//! beneath it, as [`vm`] does on `spawn`, and `spawn` on [`platform`]. An item that
//! beneath it, as [`vm`] does on `spawn`, and `spawn` on `platform`. An item that
//! a lower child needs from a higher one moves down, and the higher child re-exports it where it
//! was. `tests/host_modules.rs` reads the crate's sources and fails on a cycle, which it lists
//! with a reference for each of its dependencies.
Expand All @@ -71,7 +71,7 @@ pub mod filesystem;
pub mod network;
pub mod nvxs;
pub(crate) mod paths;
pub mod platform;
pub(crate) mod platform;
pub mod process;
pub mod report;
pub mod restore;
Expand Down
2 changes: 1 addition & 1 deletion nvx-rs/src/host/platform/linux/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::path::Path;
use crate::host::sys::Errno;

/// Returns the device and inode of a file or directory.
pub fn file_identity(path: &Path) -> io::Result<(u64, u64)> {
pub(crate) fn file_identity(path: &Path) -> io::Result<(u64, u64)> {
let metadata = fs::metadata(path)?;
Ok((metadata.dev(), metadata.ino()))
}
Expand Down
15 changes: 5 additions & 10 deletions nvx-rs/src/host/platform/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,32 @@ pub(crate) use self::endpoint::{ReadyListener, connect_endpoint, listen_ready};
pub(crate) use self::endpoint::{
connect_child_endpoint, endpoint_server_pid_until, interrupt_transfers, transport_from_handle,
};
pub use self::identity::file_identity;
pub(crate) use self::identity::file_identity;
#[cfg(feature = "cli")]
pub(crate) use self::identity::{
Directory, file_id, is_deleted, opened_file_id, opened_file_identity,
};
#[cfg(feature = "cli")]
pub(crate) use self::log_claim::{claim_log, log_claim_held, serves_launch};
pub use self::private::create_private_dir;
pub(crate) use self::private::{
check_snapshot_dir, check_store_dir, create_store_dir, make_sparse, private_file_problem,
};
#[cfg(feature = "cli")]
pub(crate) use self::private::{rename_no_replace, restore_private_file_mode};
pub use self::process::process_start_time;
// Outside this module, only the CLI, which checks the processes that its records name, and the
// tests, which name the processes that they start, read a process's start time.
#[cfg(any(test, feature = "cli"))]
pub(crate) use self::process::process_start_time;
pub(crate) use self::process::{
ProcessHandle, boot_id, host_id, monotonic_ns, open_process, own_bytes_read, pin_child,
};
pub(crate) use self::spawn::detach;
#[cfg(feature = "cli")]
pub(crate) use self::spawn::inherit_only;

/// Whether this host can run NVX microVMs.
pub const SUPPORTED: bool = true;

/// File that holds the identity of the current boot, a UUID that the kernel chooses at random.
const BOOT_ID_PATH: &str = "/proc/sys/kernel/random/boot_id";

/// Mode of a directory that only the current user may access: read, write, and search for its
/// owner, and nothing for anyone else.
const PRIVATE_DIR_MODE: Mode = Mode::S_IRWXU;

/// Mode of the directory of a sandbox store, the exact mode that OpenVMM requires of the
/// directory of its control socket.
const STORE_DIR_MODE: Mode = Mode::S_IRWXU;
Expand Down
Loading
Loading