diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d430b0b5..3d9cf7d0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Prerelease] - Unreleased +### Changed +* **Breaking:** Snapshots use ABI 10 and config schema v6. Guests must be rebuilt + and existing snapshots regenerated. Guest entry takes the random seed, host + page size, and log level. + +### Removed +* **Breaking:** `SandboxBuilder::init_data`, `GuestEnvironment`, `GuestBlob`, + `GuestHandle`, `HyperlightPEB`, and `read_n_bytes_from_user_memory`. + Pass startup data through a host function and retain its `ByteChunks` reply. + `UninitializedSandbox::new` takes a `GuestBinary`. + ## [v0.19.0] - 2026-11-04 ### Added diff --git a/Justfile b/Justfile index a05dd8f3d..510e1c041 100644 --- a/Justfile +++ b/Justfile @@ -79,7 +79,7 @@ build-and-move-c-guests: (build-c-guests "debug") (move-c-guests "debug") (build # Uses plain cargo with non-PIE link flags and the sysroot of the PIE build. # cargo-hyperlight would leak these RUSTFLAGS into its sysroot wrapper build. build-rust-guests-non-pie target=default-target: (build-rust-guests target) - {{ if os() == "windows" { "$env:RUSTC_BOOTSTRAP=1; $env:RUSTFLAGS='--sysroot=' + (Resolve-Path src/tests/rust_guests/target/sysroot).Path + ' -C relocation-model=static -C link-args=--no-pie -C link-args=--image-base=0x1000000 --cfg=hyperlight --check-cfg=cfg(hyperlight) -Clink-args=-eentrypoint';" } else { "" } }} cd src/tests/rust_guests/simpleguest && {{ if os() == "windows" { "" } else { "RUSTC_BOOTSTRAP=1 RUSTFLAGS=\"--sysroot=$(cd .. && pwd)/target/sysroot -C relocation-model=static -C link-args=--no-pie -C link-args=--image-base=0x1000000 --cfg=hyperlight --check-cfg=cfg(hyperlight) -Clink-args=-eentrypoint\"" } }} cargo build --target {{ hyperlight-target }} --target-dir ../target-non-pie/build --profile={{ if target == "debug" { "dev" } else { target } }} + {{ if os() == "windows" { "$env:RUSTC_BOOTSTRAP=1; $env:RUSTFLAGS='--sysroot=' + (Resolve-Path src/tests/rust_guests/target/sysroot).Path + ' -C relocation-model=static -C link-args=--no-pie -C link-args=--image-base=0x1000000 --cfg=hyperlight --check-cfg=cfg(hyperlight) -Clink-args=-eentrypoint';" } else { "" } }} cd src/tests/rust_guests/simpleguest && {{ if os() == "windows" { "" } else { "RUSTC_BOOTSTRAP=1 RUSTFLAGS=\"--sysroot=$(cd .. && pwd)/target/sysroot -C relocation-model=static -C link-args=--no-pie -C link-args=--image-base=0x1000000 --cfg=hyperlight --check-cfg=cfg(hyperlight) -Clink-args=-eentrypoint\"" } }} cargo build --bin simpleguest --target {{ hyperlight-target }} --target-dir ../target-non-pie/build --profile={{ if target == "debug" { "dev" } else { target } }} non_pie_guests_target := "src/tests/rust_guests/target-non-pie/build/" + hyperlight-target diff --git a/docs/how-to-build-a-hyperlight-guest-binary.md b/docs/how-to-build-a-hyperlight-guest-binary.md index 7730e690b..66b7b2da0 100644 --- a/docs/how-to-build-a-hyperlight-guest-binary.md +++ b/docs/how-to-build-a-hyperlight-guest-binary.md @@ -119,6 +119,36 @@ If you mix the manual form with `#[guest_function]`, registrations from the macro still happen automatically. Your `hyperlight_main` only needs to do whatever extra setup the macros do not cover. +### Startup data + +A guest keeps host data by storing a `Vec` +(`hyperlight_common::func::Bytes`) in its state. The data reaches the guest by +one of two routes: + +| | Host function reply | Guest function argument | +|---|---|---| +| Started by | The guest, in `hyperlight_main` | The host, in a guest call | +| Host code | Register the function before `SandboxBuilder::build` or `UninitializedSandbox::evolve` | Call the guest function with a `Vec` argument | +| Available | Before the first guest call | After the call that sends it | +| Pool size | `SandboxBuilder::g2h_pool_pages` | `SandboxBuilder::h2g_pool_pages` | + +Use a host function reply for data the guest needs at startup. Use a guest +function argument for data the host supplies or replaces later. + +Either route can retain or copy the data. The Rust type of the host function's +return value or the guest function's parameter decides: + +* `Vec` retains the transport buffers without copying. The buffers stay + valid across calls, snapshots, restores, and forks until the last clone + drops. +* `Vec` copies the data into guest memory and releases the transport + buffers at once. + +Retained buffers keep their pool slots, which reduces the capacity left for +later calls. Size the pool listed above for the retained data plus normal +traffic. See +[Capacity and latency](./virtio-host-guest-communication.md#capacity-and-latency). + ### Troubleshooting #### "duplicate lang item `panic_impl`" error diff --git a/docs/snapshot-oci-format.md b/docs/snapshot-oci-format.md index 279eb3b39..bc5c4e575 100644 --- a/docs/snapshot-oci-format.md +++ b/docs/snapshot-oci-format.md @@ -32,7 +32,7 @@ Four blob kinds per tag: * **manifest** (`application/vnd.oci.image.manifest.v1+json`). Tiny JSON pointer record selected via `index.json`. References one config, the snapshot blobs, and one transport layer by digest. -* **config** (`application/vnd.hyperlight.snapshot.config.v5+json`). The +* **config** (`application/vnd.hyperlight.snapshot.config.v6+json`). The snapshot descriptor: arch, hypervisor, CPU vendor, ABI version, resume address and captured registers, memory and transport layout, registered host functions, the start address, data length, and page-table diff --git a/docs/snapshot-versioning.md b/docs/snapshot-versioning.md index 1a92a423b..fc43f8d1c 100644 --- a/docs/snapshot-versioning.md +++ b/docs/snapshot-versioning.md @@ -14,8 +14,8 @@ A snapshot carries four independently evolvable version markers: [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 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 + offset and size of each memory region, 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_V2` @@ -27,8 +27,8 @@ A snapshot carries four independently evolvable version markers: (`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_V5` - (`application/vnd.hyperlight.snapshot.config.v5+json`), aliased as +* **Config schema**, `MT_CONFIG_V6` + (`application/vnd.hyperlight.snapshot.config.v6+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 @@ -36,7 +36,8 @@ A snapshot carries four independently evolvable version markers: changing its type, or adding a required field is a schema change and bumps this constant. Version 4 describes snapshot blobs, the blob with the page tables, and transport geometry, and requires a transport - layer. Version 5 drops the heap size. Config v1 to v4 are incompatible + layer. Version 5 drops the heap size. Version 6 contains code and scratch + geometry without init-data fields. Config v1 to v5 are incompatible with the current ABI. ABI 6 requires checkpoint readiness after guest initialization and permits @@ -52,6 +53,9 @@ regenerated. ABI 9 removes the heap region from snapshot memory. Earlier snapshots must be regenerated. +ABI 10 contains no PEB or init-data region. Guest entry takes the random seed, +host page size, and log level. Earlier snapshots must be regenerated. + The `OCI_LAYOUT_VERSION` constant is pinned by the OCI image-layout spec at `1.0.0`. @@ -83,7 +87,7 @@ Compile-time tripwires in [src/hyperlight_host/src/sandbox/snapshot/tripwires.rs](../src/hyperlight_host/src/sandbox/snapshot/tripwires.rs) hold a copy of every value that defines the format: `SNAPSHOT_ABI_VERSION`, the snapshot and config media-type strings, the -OCI layout version, the `HyperlightPEB` size, every `OutBAction` and +OCI layout version, every `OutBAction` and `VmAction` discriminant, and `BASE_ADDRESS`. If the source value drifts from the copy in `tripwires.rs`, the crate fails to compile. diff --git a/src/hyperlight_common/src/lib.rs b/src/hyperlight_common/src/lib.rs index dcf791fec..2f37be8fe 100644 --- a/src/hyperlight_common/src/lib.rs +++ b/src/hyperlight_common/src/lib.rs @@ -24,9 +24,6 @@ pub mod layout; // cbindgen:ignore pub mod log_level; -/// cbindgen:ignore -pub mod mem; - /// cbindgen:ignore pub mod outb; diff --git a/src/hyperlight_common/src/mem.rs b/src/hyperlight_common/src/mem.rs deleted file mode 100644 index b5b61209a..000000000 --- a/src/hyperlight_common/src/mem.rs +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 The Hyperlight Authors. - -/// A memory region in the guest address space -#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] -#[repr(C)] -pub struct GuestMemoryRegion { - /// The size of the memory region - pub size: u64, - /// The address of the memory region - pub ptr: u64, -} - -#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] -#[repr(C)] -pub struct HyperlightPEB { - pub init_data: GuestMemoryRegion, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn peb_round_trip() { - let peb = HyperlightPEB { - init_data: GuestMemoryRegion { - size: 0x1111, - ptr: 0x2222, - }, - }; - let bytes = bytemuck::bytes_of(&peb); - let peb2 = *bytemuck::from_bytes::(bytes); - let peb2_bytes = bytemuck::bytes_of(&peb2); - assert_eq!(peb, peb2); - assert_eq!(bytes, peb2_bytes); - } -} diff --git a/src/hyperlight_guest/src/guest_handle/handle.rs b/src/hyperlight_guest/src/guest_handle/handle.rs deleted file mode 100644 index 47b4aa845..000000000 --- a/src/hyperlight_guest/src/guest_handle/handle.rs +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 The Hyperlight Authors. - -use alloc::format; -use alloc::vec::Vec; - -use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; -use hyperlight_common::mem::HyperlightPEB; -use tracing::instrument; - -use crate::error::{HyperlightGuestError, Result}; - -/// Access to memory regions described by the guest's `HyperlightPEB`. -#[derive(Debug, Clone, Copy, Default)] -pub struct GuestHandle { - peb: Option<*mut HyperlightPEB>, -} - -impl GuestHandle { - /// Creates a new uninitialized guest state. - pub const fn new() -> Self { - Self { peb: None } - } - - /// Initializes the guest state with a given PEB pointer. - pub fn init(peb: *mut HyperlightPEB) -> Self { - Self { peb: Some(peb) } - } - - /// Returns the PEB pointer - pub fn peb(&self) -> Option<*mut HyperlightPEB> { - self.peb - } - - /// Get user memory region as bytes. - #[instrument(skip_all, level = "Trace")] - pub fn read_n_bytes_from_user_memory(&self, num: u64) -> Result> { - let peb_ptr = self.peb().unwrap(); - // SAFETY: GuestHandle is initialized with the PEB provided by the host, - // which remains valid for the guest lifetime. - let init_data = unsafe { (*peb_ptr).init_data }; - - if num > init_data.size { - return Err(HyperlightGuestError::new( - ErrorCode::GuestError, - format!( - "Requested {} bytes from user memory, but only {} bytes are available", - num, init_data.size - ), - )); - } - - // SAFETY: The PEB describes a valid user memory region and num was - // checked against its size. - let bytes = - unsafe { core::slice::from_raw_parts(init_data.ptr as *const u8, num as usize) }; - Ok(bytes.to_vec()) - } -} diff --git a/src/hyperlight_guest/src/lib.rs b/src/hyperlight_guest/src/lib.rs index 1746734ec..b022860b0 100644 --- a/src/hyperlight_guest/src/lib.rs +++ b/src/hyperlight_guest/src/lib.rs @@ -19,7 +19,3 @@ pub mod paging; pub mod prim_alloc; pub mod transport; pub mod types; - -pub mod guest_handle { - pub mod handle; -} diff --git a/src/hyperlight_guest_bin/src/arch/aarch64/mod.rs b/src/hyperlight_guest_bin/src/arch/aarch64/mod.rs index c04a96bfb..b97de1840 100644 --- a/src/hyperlight_guest_bin/src/arch/aarch64/mod.rs +++ b/src/hyperlight_guest_bin/src/arch/aarch64/mod.rs @@ -56,22 +56,16 @@ unsafe fn init_vbar() { /// Machine-specific initialisation; calls [`crate::generic_init`] /// once VBAR and the main stack have been set up #[unsafe(no_mangle)] -pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) -> ! { +pub extern "C" fn entrypoint(seed: u64, ops: u64, max_log_level: u64) -> ! { unsafe { init_vbar(); let stack_top = crate::init::init_stack(); - pivot_stack(peb_address, seed, ops, max_log_level, stack_top); + pivot_stack(seed, ops, max_log_level, stack_top); } } unsafe extern "C" { - unsafe fn pivot_stack( - peb_address: u64, - seed: u64, - ops: u64, - max_log_level: u64, - stack_top: u64, - ) -> !; + unsafe fn pivot_stack(seed: u64, ops: u64, max_log_level: u64, stack_top: u64) -> !; } core::arch::global_asm!(" @@ -83,7 +77,7 @@ core::arch::global_asm!(" msr SPSel, #1\n mov sp, x5\n msr SPSel, #0\n - mov sp, x4\n + mov sp, x3\n bl {generic_init}\n ldr x1, ={hlt_addr}\n str x0, [x1]\n diff --git a/src/hyperlight_guest_bin/src/arch/amd64/init.rs b/src/hyperlight_guest_bin/src/arch/amd64/init.rs index 613e7fd6e..044ad960f 100644 --- a/src/hyperlight_guest_bin/src/arch/amd64/init.rs +++ b/src/hyperlight_guest_bin/src/arch/amd64/init.rs @@ -94,7 +94,7 @@ unsafe fn init_tss(pc: *mut ProcCtrl) { /// Machine-specific initialisation; calls [`crate::generic_init`] /// once stack, CoW, etc have been set up. #[unsafe(no_mangle)] -pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) { +pub extern "C" fn entrypoint(seed: u64, ops: u64, max_log_level: u64) { unsafe { // Allocate a VA for processor control structures which must // survive snapshotting at the same VA. @@ -108,18 +108,12 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve // Architecture early init is complete! We pivot now to // executing on the main stack, and jump into generic // initialisation code in lib.rs - pivot_stack(peb_address, seed, ops, max_log_level, stack_top); + pivot_stack(seed, ops, max_log_level, stack_top); } } unsafe extern "C" { - unsafe fn pivot_stack( - peb_address: u64, - seed: u64, - ops: u64, - max_log_level: u64, - stack_top: u64, - ) -> !; + unsafe fn pivot_stack(seed: u64, ops: u64, max_log_level: u64, stack_top: u64) -> !; } // Mark this as the bottom-most frame for debuggers: @@ -135,7 +129,7 @@ core::arch::global_asm!(" pivot_stack:\n .cfi_startproc\n .cfi_undefined rip\n - mov rsp, r8\n + mov rsp, rcx\n xor ebp, ebp\n call {generic_init}\n mov dx, {halt_port}\n diff --git a/src/hyperlight_guest_bin/src/host_comm.rs b/src/hyperlight_guest_bin/src/host_comm.rs index 44cb11e8d..72ae36f3b 100644 --- a/src/hyperlight_guest_bin/src/host_comm.rs +++ b/src/hyperlight_guest_bin/src/host_comm.rs @@ -13,8 +13,6 @@ use hyperlight_common::func::{ParameterTuple, SupportedReturnType}; use hyperlight_guest::error::{HyperlightGuestError, Result}; use hyperlight_guest::{bail, transport}; -use crate::GUEST_HANDLE; - /// Call a host function and convert its response outside the transport borrow. pub fn call_host_function( function_name: &str, @@ -41,11 +39,6 @@ where call_host_function::(function_name.as_ref(), Some(args.into_value()), T::TYPE) } -pub fn read_n_bytes_from_user_memory(num: u64) -> Result> { - let handle = unsafe { GUEST_HANDLE }; - handle.read_n_bytes_from_user_memory(num) -} - /// Print a message using the host's print function. pub fn print_output_with_host_print(function_call: FunctionCall) -> Result { if let ParameterValue::String(message) = function_call.parameters.unwrap().remove(0) { diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 90793db86..8fd9cff22 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -12,11 +12,9 @@ use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::log_level::{GUEST_LOG_FILTER_UPDATE_PENDING, GuestLogFilter}; -use hyperlight_common::mem::HyperlightPEB; #[cfg(feature = "mem_profile")] use hyperlight_common::outb::OutBAction; use hyperlight_guest::exit::write_abort; -use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest::layout::{HEAP_GVA_END, HEAP_GVA_START}; use talc::TalcLock; use talc::source::Manual; @@ -116,7 +114,6 @@ pub(crate) static HEAP_ALLOCATOR: GuestHeap = GuestHeap::new(Manual); #[global_allocator] pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap = ProfiledLockedHeap(GuestHeap::new(Manual)); -pub static mut GUEST_HANDLE: GuestHandle = GuestHandle::new(); pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister = GuestFunctionRegister::new(); @@ -254,17 +251,8 @@ core::arch::global_asm!( /// Architecture-nonspecific initialisation: set up the heap, /// coordinate some addresses and configuration with the host, and run /// user initialisation -pub(crate) extern "C" fn generic_init( - peb_address: u64, - _seed: u64, - ops: u64, - max_log_level: u64, -) -> u64 { +pub(crate) extern "C" fn generic_init(_seed: u64, ops: u64, max_log_level: u64) -> u64 { unsafe { - GUEST_HANDLE = GuestHandle::init(peb_address as *mut HyperlightPEB); - #[allow(static_mut_refs)] - let peb_ptr = GUEST_HANDLE.peb().unwrap(); - #[cfg(not(all(feature = "mem_profile", target_arch = "x86_64")))] let heap_allocator = &HEAP_ALLOCATOR; #[cfg(all(feature = "mem_profile", target_arch = "x86_64"))] @@ -280,8 +268,7 @@ pub(crate) extern "C" fn generic_init( (HEAP_GVA_END - HEAP_GVA_START) as usize, ) .expect("Failed to claim guest heap"); - peb_ptr - }; + } // Save the guest start TSC for tracing #[cfg(feature = "trace_guest")] @@ -289,8 +276,7 @@ pub(crate) extern "C" fn generic_init( #[cfg(feature = "libc")] unsafe { - let srand_seed = (((peb_address << 8) ^ (_seed >> 4)) >> 32) as u32; - srand(srand_seed); + srand(_seed as u32); } unsafe { diff --git a/src/hyperlight_host/src/hypervisor/hyperlight_vm/aarch64.rs b/src/hyperlight_host/src/hypervisor/hyperlight_vm/aarch64.rs index 9f527987e..08cd88f4e 100644 --- a/src/hyperlight_host/src/hypervisor/hyperlight_vm/aarch64.rs +++ b/src/hyperlight_host/src/hypervisor/hyperlight_vm/aarch64.rs @@ -107,10 +107,8 @@ impl HyperlightVm { Ok(ret) } - #[allow(clippy::too_many_arguments)] pub(crate) fn initialise( &mut self, - peb_addr: crate::mem::ptr::RawPtr, seed: u64, mem_mgr: &mut SandboxMemoryManager, host_funcs: &Arc>, @@ -120,10 +118,9 @@ impl HyperlightVm { return Ok(()); }; let mut x: [u64; 31] = [0; 31]; - x[0] = peb_addr.into(); - x[1] = seed; - x[2] = self.page_size as u64; - x[3] = get_guest_log_filter(guest_max_log_level); + x[0] = seed; + x[1] = self.page_size as u64; + x[2] = get_guest_log_filter(guest_max_log_level); let regs = CommonRegisters { pc: initialise, sp: self.rsp_gva, diff --git a/src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs b/src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs index f6037240a..b7c946bb0 100644 --- a/src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs +++ b/src/hyperlight_host/src/hypervisor/hyperlight_vm/x86_64.rs @@ -37,7 +37,6 @@ use crate::hypervisor::virtual_machine::{ #[cfg(crashdump)] use crate::mem::memory_region::MemoryRegion; use crate::mem::mgr::SandboxMemoryManager; -use crate::mem::ptr::RawPtr; use crate::mem::shared_mem::{GuestSharedMemory, HostSharedMemory}; use crate::sandbox::SandboxConfiguration; use crate::sandbox::host_funcs::FunctionRegistry; @@ -165,13 +164,10 @@ impl HyperlightVm { Ok(ret) } - /// Initialise the internally stored vCPU with the given PEB address and - /// random number seed, then run it until a HLT instruction. + /// Initialise the vCPU with a random seed and run it until a HLT instruction. #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] - #[allow(clippy::too_many_arguments)] pub(crate) fn initialise( &mut self, - peb_addr: RawPtr, seed: u64, mem_mgr: &mut SandboxMemoryManager, host_funcs: &Arc>, @@ -192,10 +188,9 @@ impl HyperlightVm { rsp: self.rsp_gva - 8, // function args - rdi: peb_addr.into(), - rsi: seed, - rdx: self.page_size as u64, - rcx: get_guest_log_filter(guest_max_log_level), + rdi: seed, + rsi: self.page_size as u64, + rdx: get_guest_log_filter(guest_max_log_level), rflags: 1 << 1, ..Default::default() @@ -866,7 +861,6 @@ mod tests { use crate::mem::layout::SandboxMemoryLayout; use crate::mem::memory_region::MemoryRegionFlags; use crate::mem::mgr::{GuestPageTableBuffer, SandboxMemoryManager}; - use crate::mem::ptr::RawPtr; use crate::mem::shared_mem::ExclusiveSharedMemory; use crate::sandbox::SandboxConfiguration; use crate::sandbox::host_funcs::FunctionRegistry; @@ -1414,7 +1408,7 @@ mod tests { fn create_test_layout(code_size: usize) -> (SandboxMemoryLayout, Box<[u8]>) { let config = SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(config, code_size, 4096, None).unwrap(); + let layout = SandboxMemoryLayout::new(config, code_size).unwrap(); let pt_base_gpa = layout.get_pt_base_gpa(); let pt_buf = GuestPageTableBuffer::new(pt_base_gpa as usize); @@ -1467,7 +1461,6 @@ mod tests { let mut snapshot_contents = vec![0u8; mem_size]; snapshot_contents[layout.guest_code_offset()..layout.guest_code_offset() + code.len()] .copy_from_slice(code); - layout.write_peb(&mut snapshot_contents).unwrap(); let snapshot_memory = crate::sandbox::snapshot::memory::tests::flat_memory( &snapshot_contents, &pt_bytes, @@ -1485,7 +1478,6 @@ mod tests { let (mut hshm, gshm) = mem_mgr.build().unwrap(); - let peb_address = gshm.layout.peb_address(); let stack_top_gva = hyperlight_common::layout::SCRATCH_TOP_GVA as u64 - hyperlight_common::layout::SCRATCH_TOP_EXN_STACK_OFFSET + 1; @@ -1499,12 +1491,10 @@ mod tests { .unwrap(); let seed = rand::rng().random::(); - let peb_addr = RawPtr::from(u64::try_from(peb_address).unwrap()); let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default())); - vm.initialise(peb_addr, seed, &mut hshm, &host_funcs, None) - .unwrap(); + vm.initialise(seed, &mut hshm, &host_funcs, None).unwrap(); TestVmContext { vm, diff --git a/src/hyperlight_host/src/hypervisor/mod.rs b/src/hyperlight_host/src/hypervisor/mod.rs index 79501a67a..501ba4358 100644 --- a/src/hyperlight_host/src/hypervisor/mod.rs +++ b/src/hyperlight_host/src/hypervisor/mod.rs @@ -481,7 +481,6 @@ pub(crate) mod tests { return Ok(()); } - use crate::mem::ptr::RawPtr; use crate::sandbox::host_funcs::FunctionRegistry; let filename = dummy_guest_as_pathbuf(); @@ -503,20 +502,13 @@ pub(crate) mod tests { // Set up required parameters for initialise // The dummy guest halts only when it receives these values. - let peb_addr = RawPtr::from(0x230000u64); let seed = 1234567890u64; let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default())); let guest_max_log_level = Some(tracing_core::LevelFilter::ERROR); // Test the initialise method - vm.initialise( - peb_addr, - seed, - &mut mem_mgr, - &host_funcs, - guest_max_log_level, - ) - .unwrap(); + vm.initialise(seed, &mut mem_mgr, &host_funcs, guest_max_log_level) + .unwrap(); Ok(()) } diff --git a/src/hyperlight_host/src/mem/exe.rs b/src/hyperlight_host/src/mem/exe.rs index aedb85b1a..fbbc97c4e 100644 --- a/src/hyperlight_host/src/mem/exe.rs +++ b/src/hyperlight_host/src/mem/exe.rs @@ -177,10 +177,10 @@ mod tests { /// A guest not built with hyperlight-guest-bin has no version note and /// should be accepted (no version check is performed). #[test] - fn from_env_accepts_guest_without_version_note() { + fn from_binary_accepts_guest_without_version_note() { let path = dummy_guest_as_pathbuf(); - let result = crate::sandbox::snapshot::Snapshot::from_env( + let result = crate::sandbox::snapshot::Snapshot::from_binary( crate::GuestBinary::FilePath(path), crate::sandbox::SandboxConfiguration::default(), ); @@ -202,13 +202,13 @@ mod tests { ); } - /// Load an unpatched simpleguest through `Snapshot::from_env` and verify + /// Load an unpatched simpleguest through `Snapshot::from_binary` and verify /// that it succeeds when the embedded version matches the host version. #[test] - fn from_env_accepts_matching_version() { + fn from_binary_accepts_matching_version() { let path = simple_guest_as_pathbuf(); - let result = crate::sandbox::snapshot::Snapshot::from_env( + let result = crate::sandbox::snapshot::Snapshot::from_binary( crate::GuestBinary::FilePath(path), crate::sandbox::SandboxConfiguration::default(), ); @@ -216,13 +216,13 @@ mod tests { assert!(result.is_ok(), "should accept matching version"); } - /// Load a patched guest binary through `Snapshot::from_env` and verify + /// Load a patched guest binary through `Snapshot::from_binary` and verify /// that a version mismatch produces `GuestBinVersionMismatch`. #[test] - fn from_env_rejects_version_mismatch() { + fn from_binary_rejects_version_mismatch() { let bytes = simpleguest_with_patched_version(); - let result = crate::sandbox::snapshot::Snapshot::from_env( + let result = crate::sandbox::snapshot::Snapshot::from_binary( crate::GuestBinary::Buffer(bytes), crate::sandbox::SandboxConfiguration::default(), ); diff --git a/src/hyperlight_host/src/mem/layout.rs b/src/hyperlight_host/src/mem/layout.rs index 0cf9a5b3d..0543e5abc 100644 --- a/src/hyperlight_host/src/mem/layout.rs +++ b/src/hyperlight_host/src/mem/layout.rs @@ -10,10 +10,6 @@ //! +-------------------------------------------+ //! | Guest Page Tables | //! +-------------------------------------------+ -//! | Init Data | (GuestBlob size) -//! +-------------------------------------------+ -//! | PEB Struct | (HyperlightPEB size) -//! +-------------------------------------------+ //! | Guest Code | //! +-------------------------------------------+ 0x1_000 //! | NULL guard page | @@ -24,9 +20,6 @@ //! [`hyperlight_common::layout::SNAPSHOT_PT_GVA`] = //! 0xffff_8000_0000_0000. //! -//! - `InitData` - some extra data that can be loaded onto the sandbox during -//! initialization. -//! //! The guest heap is outside this layout. The guest maps heap pages //! from scratch on first touch. //! @@ -49,17 +42,14 @@ //! +-------------------------------------------+ (scratch size) use std::fmt::Debug; -use std::mem::size_of; use hyperlight_common::layout::{QueueDims, TransportArena}; -use hyperlight_common::mem::HyperlightPEB; use hyperlight_common::vmem::PAGE_SIZE; use tracing::{Span, instrument}; -use super::memory_region::MemoryRegionType::{Code, InitData, Peb}; +use super::memory_region::MemoryRegionType::Code; use super::memory_region::{ - DEFAULT_GUEST_BLOB_MEM_FLAGS, GuestMemoryRegion, MemoryRegion_, MemoryRegionFlags, - MemoryRegionVecBuilder, + GuestMemoryRegion, MemoryRegion_, MemoryRegionFlags, MemoryRegionVecBuilder, }; use crate::error::HyperlightError::{MemoryRequestTooBig, MemoryRequestTooSmall}; use crate::sandbox::SandboxConfiguration; @@ -71,10 +61,6 @@ pub(crate) struct SandboxMemoryLayout { code_size: usize, /// Guest virtual address of the code section. code_gva: usize, - /// The size of the init data section (guest blob). - init_data_size: usize, - /// Permission flags for the init data region. - init_data_permissions: Option, /// The size of the scratch region in physical memory. scratch_size: usize, /// G2H ring and buffer pool dimensions. @@ -98,10 +84,6 @@ impl Debug for SandboxMemoryLayout { ) .field("Code Size", &format_args!("{:#x}", self.code_size)) .field("Code GVA", &format_args!("{:#x}", self.code_gva)) - .field( - "Init Data Size", - &format_args!("{:#x}", self.init_data_size), - ) .field("Scratch Size", &format_args!("{:#x}", self.scratch_size)) .field("G2H Queue Size", &self.get_g2h_queue_size()) .field("H2G Queue Size", &self.get_h2g_queue_size()) @@ -113,12 +95,6 @@ impl Debug for SandboxMemoryLayout { "Guest Code Offset", &format_args!("{:#x}", self.guest_code_offset()), ) - .field("PEB Offset", &format_args!("{:#x}", self.peb_offset())) - .field("PEB Address", &format_args!("{:#x}", self.peb_address())) - .field( - "Init Data Offset", - &format_args!("{:#x}", self.init_data_offset()), - ) .finish() } } @@ -137,12 +113,7 @@ impl SandboxMemoryLayout { /// Create a new `SandboxMemoryLayout` with the given /// `SandboxConfiguration` and code size. #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn new( - cfg: SandboxConfiguration, - code_size: usize, - init_data_size: usize, - init_data_permissions: Option, - ) -> Result { + pub(crate) fn new(cfg: SandboxConfiguration, code_size: usize) -> Result { let scratch_size = cfg.get_scratch_size(); if scratch_size > Self::MAX_MEMORY_SIZE { return Err(MemoryRequestTooBig(scratch_size, Self::MAX_MEMORY_SIZE)); @@ -176,8 +147,6 @@ impl SandboxMemoryLayout { let ret = Self { code_size, code_gva: Self::BASE_ADDRESS, - init_data_size, - init_data_permissions, scratch_size, g2h_dims, h2g_dims, @@ -262,14 +231,6 @@ impl SandboxMemoryLayout { self.code_size } - pub(crate) fn init_data_size(&self) -> usize { - self.init_data_size - } - - pub(crate) fn init_data_permissions(&self) -> Option { - self.init_data_permissions - } - pub(crate) fn get_scratch_size(&self) -> usize { self.scratch_size } @@ -325,48 +286,12 @@ impl SandboxMemoryLayout { let mut builder = MemoryRegionVecBuilder::new(Self::BASE_ADDRESS, Self::BASE_ADDRESS); // code - let peb_offset = builder.push_page_aligned( + let final_offset = builder.push_page_aligned( self.code_size, MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE, Code, ); - let expected_peb_offset = TryInto::::try_into(self.peb_offset())?; - - if peb_offset != expected_peb_offset { - return Err(new_error!( - "PEB offset does not match expected PEB offset expected: {}, actual: {}", - expected_peb_offset, - peb_offset - )); - } - - // PEB - let init_data_offset = - builder.push_page_aligned(size_of::(), MemoryRegionFlags::READ, Peb); - - let expected_init_data_offset = TryInto::::try_into(self.init_data_offset())?; - - if init_data_offset != expected_init_data_offset { - return Err(new_error!( - "Init Data offset does not match expected Init Data offset expected: {}, actual: {}", - expected_init_data_offset, - init_data_offset - )); - } - - // init data - let after_init_offset = if self.init_data_size > 0 { - let mem_flags = self - .init_data_permissions - .unwrap_or(DEFAULT_GUEST_BLOB_MEM_FLAGS); - builder.push_page_aligned(self.init_data_size, mem_flags, InitData) - } else { - init_data_offset - }; - - let final_offset = after_init_offset; - let expected_final_offset = TryInto::::try_into(self.get_memory_size()?)?; // This function is primarily used to construct @@ -389,6 +314,7 @@ impl SandboxMemoryLayout { } let mut regions = builder.build(); + for region in &mut regions { if region.region_type == Code { let end = self @@ -407,10 +333,10 @@ impl SandboxMemoryLayout { Ok(regions) } - /// Set the code GVA after checking that it does not overlap another region. + /// Set the code GVA after checking that its address range fits. pub(crate) fn set_code_gva(&mut self, code_gva: u64) -> Result<()> { let code_gva = usize::try_from(code_gva)?; - let code_end = code_gva + code_gva .checked_add(self.code_size.next_multiple_of(PAGE_SIZE)) .ok_or_else(|| { new_error!( @@ -419,64 +345,8 @@ impl SandboxMemoryLayout { self.code_size ) })?; - for region in self.get_memory_regions()? { - if region.region_type == Code { - continue; - } - if code_gva < region.guest_region.end && region.guest_region.start < code_end { - return Err(new_error!( - "code mapping [{:#x}, {:#x}) conflicts with {:?} region [{:#x}, {:#x})", - code_gva, - code_end, - region.region_type, - region.guest_region.start, - region.guest_region.end, - )); - } - } - self.code_gva = code_gva; - Ok(()) - } - - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn write_init_data(&self, out: &mut [u8], bytes: &[u8]) -> Result<()> { - out[self.init_data_offset()..self.init_data_offset() + self.init_data_size] - .copy_from_slice(bytes); - Ok(()) - } - - /// Write the finished memory layout to `mem` and return `Ok` if - /// successful. - /// - /// Note: `mem` may have been modified, even if `Err` was returned - /// from this function. - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn write_peb(&self, mem: &mut [u8]) -> Result<()> { - use hyperlight_common::mem::GuestMemoryRegion; - - let guest_base = Self::BASE_ADDRESS as u64; - - let peb = HyperlightPEB { - init_data: GuestMemoryRegion { - size: (self.get_unaligned_memory_size() - self.init_data_offset()) as u64, - ptr: guest_base + self.init_data_offset() as u64, - }, - }; - - let offset = self.peb_offset(); - let bytes = bytemuck::bytes_of(&peb); - let end = offset + bytes.len(); - let mem_len = mem.len(); - let dst = mem.get_mut(offset..end).ok_or_else(|| { - new_error!( - "memory too small to write PEB: need {} bytes at offset {:#x}, have {} bytes", - bytes.len(), - offset, - mem_len - ) - })?; - dst.copy_from_slice(bytes); + self.code_gva = code_gva; Ok(()) } } @@ -487,21 +357,6 @@ impl SandboxMemoryLayout { /// is a snapshot ABI break: see the `layout_offsets_are_pinned` test /// and docs/snapshot-versioning.md. impl SandboxMemoryLayout { - /// Offset of the PEB struct within the snapshot region. - pub(crate) fn peb_offset(&self) -> usize { - self.code_size.next_multiple_of(PAGE_SIZE) - } - - /// Guest physical address of the PEB. - pub(crate) fn peb_address(&self) -> usize { - Self::BASE_ADDRESS + self.peb_offset() - } - - /// Offset of the init data section within the snapshot region. - pub(crate) fn init_data_offset(&self) -> usize { - (self.peb_offset() + size_of::()).next_multiple_of(PAGE_SIZE) - } - /// The code offset is always 0. pub(crate) fn guest_code_offset(&self) -> usize { 0 @@ -533,15 +388,10 @@ impl SandboxMemoryLayout { self.transport_arena } - /// Total size of guest memory in `self`'s memory layout. - fn get_unaligned_memory_size(&self) -> usize { - self.init_data_offset() + self.init_data_size - } - /// Total size of guest memory in `self`'s memory layout, aligned /// to page size boundaries. pub(crate) fn get_memory_size(&self) -> Result { - let total_memory = self.get_unaligned_memory_size(); + let total_memory = self.code_size.next_multiple_of(PAGE_SIZE); // Size should be a multiple of host page size. let remainder = total_memory % page_size::get(); @@ -563,33 +413,31 @@ impl SandboxMemoryLayout { mod tests { use super::*; - // helper func for testing - fn get_expected_memory_size(layout: &SandboxMemoryLayout) -> usize { - let mut expected_size = 0; - // in order of layout - expected_size += layout.code_size; - - // PEB - let peb_and_array = size_of::(); - expected_size += peb_and_array.next_multiple_of(PAGE_SIZE); - - expected_size.next_multiple_of(page_size::get()) - } - + /// Only guest code occupies the initial snapshot data region. #[test] - fn test_get_memory_size() { - let sbox_cfg = SandboxConfiguration::default(); - let sbox_mem_layout = SandboxMemoryLayout::new(sbox_cfg, 4096, 0, None).unwrap(); - assert_eq!( - sbox_mem_layout.get_memory_size().unwrap(), - get_expected_memory_size(&sbox_mem_layout) - ); + fn initial_memory_contains_only_code() { + for code_size in [1, PAGE_SIZE, PAGE_SIZE + 1] { + let layout = + SandboxMemoryLayout::new(SandboxConfiguration::default(), code_size).unwrap(); + let regions = layout.get_memory_regions().unwrap(); + + assert_eq!(regions.len(), 1); + assert_eq!(regions[0].region_type, Code); + assert_eq!( + regions[0].guest_region.len(), + code_size.next_multiple_of(PAGE_SIZE) + ); + assert_eq!( + layout.get_memory_size().unwrap(), + code_size.next_multiple_of(SandboxMemoryLayout::host_page_size()) + ); + } } #[test] fn transport_arena_sits_below_the_reserved_top() { let cfg = SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let arena = layout.get_transport_arena(); let scratch_base = hyperlight_common::layout::scratch_base_gpa(layout.get_scratch_size()); @@ -612,7 +460,7 @@ mod tests { fn resizing_scratch_keeps_the_transport_arena() { let mut cfg = SandboxConfiguration::default(); cfg.set_scratch_size(4 << 20); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); // Sizes in host pages, which can be larger than the guest's. let page = SandboxMemoryLayout::host_page_size(); let half = (layout.get_scratch_size() / 2).next_multiple_of(page); @@ -652,10 +500,10 @@ mod tests { #[test] fn transport_memory_is_part_of_minimum_scratch_size() { let mut cfg = SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let minimum = layout.min_scratch(); cfg.set_scratch_size(minimum); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); assert!(matches!( layout.ensure_page_tables_fit(PAGE_SIZE), @@ -674,7 +522,7 @@ mod tests { cfg.set_g2h_pool_pages(g2h_pages); cfg.set_h2g_pool_pages(h2g_pages); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0, None); + let layout = SandboxMemoryLayout::new(cfg, 4096); assert!(matches!(layout, Err(MemoryRequestTooSmall(_, usize::MAX)))); } } @@ -684,7 +532,7 @@ mod tests { let mut cfg = SandboxConfiguration::default(); cfg.set_scratch_size(SandboxConfiguration::DEFAULT_SCRATCH_SIZE + 1); - let error = SandboxMemoryLayout::new(cfg, 4096, 0, None).unwrap_err(); + let error = SandboxMemoryLayout::new(cfg, 4096).unwrap_err(); assert_eq!( error.to_string(), format!( @@ -697,7 +545,7 @@ mod tests { #[test] fn code_gva_updates_code_region() { let mut layout = - SandboxMemoryLayout::new(SandboxConfiguration::default(), PAGE_SIZE, 0, None).unwrap(); + SandboxMemoryLayout::new(SandboxConfiguration::default(), PAGE_SIZE).unwrap(); let code_gva = 0x100_0000; layout.set_code_gva(code_gva).unwrap(); @@ -717,10 +565,10 @@ mod tests { } #[test] - fn code_gva_rejects_overlap() { + fn code_gva_rejects_overflow() { let mut layout = - SandboxMemoryLayout::new(SandboxConfiguration::default(), PAGE_SIZE, 0, None).unwrap(); - assert!(layout.set_code_gva(layout.peb_address() as u64).is_err()); + SandboxMemoryLayout::new(SandboxConfiguration::default(), PAGE_SIZE).unwrap(); + assert!(layout.set_code_gva(u64::MAX).is_err()); } #[test] @@ -728,7 +576,7 @@ mod tests { let mut cfg = SandboxConfiguration::default(); // scratch_size exceeds 16 GiB limit cfg.set_scratch_size(17 * 1024 * 1024 * 1024); - let layout = SandboxMemoryLayout::new(cfg, 4096, 4096, None); + let layout = SandboxMemoryLayout::new(cfg, 4096); assert!(matches!(layout.unwrap_err(), MemoryRequestTooBig(..))); } @@ -791,15 +639,12 @@ mod tests { let mut cfg = SandboxConfiguration::default(); cfg.set_scratch_size(0x30000); - let layout = SandboxMemoryLayout::new(cfg, 0x1000, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 0x1000).unwrap(); pin_eq!(layout.guest_code_offset(), 0); - pin_eq!(layout.peb_offset(), 0x1000); - pin_eq!(layout.peb_address(), 0x5000); - pin_eq!(layout.init_data_offset(), 0x2000); pin_eq!( layout.get_memory_size().unwrap(), - 0x2000_usize.next_multiple_of(page_size::get()) + 0x1000_usize.next_multiple_of(page_size::get()) ); pin_eq!(layout.get_scratch_size(), 0x30000); @@ -825,15 +670,12 @@ mod tests { // the transport arena at the same address. let mut cfg = SandboxConfiguration::default(); cfg.set_scratch_size(0x40000); - let layout = SandboxMemoryLayout::new(cfg, 0x3000, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 0x3000).unwrap(); pin_eq!(layout.guest_code_offset(), 0); - pin_eq!(layout.peb_offset(), 0x3000); - pin_eq!(layout.peb_address(), 0x7000); - pin_eq!(layout.init_data_offset(), 0x4000); pin_eq!( layout.get_memory_size().unwrap(), - 0x4000_usize.next_multiple_of(page_size::get()) + 0x3000_usize.next_multiple_of(page_size::get()) ); pin_eq!(layout.get_scratch_size(), 0x40000); diff --git a/src/hyperlight_host/src/mem/memory_region.rs b/src/hyperlight_host/src/mem/memory_region.rs index 75636a6c7..b9725eaad 100644 --- a/src/hyperlight_host/src/mem/memory_region.rs +++ b/src/hyperlight_host/src/mem/memory_region.rs @@ -21,8 +21,6 @@ use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE}; #[cfg(target_os = "windows")] use crate::hypervisor::wrappers::HandleWrapper; -pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionFlags::READ; - bitflags! { /// flags representing memory permission for a memory region #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -117,10 +115,6 @@ impl TryFrom for MemoryRegionFlags { pub enum MemoryRegionType { /// The region contains the guest's code Code, - /// The region contains the guest's init data - InitData, - /// The region contains the PEB - Peb, /// The region contains the Heap Heap, /// The region contains the Guard Page diff --git a/src/hyperlight_host/src/mem/mgr.rs b/src/hyperlight_host/src/mem/mgr.rs index 80b2ee797..9f7d0690f 100644 --- a/src/hyperlight_host/src/mem/mgr.rs +++ b/src/hyperlight_host/src/mem/mgr.rs @@ -1603,7 +1603,7 @@ mod tests { #[cfg(target_arch = "x86_64")] fn verify_page_tables(name: &str, config: SandboxConfiguration) { let path = simple_guest_as_pathbuf(); - let snapshot = Snapshot::from_env(GuestBinary::FilePath(path), config) + let snapshot = Snapshot::from_binary(GuestBinary::FilePath(path), config) .unwrap_or_else(|e| panic!("{}: failed to create snapshot: {}", name, e)); // Verify NULL page (0x0) is NOT mapped @@ -1650,7 +1650,7 @@ mod tests { let bin = GuestBinary::FilePath(path); let config = SandboxConfiguration::default(); - let snapshot = Snapshot::from_env(bin, config).unwrap(); + let snapshot = Snapshot::from_binary(bin, config).unwrap(); let mgr = SandboxMemoryManager::from_snapshot(&snapshot).unwrap(); let (mgr, _) = mgr.build().unwrap(); @@ -1701,7 +1701,7 @@ mod tests { fn virtual_reader_manager() -> (SandboxMemoryManager, u64, u64) { let cfg = SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, PAGE_SIZE, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, PAGE_SIZE).unwrap(); let host_page_size = page_size::get(); let base = SandboxMemoryLayout::BASE_ADDRESS as u64; let second_gpa = base + host_page_size as u64; @@ -1834,8 +1834,7 @@ mod tests { #[test] fn guest_physical_memory_view_resolves_scratch_and_dynamic_edges() { let host_page_size = page_size::get(); - let layout = - SandboxMemoryLayout::new(SandboxConfiguration::default(), PAGE_SIZE, 0, None).unwrap(); + let layout = SandboxMemoryLayout::new(SandboxConfiguration::default(), PAGE_SIZE).unwrap(); let scratch_start = hyperlight_common::layout::scratch_base_gpa(layout.get_scratch_size()); let snapshot = Arc::new(flat_memory( &[0u8; PAGE_SIZE], diff --git a/src/hyperlight_host/src/mem/virtq/tests.rs b/src/hyperlight_host/src/mem/virtq/tests.rs index 84c72c0c2..9a847f2ab 100644 --- a/src/hyperlight_host/src/mem/virtq/tests.rs +++ b/src/hyperlight_host/src/mem/virtq/tests.rs @@ -101,7 +101,7 @@ pub(crate) fn memory_layout() -> SandboxMemoryLayout { config.set_g2h_pool_pages(3); config.set_h2g_pool_pages(3); - SandboxMemoryLayout::new(config, 4096, 0, None).unwrap() + SandboxMemoryLayout::new(config, 4096).unwrap() } fn host_scratch() -> HostSharedMemory { diff --git a/src/hyperlight_host/src/sandbox/builder.rs b/src/hyperlight_host/src/sandbox/builder.rs index f00ab6099..def83caa2 100644 --- a/src/hyperlight_host/src/sandbox/builder.rs +++ b/src/hyperlight_host/src/sandbox/builder.rs @@ -10,7 +10,7 @@ use hyperlight_common::func::{ParameterTuple, SupportedReturnType}; use tracing_core::LevelFilter; use crate::func::HostFunction; -use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; +use crate::mem::memory_region::MemoryRegion; use crate::sandbox::SandboxConfiguration; #[cfg(gdb)] use crate::sandbox::config::DebugInfo; @@ -18,8 +18,7 @@ use crate::sandbox::config::DebugInfo; use crate::sandbox::config::GuestMsrError; use crate::sandbox::host_funcs::FunctionEntry; use crate::sandbox::snapshot::Snapshot; -use crate::sandbox::uninitialized::{GuestBlob, GuestEnvironment}; -use crate::{GuestBinary, HostFunctions, Result, Sandbox, UninitializedSandbox, new_error}; +use crate::{GuestBinary, HostFunctions, Result, Sandbox, UninitializedSandbox}; /// What a [`SandboxBuilder`] builds the sandbox from. enum Source { @@ -88,7 +87,6 @@ pub struct SandboxBuilder { source: Source, cfg: SandboxConfiguration, host_funcs: HostFunctions, - init_data: Option<(Vec, MemoryRegionFlags)>, mapped_file_cow: Vec<(std::path::PathBuf, u64)>, mapped_memory_regions: Vec, guest_log_level: Option, @@ -100,7 +98,6 @@ impl SandboxBuilder { source, cfg: SandboxConfiguration::default(), host_funcs: HostFunctions::default(), - init_data: None, mapped_file_cow: Vec::new(), mapped_memory_regions: Vec::new(), guest_log_level: None, @@ -126,17 +123,11 @@ impl SandboxBuilder { } /// Create the sandbox. - /// - /// # Errors - /// - /// When building from a snapshot, returns an error if [`Self::init_data`] - /// is set because the snapshot already contains it. pub fn build(self) -> Result { let Self { source, mut cfg, host_funcs, - init_data, mapped_file_cow, mapped_memory_regions, guest_log_level, @@ -144,15 +135,7 @@ impl SandboxBuilder { let mut sandbox = match source { Source::GuestBinary(guest_binary) => { - let env = GuestEnvironment { - init_data: init_data.as_ref().map(|(data, flags)| GuestBlob { - data, - permissions: *flags, - }), - guest_binary, - }; - - let mut uninitialized_sandbox = UninitializedSandbox::new(env, Some(cfg))?; + let mut uninitialized_sandbox = UninitializedSandbox::new(guest_binary, Some(cfg))?; uninitialized_sandbox.host_funcs = Arc::new(Mutex::new(host_funcs.into_inner())); @@ -167,12 +150,6 @@ impl SandboxBuilder { uninitialized_sandbox.evolve()? } Source::Snapshot(snapshot) => { - if init_data.is_some() { - return Err(new_error!( - "init_data has no effect when building from a snapshot, as the snapshot already contains it" - )); - } - if let Some(log_level) = guest_log_level { cfg.set_max_guest_log_level(log_level); } @@ -198,16 +175,6 @@ impl SandboxBuilder { } impl SandboxBuilder { - /// Sets the sandbox `init_data` into the sandbox's memory when it is built, with `flags` as - /// the guest's permissions on that region. - /// - /// Note: [`Self::build`] errors if this setting is set and the builder's - /// source is a snapshot, as the snapshot already contains the init data. - pub fn init_data(mut self, data: impl Into>, flags: MemoryRegionFlags) -> Self { - self.init_data = Some((data.into(), flags)); - self - } - /// Map the contents of the file at `path` into the guest at `guest_base`, /// copy-on-write. /// @@ -448,7 +415,6 @@ mod tests { use tracing_core::LevelFilter; use super::SandboxBuilder; - use crate::mem::memory_region::MemoryRegionFlags; #[test] fn transport_settings_are_normalized() { @@ -515,18 +481,11 @@ mod tests { } #[test] - fn build_from_snapshot_rejects_init_data_and_accepts_guest_log_level() { + fn build_from_snapshot_accepts_guest_log_level() { let path = simple_guest_as_string().unwrap(); let mut sandbox = SandboxBuilder::from_file(path).build().unwrap(); let snapshot = sandbox.snapshot().unwrap(); - assert!( - SandboxBuilder::from_snapshot(snapshot.clone()) - .init_data([0u8; 8], MemoryRegionFlags::READ) - .build() - .is_err() - ); - assert!( SandboxBuilder::from_snapshot(snapshot) .guest_log_level(LevelFilter::INFO) diff --git a/src/hyperlight_host/src/sandbox/initialized.rs b/src/hyperlight_host/src/sandbox/initialized.rs index db3b7bcec..f0b6c81cd 100644 --- a/src/hyperlight_host/src/sandbox/initialized.rs +++ b/src/hyperlight_host/src/sandbox/initialized.rs @@ -267,7 +267,6 @@ impl Sandbox { ) -> Result { use rand::RngExt; - use crate::mem::ptr::RawPtr; use crate::sandbox::uninitialized_evolve::set_up_hypervisor_partition; // Validate that the provided host functions are a superset of @@ -315,11 +314,10 @@ impl Sandbox { let mut rng = rand::rng(); rng.random::() }; - let peb_addr = RawPtr::from(u64::try_from(hshm.layout.peb_address())?); // For NextAction::Call, initialise is a no-op. Runtime overrides are // delivered through the scratch-memory request below. - vm.initialise(peb_addr, seed, &mut hshm, &host_funcs, max_guest_log_level) + vm.initialise(seed, &mut hshm, &host_funcs, max_guest_log_level) .map_err(crate::hypervisor::hyperlight_vm::HyperlightVmError::Initialize)?; if matches!( snapshot.next_action(), @@ -1406,7 +1404,6 @@ mod tests { use crate::mem::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, SharedMemory as _}; use crate::sandbox::SandboxConfiguration; use crate::sandbox::snapshot::SnapshotOptions; - use crate::sandbox::uninitialized::{GuestBlob, GuestEnvironment}; use crate::{ GuestBinary, HyperlightError, Result, Sandbox, SandboxBuilder, SandboxStatus, UninitializedSandbox, @@ -2053,8 +2050,7 @@ mod tests { // Leave room for runtime allocations and eagerly copied page tables. let scratch_size = { let defaults = SandboxConfiguration::default(); - let layout = - crate::mem::layout::SandboxMemoryLayout::new(defaults, 0, 0, None).unwrap(); + let layout = crate::mem::layout::SandboxMemoryLayout::new(defaults, 0).unwrap(); layout.min_scratch().next_multiple_of(page_size::get()) } + 0x40000; @@ -3455,18 +3451,11 @@ mod tests { #[test] fn snapshot_restore_replaces_rust_guest_with_c_guest() { - let init_data = b"cross-layout-init-data"; - let source_env = GuestEnvironment { - guest_binary: GuestBinary::FilePath(c_simple_guest_as_pathbuf()), - init_data: Some(GuestBlob { - data: init_data, - permissions: MemoryRegionFlags::READ | MemoryRegionFlags::WRITE, - }), - }; - let mut source = UninitializedSandbox::new(source_env, None) - .unwrap() - .evolve() - .unwrap(); + let mut source = + UninitializedSandbox::new(GuestBinary::FilePath(c_simple_guest_as_pathbuf()), None) + .unwrap() + .evolve() + .unwrap(); let mut target = UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_as_pathbuf()), None) .unwrap() @@ -3482,14 +3471,6 @@ mod tests { source.mem_mgr.layout.code_size(), target.mem_mgr.layout.code_size() ); - assert_ne!( - source.mem_mgr.layout.init_data_size(), - target.mem_mgr.layout.init_data_size() - ); - assert_ne!( - source.mem_mgr.layout.init_data_permissions(), - target.mem_mgr.layout.init_data_permissions() - ); let snapshot = source.snapshot().unwrap(); target.restore(snapshot).unwrap(); @@ -5090,7 +5071,7 @@ mod tests { let mut config = SandboxConfiguration::default(); config.guest_msrs(&[KERNEL_GS_BASE]).unwrap(); let snapshot = Arc::new( - Snapshot::from_env(GuestBinary::FilePath(simple_guest_as_pathbuf()), config) + Snapshot::from_binary(GuestBinary::FilePath(simple_guest_as_pathbuf()), config) .unwrap(), ); assert!(snapshot.msrs().is_none()); @@ -6050,7 +6031,7 @@ mod tests { fn round_trip_pre_init_snapshot() { let path = simple_guest_as_pathbuf(); let snap = - Snapshot::from_env(GuestBinary::FilePath(path), SandboxConfiguration::default()) + Snapshot::from_binary(GuestBinary::FilePath(path), SandboxConfiguration::default()) .unwrap(); assert!(snap.virtq().is_none()); let mut sbox = SandboxBuilder::from_snapshot(Arc::new(snap)) @@ -6083,7 +6064,7 @@ mod tests { // emit one guest log message at each level, and count how many // reached the host (guest logs carry target "hyperlight_guest"). let count_guest_logs = |max_level: LevelFilter| -> usize { - let snap = Snapshot::from_env( + let snap = Snapshot::from_binary( GuestBinary::FilePath(simple_guest_as_pathbuf()), SandboxConfiguration::default(), ) @@ -6568,7 +6549,7 @@ mod tests { fn pre_init_snapshot_accepts_arbitrary_host_functions() { let path = simple_guest_as_pathbuf(); let snap = - Snapshot::from_env(GuestBinary::FilePath(path), SandboxConfiguration::default()) + Snapshot::from_binary(GuestBinary::FilePath(path), SandboxConfiguration::default()) .unwrap(); let mut hf = HostFunctions::default(); hf.register_host_function("Unrelated", |a: i32| Ok(a + 1)) diff --git a/src/hyperlight_host/src/sandbox/snapshot/file/config.rs b/src/hyperlight_host/src/sandbox/snapshot/file/config.rs index 1dbade579..61429de7e 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file/config.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file/config.rs @@ -163,7 +163,7 @@ impl CpuVendor { /// Top-level Hyperlight snapshot config JSON. Lives at /// `blobs/sha256/` with media type -/// `application/vnd.hyperlight.snapshot.config.v5+json`. +/// `application/vnd.hyperlight.snapshot.config.v6+json`. /// /// In OCI terms this is the "image config" blob that the manifest's /// `config` descriptor points to. It describes the accompanying @@ -218,7 +218,7 @@ pub(super) struct OciSnapshotConfig { pub(super) metadata: BTreeMap, } -/// Sizes and permissions of the regions inside the snapshot blob, +/// Sizes and addresses of the regions inside the snapshot blob, /// enough for the loader to rebuild a `SandboxMemoryLayout`. #[derive(Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -228,9 +228,6 @@ pub(super) struct MemoryLayout { /// code region is identity mapped. #[serde(default)] pub(super) code_virt_base: u64, - pub(super) init_data_size: usize, - /// Memory region flag bits. `None` means default permissions. - pub(super) init_data_permissions: Option, pub(super) scratch_size: usize, pub(super) g2h_queue_size: usize, pub(super) h2g_queue_size: usize, @@ -487,7 +484,6 @@ impl OciSnapshotConfig { let max_region = SandboxMemoryLayout::MAX_MEMORY_SIZE; for (name, value) in [ ("code_size", self.layout.code_size), - ("init_data_size", self.layout.init_data_size), ("scratch_size", self.layout.scratch_size), ("g2h_buffer_size", self.layout.g2h_buffer_size), ("h2g_buffer_size", self.layout.h2g_buffer_size), @@ -824,8 +820,6 @@ mod tests { layout: MemoryLayout { code_size: 0, code_virt_base: 0, - init_data_size: 0, - init_data_permissions: None, scratch_size: 0, g2h_queue_size: 64, h2g_queue_size: 32, @@ -926,7 +920,7 @@ mod schema_pin { const PINNED_CALL: &str = r#"{ "hyperlight_version": "x.y.z", "arch": "x86_64", - "abi_version": 9, + "abi_version": 10, "hypervisor": "mshv", "cpu_vendor": "intel", "layer_alignment": 4096, @@ -1088,8 +1082,6 @@ mod schema_pin { "layout": { "code_size": 4, "code_virt_base": 0, - "init_data_size": 5, - "init_data_permissions": null, "scratch_size": 8, "g2h_queue_size": 64, "h2g_queue_size": 32, @@ -1122,7 +1114,7 @@ mod schema_pin { const PINNED_CALL: &str = r#"{ "hyperlight_version": "x.y.z", "arch": "aarch64", - "abi_version": 9, + "abi_version": 10, "hypervisor": "mshv", "cpu_vendor": "intel", "layer_alignment": 16384, @@ -1140,8 +1132,6 @@ mod schema_pin { "layout": { "code_size": 4, "code_virt_base": 0, - "init_data_size": 5, - "init_data_permissions": null, "scratch_size": 8, "g2h_queue_size": 64, "h2g_queue_size": 32, diff --git a/src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs b/src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs index 2a98cc3e9..60df07b34 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file/media_types.rs @@ -14,7 +14,9 @@ pub(in crate::sandbox::snapshot) const MT_CONFIG_V4: &str = "application/vnd.hyperlight.snapshot.config.v4+json"; pub(in crate::sandbox::snapshot) const MT_CONFIG_V5: &str = "application/vnd.hyperlight.snapshot.config.v5+json"; -pub(in crate::sandbox::snapshot) const MT_CONFIG_CURRENT: &str = MT_CONFIG_V5; +pub(in crate::sandbox::snapshot) const MT_CONFIG_V6: &str = + "application/vnd.hyperlight.snapshot.config.v6+json"; +pub(in crate::sandbox::snapshot) const MT_CONFIG_CURRENT: &str = MT_CONFIG_V6; pub(in crate::sandbox::snapshot) const MT_SNAPSHOT_V2: &str = "application/vnd.hyperlight.snapshot.memory.v2"; pub(in crate::sandbox::snapshot) const MT_SNAPSHOT_CURRENT: &str = MT_SNAPSHOT_V2; @@ -25,7 +27,7 @@ pub(in crate::sandbox::snapshot) const MT_TRANSPORT_CURRENT: &str = MT_TRANSPORT /// ABI version for the snapshot memory blob. Bumped when the /// host-guest contract for the snapshot bytes changes. See /// docs/snapshot-versioning.md. -pub(in crate::sandbox::snapshot) const SNAPSHOT_ABI_VERSION: u32 = 9; +pub(in crate::sandbox::snapshot) const SNAPSHOT_ABI_VERSION: u32 = 10; /// OCI standard annotation key for a manifest's tag inside an image /// index. Set on the manifest descriptor in `index.json`, not on the diff --git a/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs b/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs index 111ce40bf..ffc731508 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs @@ -30,14 +30,13 @@ use self::media_types::{ }; pub(super) use self::media_types::{ MT_CONFIG_CURRENT, MT_CONFIG_V1, MT_CONFIG_V2, MT_CONFIG_V3, MT_CONFIG_V4, MT_CONFIG_V5, - MT_SNAPSHOT_CURRENT, MT_SNAPSHOT_V2, MT_TRANSPORT_CURRENT, MT_TRANSPORT_V1, + MT_CONFIG_V6, MT_SNAPSHOT_CURRENT, MT_SNAPSHOT_V2, MT_TRANSPORT_CURRENT, MT_TRANSPORT_V1, SNAPSHOT_ABI_VERSION, }; use self::reference::{OciDigest, OciReference, OciTag}; use super::memory::{MAX_SNAPSHOT_LAYERS, host_page_size}; use super::{NextAction, Snapshot, SnapshotBlob, SnapshotMemory}; use crate::mem::layout::SandboxMemoryLayout; -use crate::mem::memory_region::MemoryRegionFlags; use crate::mem::shared_mem::{ReadonlySharedMemory, SharedMemory}; pub(super) const OCI_LAYOUT_VERSION: &str = "1.0.0"; @@ -645,8 +644,6 @@ impl Snapshot { layout: MemoryLayout { code_size: l.code_size(), code_virt_base: l.get_guest_code_gva() as u64, - init_data_size: l.init_data_size(), - init_data_permissions: l.init_data_permissions().map(|f| f.bits()), scratch_size: l.get_scratch_size(), g2h_queue_size: l.get_g2h_queue_size(), h2g_queue_size: l.get_h2g_queue_size(), @@ -789,8 +786,8 @@ impl Snapshot { // Loader dispatch on config media type. let cfg_media = cfg_desc.media_type().to_string(); match cfg_media.as_str() { - MT_CONFIG_V5 => {} - MT_CONFIG_V1 | MT_CONFIG_V2 | MT_CONFIG_V3 | MT_CONFIG_V4 => { + MT_CONFIG_V6 => {} + MT_CONFIG_V1 | MT_CONFIG_V2 | MT_CONFIG_V3 | MT_CONFIG_V4 | MT_CONFIG_V5 => { return Err(crate::new_error!( "snapshot config media type {:?} is incompatible with snapshot ABI {}", cfg_media, @@ -801,7 +798,7 @@ impl Snapshot { return Err(crate::new_error!( "unexpected config media type {:?} (supported: {:?})", other, - MT_CONFIG_V5 + MT_CONFIG_V6 )); } } @@ -952,21 +949,7 @@ impl Snapshot { sbox_cfg.set_h2g_buffer_size(cfg.layout.h2g_buffer_size); sbox_cfg.set_g2h_pool_pages(cfg.layout.g2h_pool_pages); sbox_cfg.set_h2g_pool_pages(cfg.layout.h2g_pool_pages); - let init_data_perms = match cfg.layout.init_data_permissions { - None => None, - Some(bits) => Some(MemoryRegionFlags::from_bits(bits).ok_or_else(|| { - crate::new_error!( - "snapshot init_data_permissions {:#x} contains unknown flag bits", - bits - ) - })?), - }; - let mut layout = SandboxMemoryLayout::new( - sbox_cfg, - cfg.layout.code_size, - cfg.layout.init_data_size, - init_data_perms, - )?; + let mut layout = SandboxMemoryLayout::new(sbox_cfg, cfg.layout.code_size)?; let code_gva = if cfg.layout.code_virt_base == 0 { layout.get_guest_code_gpa() as u64 } else { @@ -1016,9 +999,7 @@ impl Snapshot { } } - // The snapshot GPA span must cover at least the regions the - // layout fields describe (code, PEB, init data), - // otherwise the guest mapping is too short to back them. + // The snapshot GPA span must cover the guest code region. let required_memory_size = layout.get_memory_size()?; if memory.gpa_span_len() < required_memory_size { return Err(crate::new_error!( diff --git a/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs b/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs index 58bfa19aa..00e78721d 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs @@ -29,6 +29,51 @@ fn create_c_test_sandbox() -> Sandbox { SandboxBuilder::from_file(path).build().unwrap() } +/// Startup replies survive release in the source guest, restore, and OCI loading. +#[test] +fn retained_initial_data_survives_snapshots() { + use hyperlight_common::flatbuffer_wrappers::function_types::Bytes; + + let path = simple_guest_as_pathbuf().with_file_name("simpleguest-initial-data"); + let expected: Vec = (0..6 * 1024).map(|i| (i % 251) as u8).collect(); + let data = expected.clone(); + let mut sandbox = SandboxBuilder::from_file(path) + .host_function("InitialData", move || vec![Bytes::copy_from_slice(&data)]) + .build() + .unwrap(); + let actual: Vec = sandbox.call("ReadInitialData", ()).unwrap(); + assert_eq!(actual.concat(), expected); + + let snapshot = sandbox.snapshot().unwrap(); + sandbox.call::<()>("ClearInitialData", ()).unwrap(); + let actual: Vec = sandbox.call("ReadInitialData", ()).unwrap(); + assert!(actual.is_empty()); + + sandbox.restore(snapshot.clone()).unwrap(); + let actual: Vec = sandbox.call("ReadInitialData", ()).unwrap(); + assert_eq!(actual.concat(), expected); + + let dir = tempfile::tempdir().unwrap(); + let tag = OciTag::new("latest").unwrap(); + snapshot.save(dir.path(), &tag).unwrap(); + drop(sandbox); + drop(snapshot); + + let loaded = Arc::new(Snapshot::checked_load(dir.path(), tag).unwrap()); + let mut restored = SandboxBuilder::from_snapshot(loaded.clone()) + .host_function("InitialData", Vec::::new) + .build() + .unwrap(); + let mut fork = SandboxBuilder::from_snapshot(loaded) + .host_function("InitialData", Vec::::new) + .build() + .unwrap(); + + restored.call::<()>("ClearInitialData", ()).unwrap(); + let actual: Vec = fork.call("ReadInitialData", ()).unwrap(); + assert_eq!(actual.concat(), expected); +} + fn random_sequence(sandbox: &mut Sandbox) -> [i32; 4] { std::array::from_fn(|_| sandbox.call("NextRandom", ()).unwrap()) } @@ -139,7 +184,7 @@ fn from_snapshot_already_initialized_in_memory() { #[test] fn from_snapshot_in_memory_pre_init() { - let snap = Snapshot::from_env( + let snap = Snapshot::from_binary( GuestBinary::FilePath(simple_guest_as_pathbuf()), crate::sandbox::SandboxConfiguration::default(), ) @@ -282,16 +327,16 @@ fn layered_writer_preserves_blobs_and_transport_geometry() { let layers = manifest["layers"].as_array().unwrap(); assert_eq!( manifest["config"]["mediaType"], - "application/vnd.hyperlight.snapshot.config.v5+json" + "application/vnd.hyperlight.snapshot.config.v6+json" ); // `artifactType` mirrors `config.mediaType` so registries that surface // the distribution-spec referrers API report a useful type, and tooling // that falls back to `config.mediaType` sees the same value. assert_eq!( manifest["artifactType"], - "application/vnd.hyperlight.snapshot.config.v5+json" + "application/vnd.hyperlight.snapshot.config.v6+json" ); - assert_eq!(config["abi_version"], 9); + assert_eq!(config["abi_version"], 10); let blobs = snapshot.snapshot_memory().blobs().collect::>(); assert_eq!(layers.len(), blobs.len() + 1); for (index, blob) in blobs.iter().enumerate() { @@ -657,7 +702,7 @@ fn disk_snapshot_non_superset_guest_msrs_rejected() { #[test] fn pre_init_snapshot_cannot_be_persisted() { - let snap = Snapshot::from_env( + let snap = Snapshot::from_binary( GuestBinary::FilePath(simple_guest_as_pathbuf()), crate::sandbox::SandboxConfiguration::default(), ) @@ -2389,6 +2434,7 @@ fn legacy_config_versions_rejected() { "application/vnd.hyperlight.snapshot.config.v2+json", "application/vnd.hyperlight.snapshot.config.v3+json", "application/vnd.hyperlight.snapshot.config.v4+json", + "application/vnd.hyperlight.snapshot.config.v5+json", ] { let (_dir, path) = save_for_mutation(); rewrite_manifest(&path, |m| { @@ -2398,7 +2444,7 @@ fn legacy_config_versions_rejected() { &path, OciTag::new("latest").unwrap(), )); - assert_err_contains(err, "incompatible with snapshot ABI 9"); + assert_err_contains(err, "incompatible with snapshot ABI 10"); } } @@ -2557,17 +2603,16 @@ fn malformed_config_json_rejected() { } #[test] -fn bad_init_data_permissions_rejected() { +fn legacy_init_data_fields_rejected() { let (_dir, path) = save_for_mutation(); rewrite_config(&path, |cfg| { - // 1u32 << 31 is well outside the defined READ|WRITE|EXECUTE bits. - cfg["layout"]["init_data_permissions"] = Value::from(0x8000_0000u32); + cfg["layout"]["init_data_size"] = Value::from(4096); }); let err = unwrap_err_snapshot(Snapshot::checked_load( &path, OciTag::new("latest").unwrap(), )); - assert_err_contains(err, "init_data_permissions"); + assert_err_contains(err, "unknown field `init_data_size`"); } #[test] @@ -4000,34 +4045,3 @@ fn from_snapshot_honors_guest_core_dump_disabled() { entries.iter().map(|e| e.path()).collect::>() ); } - -/// Non-default `init_data_permissions` survive an OCI round-trip -/// byte-for-byte. The default code path uses `READ`, so this pins -/// `READ | WRITE` instead. A regression in the permission -/// serialisation would silently downgrade or upgrade access to the -/// init_data region. -#[test] -fn round_trip_preserves_non_default_init_data_permissions() { - use crate::mem::memory_region::MemoryRegionFlags; - - let path = simple_guest_as_pathbuf(); - let data: &[u8] = b"perm-pinned-init-data"; - let mut sbox = SandboxBuilder::from_file(path) - .init_data(data, MemoryRegionFlags::READ | MemoryRegionFlags::WRITE) - .build() - .unwrap(); - let snap = sbox.snapshot().unwrap(); - let expected = snap.layout().init_data_permissions(); - assert_eq!( - expected, - Some(MemoryRegionFlags::READ | MemoryRegionFlags::WRITE), - "fixture must produce non-default init_data_permissions", - ); - - let dir = tempfile::tempdir().unwrap(); - let oci_dir = dir.path().join("layout"); - snap.save(&oci_dir, &OciTag::new("latest").unwrap()) - .unwrap(); - let loaded = Snapshot::checked_load(&oci_dir, OciTag::new("latest").unwrap()).unwrap(); - assert_eq!(loaded.layout().init_data_permissions(), expected); -} diff --git a/src/hyperlight_host/src/sandbox/snapshot/mod.rs b/src/hyperlight_host/src/sandbox/snapshot/mod.rs index aaed10e2d..bb24e0800 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/mod.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/mod.rs @@ -36,7 +36,7 @@ use crate::mem::mgr::{BackingSource, GuestBacking, GuestPageTableBuffer, GuestPh use crate::mem::shared_mem::ExclusiveSharedMemory; use crate::mem::virtq::VirtqSnapshot; use crate::sandbox::SandboxConfiguration; -use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment}; +use crate::sandbox::uninitialized::GuestBinary; const PTE_SIZE: usize = size_of::(); @@ -564,16 +564,9 @@ fn map_specials(pt_buf: &GuestPageTableBuffer, scratch_size: usize) { } impl Snapshot { - /// Create a new snapshot from the guest binary identified by `env`. With the configuration - /// specified in `cfg`. - pub(crate) fn from_env<'b>( - env: impl Into>, - cfg: SandboxConfiguration, - ) -> Result { - let env = env.into(); - let mut bin = env.guest_binary; + /// Create a snapshot from a guest binary with the configuration in `cfg`. + pub(crate) fn from_binary(mut bin: GuestBinary, cfg: SandboxConfiguration) -> Result { bin.canonicalize()?; - let blob = env.init_data; let exe_info = match bin { GuestBinary::FilePath(bin_path) => ExeInfo::from_file(&bin_path)?, @@ -591,15 +584,7 @@ impl Snapshot { }); } - let guest_blob_size = blob.as_ref().map(|b| b.data.len()).unwrap_or(0); - let guest_blob_mem_flags = blob.as_ref().map(|b| b.permissions); - - let mut layout = crate::mem::layout::SandboxMemoryLayout::new( - cfg, - exe_info.loaded_size(), - guest_blob_size, - guest_blob_mem_flags, - )?; + let mut layout = crate::mem::layout::SandboxMemoryLayout::new(cfg, exe_info.loaded_size())?; let load_addr = layout.get_guest_code_gpa() as u64; let base_va = exe_info.base_va(); @@ -675,10 +660,6 @@ impl Snapshot { &mut data[layout.guest_code_offset()..], )?; - layout.write_peb(data)?; - - blob.map(|x| layout.write_init_data(data, x.data)) - .transpose()?; memory.copy_from_slice(&pt_bytes, data_len)?; let memory = Arc::new(SnapshotMemory::from_flat( memory.freeze(data_len)?, @@ -1421,7 +1402,7 @@ mod tests { fn make_pt_mgr(data_gpa: u64, gvas: &[u64]) -> (SandboxMemoryManager, u64) { let cfg = crate::sandbox::SandboxConfiguration::default(); let scratch_mem = ExclusiveSharedMemory::new(cfg.get_scratch_size()).unwrap(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0x3000, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let pt_base = layout.get_pt_base_gpa(); let memory = make_pt_memory(&vec![0u8; page_size::get()], pt_base, data_gpa, gvas); layout @@ -1440,7 +1421,7 @@ mod tests { #[test] fn capture_and_restore_preserve_multiple_page_table_roots() { let cfg = crate::sandbox::SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0x3000, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let pt_base = layout.get_pt_base_gpa(); let base = SandboxMemoryLayout::BASE_ADDRESS as u64; let pt_buf = GuestPageTableBuffer::new(pt_base as usize); @@ -1824,7 +1805,7 @@ mod tests { fn capture_reuses_shared_pages_and_materializes_private_pages() { let host_page_size = page_size::get(); let cfg = crate::sandbox::SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0x3000, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let pt_base = layout.get_pt_base_gpa(); let scratch_offset = layout.get_scratch_size() / 2; let scratch_gpa = hyperlight_common::layout::scratch_base_gpa(layout.get_scratch_size()) @@ -2005,7 +1986,7 @@ mod tests { fn capture_rejects_leaf_into_page_table_tail() { let host_page_size = page_size::get(); let cfg = crate::sandbox::SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0x3000, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let pt_base = layout.get_pt_base_gpa(); let base = SandboxMemoryLayout::BASE_ADDRESS as u64; let pt_buf = GuestPageTableBuffer::new(pt_base as usize); @@ -2080,7 +2061,7 @@ mod tests { fn capture_rejects_delta_exceeding_layer_limit() { let host_page_size = page_size::get(); let cfg = crate::sandbox::SandboxConfiguration::default(); - let layout = SandboxMemoryLayout::new(cfg, 4096, 0x3000, None).unwrap(); + let layout = SandboxMemoryLayout::new(cfg, 4096).unwrap(); let pt_base = layout.get_pt_base_gpa(); let scratch_offset = layout.get_scratch_size() / 2; let scratch_gpa = hyperlight_common::layout::scratch_base_gpa(layout.get_scratch_size()) diff --git a/src/hyperlight_host/src/sandbox/snapshot/tripwires.rs b/src/hyperlight_host/src/sandbox/snapshot/tripwires.rs index f34933d21..e64061c7d 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/tripwires.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/tripwires.rs @@ -5,9 +5,8 @@ //! //! Each assertion pins one piece of the contract that snapshots //! depend on: the manifest media types, the OCI Image Layout version, -//! the `HyperlightPEB` size, the `OutBAction` and `VmAction` -//! port numbers, and `BASE_ADDRESS`. A change to any of these breaks -//! loading of older snapshots. +//! the `OutBAction` and `VmAction` port numbers, and `BASE_ADDRESS`. +//! A change to any of these breaks loading of older snapshots. //! //! When an assertion fires, see `docs/snapshot-versioning.md`. @@ -16,8 +15,8 @@ use super::file::{ SNAPSHOT_ABI_VERSION, }; -const EXPECTED_ABI_VERSION: u32 = 9; -const EXPECTED_MT_CONFIG: &str = "application/vnd.hyperlight.snapshot.config.v5+json"; +const EXPECTED_ABI_VERSION: u32 = 10; +const EXPECTED_MT_CONFIG: &str = "application/vnd.hyperlight.snapshot.config.v6+json"; const EXPECTED_MT_SNAPSHOT: &str = "application/vnd.hyperlight.snapshot.memory.v2"; const EXPECTED_MT_TRANSPORT: &str = "application/vnd.hyperlight.snapshot.transport.v1"; const EXPECTED_OCI_LAYOUT_VERSION: &str = "1.0.0"; @@ -43,18 +42,6 @@ const _: () = { abi_assert!(str_eq(OCI_LAYOUT_VERSION, EXPECTED_OCI_LAYOUT_VERSION)); }; -const _: () = { - use hyperlight_common::mem::HyperlightPEB; - use hyperlight_common::vmem::PAGE_SIZE; - // The loading host derives `init_data_offset`, and every - // offset after it, from the PEB's page-rounded size. Existing - // snapshots place the PEB in a single page, so the only thing that - // must hold is that the PEB keeps fitting in one page. Field layout - // is not pinned: only the captured guest reads the fields, and it - // travels inside the snapshot, so it stays self-consistent. - abi_assert!(std::mem::size_of::() <= PAGE_SIZE); -}; - const _: () = { use hyperlight_common::outb::OutBAction; abi_assert!(OutBAction::Abort as u16 == 102); diff --git a/src/hyperlight_host/src/sandbox/uninitialized.rs b/src/hyperlight_host/src/sandbox/uninitialized.rs index 948a70210..671cf6640 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized.rs @@ -16,7 +16,6 @@ use crate::func::host_functions::{HostFunction, register_host_function}; use crate::func::{ParameterTuple, SupportedReturnType}; #[cfg(feature = "build-metadata")] use crate::log_build_details; -use crate::mem::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegionFlags}; use crate::mem::mgr::SandboxMemoryManager; use crate::mem::shared_mem::ExclusiveSharedMemory; use crate::sandbox::SandboxConfiguration; @@ -69,9 +68,7 @@ impl GuestBinary { /// /// For [`GuestBinary::FilePath`], this resolves the path to its canonical /// form. For [`GuestBinary::Buffer`], this method is a no-op. - /// TODO: Maybe we should make the GuestEnvironment or - /// GuestBinary constructors crate-private and turn this - /// into an invariant on one of those types. + /// TODO: Make canonical paths an invariant of `GuestBinary`. pub fn canonicalize(&mut self) -> Result<()> { if let GuestBinary::FilePath(p) = self { *p = p @@ -82,58 +79,6 @@ impl GuestBinary { } } -/// A `GuestBlob` containing data and the permissions for its use. -#[derive(Debug)] -pub struct GuestBlob<'a> { - /// The data contained in the blob. - pub data: &'a [u8], - /// The permissions for the blob in memory. - /// By default, it's READ - pub permissions: MemoryRegionFlags, -} - -impl<'a> From<&'a [u8]> for GuestBlob<'a> { - fn from(data: &'a [u8]) -> Self { - GuestBlob { - data, - permissions: DEFAULT_GUEST_BLOB_MEM_FLAGS, - } - } -} - -/// Container for a guest binary and optional initialization data. -/// -/// This struct combines a guest binary (either from a file or memory buffer) with -/// optional data that will be available to the guest during execution. -/// -/// The guest binary is owned. `'b` is the lifetime of the borrowed init data. -#[derive(Debug)] -pub struct GuestEnvironment<'b> { - /// The guest binary, which can be a file path or a buffer. - pub guest_binary: GuestBinary, - /// An optional guest blob, which can be used to provide additional data to the guest. - pub init_data: Option>, -} - -impl<'b> GuestEnvironment<'b> { - /// Creates a new `GuestEnvironment` with the given guest binary and an optional guest blob. - pub fn new(guest_binary: GuestBinary, init_data: Option<&'b [u8]>) -> Self { - GuestEnvironment { - guest_binary, - init_data: init_data.map(GuestBlob::from), - } - } -} - -impl From for GuestEnvironment<'_> { - fn from(guest_binary: GuestBinary) -> Self { - GuestEnvironment { - guest_binary, - init_data: None, - } - } -} - impl UninitializedSandbox { // Creates a new uninitialized sandbox from a pre-built snapshot. // Note that since memory configuration is part of the snapshot the only configuration @@ -170,7 +115,7 @@ impl UninitializedSandbox { Ok(sandbox) } - /// Creates a new uninitialized sandbox for the given guest environment. + /// Creates a new uninitialized sandbox for the given guest binary. /// /// The guest binary can be provided as either a file path or memory buffer. /// An optional configuration can customize memory sizes and sandbox settings. @@ -178,15 +123,12 @@ impl UninitializedSandbox { /// before calling [`evolve`](Self::evolve) to complete initialization and create the VM. #[instrument( err(Debug), - skip(env), + skip(guest_binary), parent = Span::current() )] - pub fn new<'b>( - env: impl Into>, - cfg: Option, - ) -> Result { + pub fn new(guest_binary: GuestBinary, cfg: Option) -> Result { let cfg = cfg.unwrap_or_default(); - let snapshot = Snapshot::from_env(env, cfg)?; + let snapshot = Snapshot::from_binary(guest_binary, cfg)?; Self::from_snapshot(Arc::new(snapshot), Some(cfg)) } @@ -344,7 +286,7 @@ mod tests { use hyperlight_testing::simple_guest_as_pathbuf; use crate::sandbox::SandboxConfiguration; - use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment}; + use crate::sandbox::uninitialized::GuestBinary; use crate::{Result, Sandbox, UninitializedSandbox, new_error}; #[cfg(target_os = "linux")] @@ -364,23 +306,6 @@ mod tests { assert!(sandbox.is_ok()); } - #[test] - fn test_load_extra_blob() { - let binary_path = simple_guest_as_pathbuf(); - let buffer = [0xde, 0xad, 0xbe, 0xef]; - let guest_env = - GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&buffer)); - - let uninitialized_sandbox = UninitializedSandbox::new(guest_env, None).unwrap(); - let mut sandbox = uninitialized_sandbox.evolve().unwrap(); - - let res = sandbox - .call::>("ReadFromUserMemory", (4u64, buffer.to_vec())) - .expect("Failed to call ReadFromUserMemory"); - - assert_eq!(res, buffer.to_vec()); - } - #[test] fn test_new_sandbox() { // Guest Binary exists at path @@ -1064,10 +989,10 @@ mod tests { // Test 1: Create snapshot with default config, create multiple sandboxes from it { - let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None); + let env = GuestBinary::FilePath(binary_path.clone()); let snapshot = Arc::new( - Snapshot::from_env(env, Default::default()) + Snapshot::from_binary(env, Default::default()) .expect("Failed to create snapshot with default config"), ); @@ -1089,10 +1014,10 @@ mod tests { let mut cfg = SandboxConfiguration::default(); cfg.set_scratch_size(SandboxConfiguration::DEFAULT_SCRATCH_SIZE + 64 * 1024); - let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None); + let env = GuestBinary::FilePath(binary_path.clone()); let snapshot = Arc::new( - Snapshot::from_env(env, cfg) + Snapshot::from_binary(env, cfg) .expect("Failed to create snapshot with custom stack size"), ); @@ -1107,10 +1032,10 @@ mod tests { let mut cfg = SandboxConfiguration::default(); cfg.set_scratch_size(512 * 1024); // 512KB scratch - let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None); + let env = GuestBinary::FilePath(binary_path.clone()); let snapshot = Arc::new( - Snapshot::from_env(env, cfg) + Snapshot::from_binary(env, cfg) .expect("Failed to create snapshot with all custom settings"), ); @@ -1132,7 +1057,7 @@ mod tests { let binary_bytes = fs::read(&binary_path).expect("Failed to read binary file"); let snapshot = Arc::new( - Snapshot::from_env(GuestBinary::Buffer(binary_bytes), Default::default()) + Snapshot::from_binary(GuestBinary::Buffer(binary_bytes), Default::default()) .expect("Failed to create snapshot from buffer"), ); @@ -1144,10 +1069,10 @@ mod tests { // Test 6: Register host functions on sandboxes created from snapshot { - let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None); + let env = GuestBinary::FilePath(binary_path.clone()); let snapshot = Arc::new( - Snapshot::from_env(env, Default::default()).expect("Failed to create snapshot"), + Snapshot::from_binary(env, Default::default()).expect("Failed to create snapshot"), ); let mut sandbox = UninitializedSandbox::from_snapshot(snapshot, None) @@ -1176,28 +1101,11 @@ mod tests { assert_eq!(result, ReturnValue::Int(30)); } - // Test 7: Create snapshot with init data (guest blob) - { - let init_data = [0xCA, 0xFE, 0xBA, 0xBE]; - let guest_env = - GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&init_data)); - - let snapshot = Arc::new( - Snapshot::from_env(guest_env, Default::default()) - .expect("Failed to create snapshot with init data"), - ); - - let sandbox = UninitializedSandbox::from_snapshot(snapshot, None) - .expect("Failed to create sandbox from snapshot with init data"); - - let _evolved = sandbox.evolve().expect("Failed to evolve sandbox"); - } - // Test 8: Create snapshot from existing sandbox { - let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None); + let env = GuestBinary::FilePath(binary_path.clone()); let orig_snapshot = Arc::new( - Snapshot::from_env(env, Default::default()) + Snapshot::from_binary(env, Default::default()) .expect("Failed to create snapshot with default config"), ); let orig_sandbox = UninitializedSandbox::from_snapshot(orig_snapshot, None) diff --git a/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs b/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs index 8668b8f83..99170c427 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs @@ -8,7 +8,6 @@ use super::snapshot::NextAction; use crate::hypervisor::hyperlight_vm::{HyperlightVm, HyperlightVmError}; use crate::mem::exe::LoadInfo; use crate::mem::mgr::SandboxMemoryManager; -use crate::mem::ptr::RawPtr; use crate::mem::shared_mem::GuestSharedMemory; #[cfg(gdb)] use crate::sandbox::config::DebugInfo; @@ -24,7 +23,7 @@ pub(super) fn initialize_sandbox(u_sbox: UninitializedSandbox) -> Result Result() }; - let peb_addr = { - let peb_u64 = u64::try_from(hshm.layout.peb_address())?; - RawPtr::from(peb_u64) - }; #[cfg(target_os = "linux")] setup_signal_handlers(&u_sbox.config)?; @@ -75,14 +70,8 @@ pub(super) fn initialize_sandbox(u_sbox: UninitializedSandbox) -> Result i32 { - if a != 0x230000 || b != 1234567890 || c != 4096 { +pub extern "C" fn entrypoint(seed: u64, page_size: u64) -> i32 { + if seed != 1234567890 || page_size != 4096 { mmio_read(); } halt(); diff --git a/src/tests/rust_guests/simpleguest/src/bin/simpleguest-initial-data.rs b/src/tests/rust_guests/simpleguest/src/bin/simpleguest-initial-data.rs new file mode 100644 index 000000000..96eff42a7 --- /dev/null +++ b/src/tests/rust_guests/simpleguest/src/bin/simpleguest-initial-data.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 The Hyperlight Authors. + +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::vec::Vec; + +use hyperlight_common::flatbuffer_wrappers::function_types::Bytes; +use hyperlight_guest::error::Result; +use hyperlight_guest_bin::{guest_function, host_function, main}; + +static mut INITIAL_DATA: Vec = Vec::new(); + +/// Fetch application data during guest startup. +#[host_function("InitialData")] +fn initial_data() -> Result>; + +/// Keep the transport-backed reply for later calls and snapshots. +#[main] +fn main() { + let data = initial_data().expect("initial data host call failed"); + + // SAFETY: The guest is single threaded. + unsafe { INITIAL_DATA = data }; +} + +/// Read startup data without releasing its transport owners. +#[guest_function("ReadInitialData")] +fn read_initial_data() -> Vec { + // SAFETY: The guest is single threaded. + #[allow(static_mut_refs)] + unsafe { + INITIAL_DATA.clone() + } +} + +/// Release the retained transport owners. +#[guest_function("ClearInitialData")] +fn clear_initial_data() { + // SAFETY: The guest is single threaded. + #[allow(static_mut_refs)] + unsafe { + INITIAL_DATA.clear(); + } +} diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index 06665dfa6..99da90ed0 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -37,15 +37,13 @@ use hyperlight_guest::exit::{abort_with_code, abort_with_code_and_message}; use hyperlight_guest_bin::exception::arch::{Context, ExceptionInfo}; use hyperlight_guest_bin::guest_function::definition::{GuestFunc, GuestFunctionDefinition}; use hyperlight_guest_bin::guest_function::register::register_function; -use hyperlight_guest_bin::host_comm::{ - call_host_function, print_output_with_host_print, read_n_bytes_from_user_memory, -}; +use hyperlight_guest_bin::host_comm::{call_host_function, print_output_with_host_print}; use hyperlight_guest_bin::memory::malloc; use hyperlight_guest_bin::{guest_function, guest_logger, host_function}; // `log` is intentionally kept here: the LogMessage guest function exercises the // guest-side `log` crate path to verify that guests using `log` are still supported. use log::LevelFilter; -use tracing::{Span, error, instrument}; +use tracing::{Span, instrument}; extern crate hyperlight_guest; @@ -1119,22 +1117,6 @@ fn large_parameters(v: Vec, s: String) { black_box((v, s)); } -#[guest_function("ReadFromUserMemory")] -fn read_from_user_memory(num: u64, expected: Vec) -> Result> { - let bytes = read_n_bytes_from_user_memory(num).expect("Failed to read from user memory"); - - // verify that the user memory contains the expected data - if bytes != expected { - error!("User memory does not contain the expected data"); - return Err(HyperlightGuestError::new( - ErrorCode::GuestError, - "User memory does not contain the expected data".to_string(), - )); - } - - Ok(bytes) -} - #[guest_function("MapZeroInitPage")] fn map_zero_init_page(base: u64) -> bool { // SAFETY: The test supplies an unused, page-aligned GVA.