Skip to content

Create sandboxes directly from snapshots#1459

Open
ludfjig wants to merge 7 commits into
hyperlight-dev:mainfrom
ludfjig:from_snapshot
Open

Create sandboxes directly from snapshots#1459
ludfjig wants to merge 7 commits into
hyperlight-dev:mainfrom
ludfjig:from_snapshot

Conversation

@ludfjig
Copy link
Copy Markdown
Contributor

@ludfjig ludfjig commented May 15, 2026

Builds a ready-to-use MultiUseSandbox directly from an Arc<Snapshot> without going through UninitializedSandbox + evolve(). Building block for OCI-backed snapshot loading on a follow-up branch.

Adds:

  • HostFunctions newtype around the internal FunctionRegistry. default() pre-registers HostPrint, empty() starts empty.
  • MultiUseSandbox::from_snapshot(snap, host_funcs, cfg). Snapshot is the source of truth for layout. cfg is honored for non-layout fields (timeouts, debug info, interrupt retry delay).
  • Snapshot records host function metadata. from_snapshot rejects registries missing functions or with mismatched signatures. Pre-init snapshots record nothing and accept any registry, matching the UninitializedSandbox contract.
  • gdb support extended to sandboxes built via from_snapshot.

Recommend to review commit-by-commit

@ludfjig ludfjig added the kind/enhancement For PRs adding features, improving functionality, docs, tests, etc. label May 15, 2026
@ludfjig ludfjig marked this pull request as ready for review May 15, 2026 07:10
@danbugs danbugs requested a review from Copilot May 15, 2026 16:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the ability to construct a MultiUseSandbox directly from an Arc<Snapshot>, bypassing UninitializedSandbox + evolve(). Snapshots now record the host functions registered at capture time, and the new constructor validates that the provided host-function set is a superset of those required. The gdb entry-point breakpoint mechanism is reworked into a one-shot tracked on the VM so it works for both Initialise and Call snapshots.

Changes:

  • New HostFunctions newtype (with default() pre-registering HostPrint and new() empty) and MultiUseSandbox::from_snapshot(snap, host_funcs, cfg).
  • Snapshot now carries HostFunctionDetails and exposes validate_host_functions to reject missing/mismatched signatures; pre-init snapshots accept any registry.
  • Gdb support: replaces VcpuStopReason::EntryPointBp with a one_shot_entry_bp field cleared by the run loop; entry breakpoint installed for both Initialise and Call entries; new gdb e2e test for snapshot path.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/hyperlight_host/src/sandbox/initialized_multi_use.rs Implements from_snapshot, layout-override warning, snapshot host-function plumbing, and tests.
src/hyperlight_host/src/sandbox/snapshot/mod.rs Adds host_functions field and validate_host_functions; updates constructors and tests.
src/hyperlight_host/src/sandbox/host_funcs.rs Adds HostFunctions newtype and with_default_host_print; changes register_host_function to infallible; tweaks From impl.
src/hyperlight_host/src/sandbox/uninitialized.rs Uses with_default_host_print and removes redundant register_print call in new.
src/hyperlight_host/src/func/host_functions.rs Adapts Registerable impls to infallible registry call; adds HostFunctions impl.
src/hyperlight_host/src/mem/mgr.rs Plumbs HostFunctionDetails into snapshot(); inherits snapshot_count in from_snapshot.
src/hyperlight_host/src/mem/layout.rs Widens visibility of several layout fields and MAX_MEMORY_SIZE.
src/hyperlight_host/src/lib.rs Re-exports HostFunctions.
src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs Sets up one-shot entry breakpoint covering Initialise and Call.
src/hyperlight_host/src/hypervisor/hyperlight_vm/mod.rs Adds one_shot_entry_bp field and run-loop logic to clear it on first hit.
src/hyperlight_host/src/hypervisor/gdb/mod.rs Removes VcpuStopReason::EntryPointBp variant.
src/hyperlight_host/src/hypervisor/gdb/event_loop.rs Drops handling for the removed variant.
src/hyperlight_host/src/hypervisor/gdb/arch.rs vcpu_stop_reason no longer takes/uses the entrypoint, becomes side-effect-free classifier.
src/hyperlight_host/examples/guest-debugging/main.rs Extracts gdb test helpers and adds test_gdb_from_snapshot.

Comment thread src/hyperlight_host/src/sandbox/initialized_multi_use.rs
Comment thread src/hyperlight_host/src/sandbox/initialized_multi_use.rs
Comment thread src/hyperlight_host/src/mem/layout.rs
Comment thread src/hyperlight_host/src/sandbox/host_funcs.rs
Comment thread src/hyperlight_host/src/sandbox/snapshot/mod.rs
Comment thread src/hyperlight_host/examples/guest-debugging/main.rs
Comment thread src/hyperlight_host/src/sandbox/initialized_multi_use.rs
ludfjig added 4 commits May 19, 2026 09:56
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@jsturtevant jsturtevant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I've left a few comments and one missing testing that needs a fix


// Validate that the provided host functions are a superset of
// those required by the snapshot.
snapshot.validate_host_functions(&host_funcs)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the snapshot manager does other validation? Should this validation live with that validation? if not are we missing validation? Can a maliciously crafted snapshot bypass any assumptions we have about the memory layout?

Copy link
Copy Markdown
Contributor Author

@ludfjig ludfjig May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way to create malicious snapshots (yet). Right now they are completely opaque structs to the user. This will change when we load them from disk though (future PR, where I add a lot of validation)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(future PR, where I add a lot of validation)

Glad to hear we are planning this. What if a malicious guest does something (writes some handcrafted assembly) and we take a snapshot with that in there?

Comment thread src/hyperlight_host/src/sandbox/initialized_multi_use.rs
Comment thread src/hyperlight_host/src/sandbox/initialized_multi_use.rs
Copy link
Copy Markdown
Contributor

@danbugs danbugs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM!

}

/// Create a snapshot with the given mapped regions
#[allow(clippy::too_many_arguments)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider a snapshot builder to avoid this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I don't think so, none of these are really optional in that sense

let mgr = crate::mem::mgr::SandboxMemoryManager::from_snapshot(&snapshot)?;
let (mut hshm, gshm) = mgr.build()?;

let page_size = u32::try_from(page_size::get())? as usize;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm--just noticed set set_up_hypervisor_partition takes a usize page_size but vm.initialise takes a u32. Can we improve this to avoid the casts?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, but I'd prefer to fix this in follow up. I don't think we should require both methods to take in page size as well, that seems strange

/// produced by a test-only constructor) accepts any `provided`
/// set.
pub(crate) fn validate_host_functions(&self, provided: &crate::HostFunctions) -> Result<()> {
let required = match &self.host_functions.host_functions {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: host_functions.host_functions

Copy link
Copy Markdown
Contributor Author

@ludfjig ludfjig May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I can fix this in follow up as well

Comment on lines +705 to +708
for req in required {
match provided_funcs
.iter()
.find(|f| f.function_name == req.function_name)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we improve this lookup? For every req function, we look at all provided functions (i.e., O(req * provided)). What if we built a HashMap for the provided functions? This way we'd have a O(provided + req)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point let me investigate. HostFunc is already based on HashMap

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

let sregs = snapshot.sregs().ok_or_else(|| {
crate::new_error!("snapshot with NextAction::Call must have captured sregs")
})?;
vm.apply_sregs(hshm.layout.get_pt_base_gpa(), sregs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding a comment that this is overwriting the apply_sregs from vm.initialise?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not, vm.initialize is a noop


/// Construct the (out_file_path, cmd_file_path, manifest_dir)
/// triple every gdb test needs.
fn gdb_test_paths(name: &str) -> (String, String, String) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GDB changes look good to me, but it might be worth getting @dblnz to have a look :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree I would be more confident if @dblnz took a look as well :)

ludfjig added 3 commits May 20, 2026 11:27
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/enhancement For PRs adding features, improving functionality, docs, tests, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants