Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3446734
feat(virtq): add external byte transport foundations
andreiltd Jul 20, 2026
120e67b
feat(virtq): add external function value codecs
andreiltd Jul 21, 2026
e0a51c0
feat(virtq): add stateful chain byte streams
andreiltd Jul 21, 2026
e1342f2
refactor(virtq): remove reset api and harden allocation rollback
andreiltd Jul 22, 2026
4b23b8e
feat(virtq): add tiered fixed slot allocation
andreiltd Jul 22, 2026
ab25a50
feat(virtq): add canonical packed ring images
andreiltd Jul 23, 2026
f2274f1
refactor(layout): model scratch-top metadata
andreiltd Jul 24, 2026
f2c3f46
fix(guest): correct scratch allocator boundary
andreiltd Jul 24, 2026
a12ee21
feat(virtq): define virtq transport metadata
andreiltd Jul 27, 2026
3e81faa
feat(virtq): implement host side memory access
andreiltd Jul 28, 2026
c0e53a5
feat(virtq): configure transport geometry
andreiltd Jul 29, 2026
e9bce77
feat(virtq): initialize runtime transport
andreiltd Jul 30, 2026
a5709e0
feat(snapshot): preserve canonical virtq state
andreiltd Jul 31, 2026
738caaa
feat(virtq): implement guest-to-host virtqueue transport
andreiltd Aug 3, 2026
76d078f
feat(virtq): remove legacy guest-to-host stack transport
andreiltd Aug 3, 2026
69a63ed
feat(virtq): use virtqueues for guest comms
andreiltd Aug 10, 2026
d691500
feat(virtq): purge input/output regions
andreiltd Aug 10, 2026
0b588db
feat(virtq): optimize inflight bookkeeping memory
andreiltd Aug 10, 2026
90126c2
feat(virtq): checkpoint dirty queues for snapshots
andreiltd Aug 11, 2026
99059d1
feat(virtq): reject snapshots with retained buffers
andreiltd Aug 11, 2026
241e9c6
feat(virtq): add C guest `ByteChunks`
andreiltd Aug 11, 2026
dc031b2
fix: cleanup after rebase
andreiltd Aug 11, 2026
87f9232
fix(virtq): adjust scratch budget
andreiltd Aug 11, 2026
1086386
refactor(virtq) adjust virtio terminology
andreiltd Aug 11, 2026
f6779c0
fix: typo
andreiltd Aug 11, 2026
799cec0
fix(virtq): address clippy warnings in release builds
andreiltd Aug 11, 2026
29b84b0
fix(virtq): use checked arithmetic in ExternalValueRefs
andreiltd Aug 11, 2026
6fe392a
refactor: some stylish tweaks
andreiltd Aug 12, 2026
e3b4d38
fix: adjust test scratch size
andreiltd Aug 12, 2026
153d425
fix(virtq): accept transport error in fuzzing
andreiltd Aug 12, 2026
a4cea75
fix: increas fuzzing heap size
andreiltd Aug 13, 2026
7874cb1
feat(virtq): use grouped allocation for virtq chains
andreiltd Aug 13, 2026
ff376f3
fix: adjust test heap sizes
andreiltd Aug 13, 2026
40c1815
fix: some docs improvements
andreiltd Aug 13, 2026
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Prerelease] - Unreleased

### Added
* Add per-direction virtqueue configuration and account its allocations in
scratch sizing.

### Changed
* **Breaking:** Guest MSR state is now saved and restored across snapshots.
Expand All @@ -13,10 +15,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
resets to a clean default. On KVM the guest may only read or write declared
MSRs, on MSHV and WHP this is not enforced. by @ludfjig in https://github.com/hyperlight-dev/hyperlight/pull/991
* **Breaking:** Filesystem paths are now represented using `PathBuf`. `GuestBinary::FilePath` now stores a `PathBuf` instead of a `String`, and `MultiUseSandbox::generate_crashdump_to_dir` accepts `Into<PathBuf>` instead of `Into<String>`. Callers passing a `String` to `GuestBinary::FilePath` must convert it using `.into()`.
* Expose C guest `ByteChunks` values as pointer and length arrays.
* Place virtqueue rings and pools in host-owned scratch before page tables.
Snapshot ABI 2 rejects snapshots created with earlier layouts.
* Require guest logs and all host and guest function calls to use virtqueues.
* Keep registered Rust guest return values typed until transport encoding so
external byte results avoid intermediate FlatBuffer copies.
* Store canonical virtqueue rings in versioned OCI transport layers. Config v2
rejects snapshots without transport state.
* Running snapshots checkpoint dirty virtqueues before capture. Ordinary calls
keep their deferred result path.
* Reject snapshot capture while guest-owned transport buffers are retained.
* Use the reclaimed stack pages to raise the default G2H and H2G pools to 12
and 8 pages.

### Removed
* Remove legacy stack I/O, its `GuestHandle` methods, and its sandbox
configuration options.

### Fixed
* Keep sandboxes usable after an H2G request exceeds available virtqueue capacity.
* Fix symbol resolution in guest core dumps for sandboxes created from snapshots by @ludfjig in https://github.com/hyperlight-dev/hyperlight/pull/1618
* Reject malformed OCI snapshot metadata and non-regular artifact files during load.

Expand Down
4 changes: 2 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ test-loom:
# runs tests that requires being run separately, for example due to global state
test-isolated target=default-target features="" :
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_emit_guest_log --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --test integration_test -- log_message --exact --ignored
@# CPU vendor check, gated to known CI runner hardware
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::snapshot::file::config::tests::cpu_vendor_current_is_recognized --exact --ignored
Expand Down Expand Up @@ -524,7 +524,7 @@ coverage-run hypervisor="kvm": ensure-cargo-llvm-cov

# isolated tests (require running separately due to global state)
cargo +nightly test -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored
cargo +nightly test -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored
cargo +nightly test -p hyperlight-host --lib -- sandbox::outb::tests::test_log_emit_guest_log --exact --ignored
cargo +nightly test -p hyperlight-host --test integration_test -- log_message --exact --ignored
cargo +nightly test -p hyperlight-host --no-default-features -F function_call_metrics,{{ if hypervisor == "mshv3" { "mshv3" } else { "kvm" } }} --lib -- metrics::tests::test_metrics_are_emitted --exact

Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This project is composed internally of several components, depicted in the below

* [Security guidance for developers](./security-guidance-for-developers.md)
* [Paging Development Notes](./paging-development-notes.md)
* [Virtqueue host and guest communication](./virtio-host-guest-communication.md)
* [How to debug a Hyperlight guest](./how-to-debug-a-hyperlight-guest.md)
* [How to use Flatbuffers in Hyperlight](./how-to-use-flatbuffers.md)
* [How to make a Hyperlight release](./how-to-make-releases.md)
Expand Down
12 changes: 4 additions & 8 deletions docs/paging-development-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,10 @@ calls, i.e. there may be no calls in flight at the time of
snapshotting. This is not enforced, but odd things may happen if it is
violated.

Buffer management between the host and guest is needed to pass call
arguments and return values. Ideally, buffers would be dynamically
allocated from the scratch region as needed.

Currently, I/O buffers are statically allocated at the bottom of the
scratch region. This is a stopgap pending improved
physical allocation and buffer management.
Host and guest calls use two virtqueues in a fixed transport arena at
the bottom of scratch. The arena contains both rings and their
fixed-slot buffer pools. Copied page tables follow the arena. Dynamic
scratch allocations begin after the copied page tables.

The minimum scratch size is calculated by `min_scratch_size()` in the
architecture-specific layout modules under `hyperlight_common`; see
Expand Down Expand Up @@ -177,4 +174,3 @@ paging) and enables PAE. The guest is always entered in long mode.

Hyperlight unconditionally uses 48-bit virtual addresses. Hyperlight
presently only uses addresses in the lower (ttbr0) half of the address range.

27 changes: 17 additions & 10 deletions docs/snapshot-oci-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,28 @@ path/
<config-digest> Hyperlight config JSON
<snapshot-digest> raw memory bytes
(`memory_size` bytes)
<transport-digest> canonical virtqueue rings
```

Three blob kinds per tag:
Four blob kinds per tag:

* **manifest** (`application/vnd.oci.image.manifest.v1+json`). Tiny JSON
pointer record selected via `index.json`. References one config and
one layer by digest.
* **config** (`application/vnd.hyperlight.snapshot.config.v1+json`). The
two layers by digest.
* **config** (`application/vnd.hyperlight.snapshot.config.v2+json`). The
snapshot descriptor: arch, hypervisor, CPU vendor, ABI version,
resume address and captured registers, memory layout, registered
host functions, snapshot generation counter. Loaded eagerly and
fully parsed.
resume address and captured registers, memory and transport layout,
registered host functions, and snapshot generation counter. Loaded
eagerly and fully parsed.
* **layer / memory** (`application/vnd.hyperlight.snapshot.memory.v1`).
The raw guest memory image, exactly `memory_size` bytes. mmap'd on
restore.
* **layer / transport**
(`application/vnd.hyperlight.snapshot.transport.v1`). A bounded
binary image of the canonical G2H and H2G rings.

The runtime queue protocol and canonical checkpoint are described in
[Virtqueue host and guest communication](./virtio-host-guest-communication.md).

Blob filenames are the sha256 of the blob bytes, so identical blobs
across tags are stored once.
Expand All @@ -55,8 +62,8 @@ A single saved `Snapshot` consists of exactly:
config blob for tooling visibility,
* one **manifest** blob (referenced by that index entry),
* one **config** blob (referenced by the manifest's `config` field),
* one **layer** blob (the only entry in the manifest's `layers`
array, holding the raw memory image).
* one memory **layer** blob,
* one transport **layer** blob.

Saving two snapshots under different tags into the same `path`
produces two index entries and two manifests. Configs and layers are
Expand Down Expand Up @@ -98,12 +105,12 @@ podman), `go-containerregistry` (crane), and `regclient`.
## Read semantics

`Snapshot::load(path, reference)` reads a snapshot. It does not check
the manifest, config, or snapshot blobs against their sha256 digests.
the manifest, config, memory, or transport blobs against their sha256 digests.
`reference` is an
[`OciReference`], either a tag that matches the
`org.opencontainers.image.ref.name` annotation or the manifest
digest returned by `save`. `Snapshot::checked_load` adds the digest
check on those three blobs, catching accidental corruption on disk.
check on all four blobs, catching accidental corruption on disk.
Both run every other check (OCI structure, descriptor sizes, schema
versions, arch / hypervisor / CPU vendor / ABI tags, layout bounds,
entrypoint bounds). The caller is responsible for trusting the source.
Expand Down
23 changes: 13 additions & 10 deletions docs/snapshot-versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,34 @@ existing snapshots loadable, or while rejecting them with a clear error.

## What is versioned

A snapshot carries three independently evolvable version markers:
A snapshot carries four independently evolvable version markers:

* **Memory blob ABI**, `SNAPSHOT_ABI_VERSION` (a `u32` inside the
config blob, defined in
[src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs](../src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs)).
This is what the host reads back from a snapshot: the `OutBAction`
and `VmAction` port numbers, the input and output buffer stack
format, the offset and size of each memory region (including the
`HyperlightPEB` size), and the calling convention for guest function
entry. A change to any of these breaks older snapshots unless the
loader adds a compat path.
and `VmAction` port numbers, the virtqueue transport layout, the
offset and size of each memory region (including the `HyperlightPEB`
size), and the calling convention for guest function entry. A change
to any of these breaks older snapshots unless the loader adds a
compat path.
* **Snapshot blob encoding**, `MT_SNAPSHOT_V1`
(`application/vnd.hyperlight.snapshot.memory.v1`), aliased as
`MT_SNAPSHOT_CURRENT`. This is the on-wire format of the snapshot
blob: framing, section ordering, alignment, dirty/zero-page elision,
anything about how the bytes are packed inside the OCI layer.
* **Config schema**, `MT_CONFIG_V1`
(`application/vnd.hyperlight.snapshot.config.v1+json`), aliased as
* **Transport blob encoding**, `MT_TRANSPORT_V1`
(`application/vnd.hyperlight.snapshot.transport.v1`), aliased as
`MT_TRANSPORT_CURRENT`. This is the binary encoding of canonical
virtqueue state stored outside the memory layer.
* **Config schema**, `MT_CONFIG_V2`
(`application/vnd.hyperlight.snapshot.config.v2+json`), aliased as
`MT_CONFIG_CURRENT`. This is the JSON shape of the config blob:
field names, types, required vs optional, the descriptors the loader
needs in order to reconstruct the sandbox (memory sizes, buffer
sizes, `abi_version`, `hyperlight_version`, etc.). Renaming a field,
changing its type, or adding a required field is a schema change and
bumps this constant.
bumps this constant. Version 2 requires a transport layer.

The `OCI_LAYOUT_VERSION` constant is pinned by the OCI image-layout
spec at `1.0.0`.
Expand Down Expand Up @@ -382,4 +386,3 @@ major:
* The loader accepts the old `abi_version` (Option 2 step 4), so the old
golden loads.
* Register the host functions the old golden's checks call.

Loading