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
2 changes: 1 addition & 1 deletion doc/acceptance-case-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ suite in its shards, and the public sandbox scenarios in its guest and sandbox j
| `run-console-restore` | retired | SL3.8b removes `nvx-rs run`'s restore of a raw OpenVMM snapshot and the boot console that it reattached on the terminal: `run --restore-snapshot` restores a named snapshot through `snapshot restore`'s path |
| `sandbox-blocks` | kept | |
| `sandbox-gpt-layer` | kept | The [cold cells](#cold-boots) of a GPT disk, over a scratch upper and over a RAM upper, through `run`, and `provision` and `start` |
| `sandbox-lifecycle` | kept | A [cold cell](#cold-boots): the EROFS layer over a scratch upper, through `provision` and `start`, which also reattach to the VM from a new process for each command |
| `sandbox-lifecycle` | kept | A [cold cell](#cold-boots): the EROFS layer over a scratch upper, through `provision` and `start`, which also reattach to the VM from a new process for each command. Since SL4.4c, its request that writes 2 MiB checks that `exec` streams all of it, where it checked NVXC version 1's 1 MiB bound |
| `sandbox-proxy-only` | kept | Its proxy-only network, over the EROFS layer and a scratch upper; it is no [cold cell](#cold-boots) of its own |
| `sandbox-ram-upper` | kept | A [cold cell](#cold-boots): the EROFS layer over a RAM upper, through `run`, and `provision` and `start`. Since SL3.10c, it checks that `provision` records the configuration's format 10 |
| `scratch-snapshot` | kept | |
Expand Down
29 changes: 20 additions & 9 deletions doc/design/4.4-host-guest-control-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ and optionally an absolute working directory of at most 4,096 bytes and up to
256 `KEY=VALUE` environment entries of at most 4,096 bytes each, with
distinct, non-empty keys. The environment either replaces the default
environment or is layered over it, each entry replacing the default variable
of the same name. Output returns as separate standard output and standard
error streams within the credit window, at most 1 MiB of them combined for
of the same name. In version 2 of the protocol, which NVX's host speaks, the
host streams an execution's standard input, which the guest agents
acknowledge as they write it, with at most 64 KiB unacknowledged, until the
host ends it, and output returns as separate standard output and standard
error streams within the credit window, without a total limit. Version 1
carries no standard input, and forwards at most 1 MiB of output combined for
each execution. As Table 4.3 shows, an execution that runs ends with an exit
report, which carries an outcome category and a status.

Expand All @@ -53,7 +57,7 @@ report, which carries an outcome category and a status.
| `signal` | A signal ended the workload; the status is 128 plus the signal number |
| `timeout` | The workload exceeded its timeout and was killed; the status is 124 |
| `cancelled` | A cancellation killed the workload; the status is 137 |
| `output-limit` | The workload's combined output exceeded 1 MiB, and the guest agents killed it; the status is 125 |
| `output-limit` | In version 1, the workload's combined output exceeded 1 MiB, and the guest agents killed it; the status is 125 |
| `failed` | The guest agents could not supervise the workload; the status is 125 |

Otherwise, the guest agents reply with an error, which carries one of the
Expand All @@ -77,9 +81,15 @@ account for.

## 4.4.2 Feature Negotiation

Version 2 of the protocol negotiates nothing: every frame names its version,
the guest's `READY` names the version that its agents speak, and NVX's host
requires version 2, whose guest agents provide every behavior that the host
depends on. Version 1 negotiates features instead.

A successful readiness probe does not prove that an image enforces the
behaviors that a host depends on. As Table 4.5 shows, a `FEATURES` request
therefore returns a four-byte little-endian bit mask of control features.
behaviors that a host depends on. As Table 4.5 shows, a `FEATURES` request of
version 1 therefore returns a four-byte little-endian bit mask of control
features.

**Table 4.5.** Control feature bits.

Expand Down Expand Up @@ -110,10 +120,11 @@ Requests are typed, length-bounded, ordered, and associated with one VM
instance and epoch. Credit limits bound queued data in both directions.
Malformed, unauthenticated, stale, duplicated, or out-of-order records fail
closed. Only one execution runs at a time; any request other than a
cancellation that arrives during an execution is refused as `busy`. Shutdown
is therefore graceful only between executions: a host cancels a running
execution before it requests shutdown, or else terminates the VM, which fails
the execution.
cancellation, or in version 2 the execution's standard input or a shutdown,
that arrives during an execution is refused as `busy`. In version 2, a
shutdown ends the running execution first. In version 1, shutdown is therefore
graceful only between executions: a host cancels a running execution before
it requests shutdown, or else terminates the VM, which fails the execution.

The control session is process-local even when guest parser state is
snapshotted. Restore establishes a fresh host capability, peer identity,
Expand Down
25 changes: 13 additions & 12 deletions doc/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -908,11 +908,12 @@ VM directory, and prints its OpenVMM command as `>> COMMAND`. Once nvx-init is
ready and has mapped the shares, the run executes its workload once over the
control session, as [`sandbox exec`](#managed-lifecycle) does, with the same
`--cwd`, `--environment`, `--environment-file`,
`--inherit-default-environment`, and `--exec-timeout-ms` options. It then asks
`--inherit-default-environment`, and `--exec-timeout-ms` options, and streams
its standard input, output and error as `sandbox exec` does. It then asks
nvx-init to stop the VM, terminates OpenVMM if it does not exit within
`--timeout` seconds, warning that it did, and removes the VM directory, and
finally prints the workload's standard output and standard error and exits
with its status. A VM that exits before nvx-init is ready, as when the guest
finally exits with the workload's status. A VM that exits before nvx-init is
ready, as when the guest
refuses the workload identity, fails the run as it fails a `start`, with the
category and status of the outcome report that OpenVMM published, such as
`guest-exit status 125`. `--outcome-report` forwards that report, of the VM's
Expand Down Expand Up @@ -1738,10 +1739,11 @@ rejects `--state-dir`.
Environment files are limited to 1 MiB of UTF-8 JSON. Environments contain at
most 256 unique, nonempty names. Each `KEY=VALUE` entry and working-directory
path is limited to 4096 UTF-8 bytes; the combined execution request must also
fit the existing 64 KiB control-payload bound. A managed `exec` forwards at
most 1 MiB of combined standard output and standard error; the guest agent
kills a workload that writes more, and `exec` exits with status 125 and reports
the `output-limit` category.
fit the existing 64 KiB control-payload bound. A managed `exec` streams its
standard input to the workload, which sees it end when the command's own
input ends, and the workload's standard output and standard error back as
they arrive, without a limit on their size. Ctrl-C cancels the workload, and
`exec` exits with status 130.

The `managed-exec-config` scenario of `test-microvm --backend BACKEND` is the
authoritative acceptance for these public options. It invokes
Expand Down Expand Up @@ -1795,11 +1797,10 @@ template, so it has the same artifact requirements. It checks that:
is on scratch;
- `/sbin/nvx-sandbox-smoke` passes its security profile checks in a sandbox
provisioned with `--hostname`, after earlier requests;
- a request that writes more than the 1 MiB output bound exits with status
125 and an `output-limit` outcome report after the host has received more
than 1 MiB less one 32 KiB read chunk and at most 1 MiB, and a request whose
`--outcome-report` path exists fails before its workload runs and leaves the
file intact;
- a request that writes 2 MiB, more than the 1 MiB that version 1 of NVXC
forwarded, streams all of it and exits with status 0 and an `exit` outcome
report, and a request whose `--outcome-report` path exists fails before its
workload runs and leaves the file intact;
- `stop` ends OpenVMM and its control socket or pipe, leaves only
`config.json`, `sandbox.lock`, `exec.lock`, `openvmm.log`, and a successful
`outcome.json`, and leaves a cleanly unmounted scratch file system, after
Expand Down
11 changes: 7 additions & 4 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,13 @@ launches or is recorded; see [Sandbox networking](run.md#sandbox-networking).
given. It exits with status 1 for an error, 2 for a usage error, and 130 after
Ctrl-C, and otherwise with the status that the workload or OpenVMM returns. Its
errors and usage errors carry their [error codes](#error-codes), as
`error[CODE]: MESSAGE` and `nvx-rs sandbox: error[CODE]: MESSAGE`; an `exec`
prints its workload's standard output and error before any error of its own,
which starts a new line. Options take their values as those of [`run`](#run)
do, and their names cannot be abbreviated either.
`error[CODE]: MESSAGE` and `nvx-rs sandbox: error[CODE]: MESSAGE`. An `exec`,
and the workload of a `run`, stream the command's standard input to the
workload until it ends, and the workload's standard output and error back as
they arrive, without a limit; a command whose input is the null device gives
the workload none. An error of the command's own follows the workload's output
on a new line, and Ctrl-C cancels the workload. Options take their values as
those of [`run`](#run) do, and their names cannot be abbreviated either.

| Option | Default | Description |
| --- | --- | --- |
Expand Down
73 changes: 40 additions & 33 deletions nvx-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ frames, the `EXEC` payload that the host encodes and the guest agent decodes,
and the control features that a guest image advertises. Beside version 1's
frames, `protocol::app::v2` holds the codec of version 2's, which carry every
control path, with version 1's payloads, and stream an execution's standard
input, output and error. The guest loop that nvx-init and the managed agent
share, and the fake OpenVMM, serve both versions, so `v2` follows their
features, `guest` and `testing`, until the host speaks it too.
input, output and error. NVX's host speaks version 2 alone, and the guest loop
that nvx-init and the managed agent share, and the fake OpenVMM, serve both
versions, so `v2` follows the features `cli`, `guest` and `testing`, and
version 1's frames those of their servers, `guest` and `testing`.

The [conformance vectors](tests/vectors/README.md) of version 1,
`tests/vectors/nvxc-v1.txt`, and OpenVMM's vectors and cases of the `NVXS`
Expand Down Expand Up @@ -1050,7 +1051,11 @@ 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.
feature. The session speaks version 2 alone, and requires the guest's `READY`
to name that version. It streams an execution's standard input, with an
explicit end, and keeps at most 64 KiB of it that the guest has not
acknowledged writing, and receives the execution's output without a total
limit; a `STOP` ends an execution that runs.

`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
Expand Down Expand Up @@ -1905,16 +1910,17 @@ before them.
as `nvx.py` quoted it with `subprocess.list2cmdline` and `shlex.join`.
Unless `--dry-run` is given, it then executes the workload once through the
control session, as `exec` does, with its working directory and environment
options, asks the guest to shut the VM down, waits up to `--timeout` seconds
for OpenVMM to exit before it terminates it, removes the VM directory, and
prints the workload's standard output and then its standard error, and exits
with its status. `--outcome-report` forwards OpenVMM's outcome report of the
VM's stop.
options, streaming its standard input, output and error, asks the guest to
shut the VM down, waits up to `--timeout` seconds for OpenVMM to exit before
it terminates it, removes the VM directory, and exits with the workload's
status. `--outcome-report` forwards OpenVMM's outcome report of the VM's
stop.
- `provision` records the sandbox in its state directory. `start` launches
OpenVMM detached with the managed lifecycle, passes it a fresh capability
through its standard input, and waits until the guest answers. `exec` runs a
workload, prints its standard output and then its standard error, and exits
with its status; `--outcome-report` writes
workload, streams its own standard input to it, until that ends, and the
workload's standard output and error back as they arrive, and exits with the
workload's status; Ctrl-C cancels the workload. `--outcome-report` writes
`{"operation_id": HEX, "outcome": {"category": CATEGORY, "operation":
"exec", "status_code": STATUS}, "schema_version": 1}`. `stop` shuts the VM
down and prints nothing, but for a warning when it terminates a VM of another
Expand Down Expand Up @@ -2015,24 +2021,21 @@ OpenVMM that a start of such a build left behind when it was interrupted has no
record, and that build asked for it to be ended by hand: end it before the
switch too, since this build finds no VM in that sandbox.

#### Guest features

After the guest answers, `start`, and `sandbox run`, ask it for its control
features and require `GuestFeatures::LAYERED_SANDBOX`, the features that their
launches use: per-execution environments (bit 4), since `exec` and
`sandbox run` send a working directory and an environment, and host path
mapping tables (bit 6), since they then send the mapping table of the
sandbox's shares, which the guest agent mounts before
it runs any workload; a mapping that it refuses fails the start, after the
guest shuts down as `stop` has it do. It needs neither cancellation (bit 0),
since the CLI never cancels a workload, and Ctrl-C ends the session, nor
workload account creation (bit 2), since a layered sandbox runs its workloads
as an account of the image. A guest image that lacks a required feature, or
that predates the request, is stopped as a failed start is, and the start
fails with
`the guest image lacks control features that layered sandboxes need (...)`,
which names the missing features. `nvx.py` does not ask; this, the checks of
the [distro binding](#distro-bindings) and of the
#### Guest protocol

After the guest answers, `start`, and `sandbox run`, speak version 2 of `NVXC`
to it, whose `READY` names the version that the guest agent speaks, and require
that version: a guest image whose agent answers in another version, as one
that predates version 2 does, is stopped as a failed start is, and the start
fails. They ask for no control features, which only version 1's `FEATURES`
request reports, and which `start` required before: per-execution environments
and host path mapping tables, which every guest agent of version 2 provides.
`exec` and `sandbox run` send a working directory and an environment, and the
start then sends the mapping table of the sandbox's shares, which the guest
agent mounts before it runs any workload; a mapping that it refuses fails the
start, after the guest shuts down as `stop` has it do. `nvx.py` does not check
the version; this, the streams of `exec` and `sandbox run`, the checks of the
[distro binding](#distro-bindings) and of the
[scratch binding](#images-by-path), and the claim of the scratch image are the
intended changes of behavior.

Expand Down Expand Up @@ -2110,8 +2113,11 @@ intended changes of behavior.
launched exits, as `nvx::host::transport::connect_verified` does, and fails
at once with `exited`, where `nvx.py` waited until `--timeout` passed before
it reported the exit.
- `exec` refuses more than 1 MiB of workload output, which the guest agent
never forwards.
- `exec` and `sandbox run` stream the workload's standard input, output and
error while it runs, where `nvx.py` gave it no input and printed its output
once it ended. So a guest's diagnostic of a request that it refuses, such as
a working directory that the workload cannot enter, precedes the error.
Ctrl-C cancels the workload, where `nvx.py` closed the session.
- `provision`, and `start` again, refuse a share whose resolved host path
equals, contains, or lies inside the sandbox's state directory, image, or
scratch image, or the OpenVMM, kernel, or initramfs of the artifact root, as
Expand Down Expand Up @@ -2641,8 +2647,9 @@ tests, only the test hooks that

The tests need no hypervisor. The fake-OpenVMM test runs `nvx-fake-openvmm`
directly, as `nvx-rs run` runs OpenVMM on the terminal. The CLI test runs the
[scenario tests](#scenario-tests) of `nvx-rs`, and checks the guest-feature
requirement and, on Linux, Ctrl-C. The
[scenario tests](#scenario-tests) of `nvx-rs`, and checks that a start asks
the guest for no features, that `exec` and `sandbox run` stream, and, on
Linux, Ctrl-C, which also cancels an `exec`. The
[conformance vectors](tests/vectors/README.md) of the wire formats cover both
sides of every record, frame, and payload: the codec decodes each vector of
`tests/vectors/nvxc-v1.txt` to its values and encodes them to its bytes, it
Expand Down
Loading
Loading