diff --git a/src/hyperlight_common/src/mem.rs b/src/hyperlight_common/src/mem.rs index c6b3c48ad..4e5448ae9 100644 --- a/src/hyperlight_common/src/mem.rs +++ b/src/hyperlight_common/src/mem.rs @@ -46,6 +46,7 @@ pub struct HyperlightPEB { pub code_ptr: u64, pub input_stack: GuestMemoryRegion, pub output_stack: GuestMemoryRegion, + pub init_data: GuestMemoryRegion, pub guest_heap: GuestMemoryRegion, pub guest_stack: GuestStack, pub host_function_definitions: GuestMemoryRegion, diff --git a/src/hyperlight_guest/src/guest_handle/host_comm.rs b/src/hyperlight_guest/src/guest_handle/host_comm.rs index 20c776c72..97e90f3a6 100644 --- a/src/hyperlight_guest/src/guest_handle/host_comm.rs +++ b/src/hyperlight_guest/src/guest_handle/host_comm.rs @@ -34,6 +34,29 @@ use crate::error::{HyperlightGuestError, Result}; use crate::exit::out32; impl GuestHandle { + /// Get user memory region as bytes. + pub fn read_n_bytes_from_user_memory(&self, num: u64) -> Result> { + let peb_ptr = self.peb().unwrap(); + let user_memory_region_ptr = unsafe { (*peb_ptr).init_data.ptr as *mut u8 }; + let user_memory_region_size = unsafe { (*peb_ptr).init_data.size }; + + if num > user_memory_region_size { + return Err(HyperlightGuestError::new( + ErrorCode::GuestError, + format!( + "Requested {} bytes from user memory, but only {} bytes are available", + num, user_memory_region_size + ), + )); + } else { + let user_memory_region_slice = + unsafe { core::slice::from_raw_parts(user_memory_region_ptr, num as usize) }; + let user_memory_region_bytes = user_memory_region_slice.to_vec(); + + Ok(user_memory_region_bytes) + } + } + /// Get a return value from a host function call. /// This usually requires a host function to be called first using /// `call_host_function_internal`. diff --git a/src/hyperlight_guest_bin/src/host_comm.rs b/src/hyperlight_guest_bin/src/host_comm.rs index b3ea0302c..e21097661 100644 --- a/src/hyperlight_guest_bin/src/host_comm.rs +++ b/src/hyperlight_guest_bin/src/host_comm.rs @@ -65,6 +65,11 @@ pub fn get_host_function_details() -> HostFunctionDetails { handle.get_host_function_details() } +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. /// /// This function requires memory to be setup to be used. In particular, the diff --git a/src/hyperlight_host/src/mem/layout.rs b/src/hyperlight_host/src/mem/layout.rs index 70850d39f..345aa3401 100644 --- a/src/hyperlight_host/src/mem/layout.rs +++ b/src/hyperlight_host/src/mem/layout.rs @@ -21,15 +21,20 @@ use rand::{RngCore, rng}; use tracing::{Span, instrument}; use super::memory_region::MemoryRegionType::{ - Code, GuardPage, Heap, HostFunctionDefinitions, InputData, OutputData, PageTables, Peb, Stack, + Code, GuardPage, Heap, HostFunctionDefinitions, InitData, InputData, OutputData, PageTables, + Peb, Stack, +}; +use super::memory_region::{ + DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryRegionFlags, MemoryRegionVecBuilder, }; -use super::memory_region::{MemoryRegion, MemoryRegionFlags, MemoryRegionVecBuilder}; use super::mgr::AMOUNT_OF_MEMORY_PER_PT; use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, SharedMemory}; use crate::error::HyperlightError::{GuestOffsetIsInvalid, MemoryRequestTooBig}; use crate::sandbox::SandboxConfiguration; use crate::{Result, new_error}; +// +-------------------------------------------+ +// | Init Data | (GuestBlob size) // +-------------------------------------------+ // | Guest (User) Stack | // +-------------------------------------------+ @@ -56,6 +61,8 @@ use crate::{Result, new_error}; // | PML4 | // +-------------------------------------------+ 0x0_000 +/// - `InitData` - some extra data that can be loaded onto the sandbox during +/// initialization. /// /// - `HostDefinitions` - the length of this is the `HostFunctionDefinitionSize` /// field from `SandboxConfiguration` @@ -82,6 +89,7 @@ pub(crate) struct SandboxMemoryLayout { pub(super) stack_size: usize, /// The heap size of this sandbox. pub(super) heap_size: usize, + init_data_size: usize, /// The following fields are offsets to the actual PEB struct fields. /// They are used when writing the PEB struct itself @@ -92,6 +100,7 @@ pub(crate) struct SandboxMemoryLayout { pub(super) peb_host_function_definitions_offset: usize, peb_input_data_offset: usize, peb_output_data_offset: usize, + peb_init_data_offset: usize, peb_heap_data_offset: usize, peb_guest_stack_data_offset: usize, @@ -103,6 +112,7 @@ pub(crate) struct SandboxMemoryLayout { guest_heap_buffer_offset: usize, guard_page_offset: usize, guest_user_stack_buffer_offset: usize, // the lowest address of the user stack + init_data_offset: usize, // other pub(crate) peb_address: usize, @@ -111,6 +121,7 @@ pub(crate) struct SandboxMemoryLayout { total_page_table_size: usize, // The offset in the sandbox memory where the code starts guest_code_offset: usize, + pub(crate) init_data_permissions: Option, } impl Debug for SandboxMemoryLayout { @@ -122,6 +133,10 @@ impl Debug for SandboxMemoryLayout { ) .field("Stack Size", &format_args!("{:#x}", self.stack_size)) .field("Heap Size", &format_args!("{:#x}", self.heap_size)) + .field( + "Init Data Size", + &format_args!("{:#x}", self.init_data_size), + ) .field("PEB Address", &format_args!("{:#x}", self.peb_address)) .field("PEB Offset", &format_args!("{:#x}", self.peb_offset)) .field("Code Size", &format_args!("{:#x}", self.code_size)) @@ -149,6 +164,10 @@ impl Debug for SandboxMemoryLayout { "Output Data Offset", &format_args!("{:#x}", self.peb_output_data_offset), ) + .field( + "Init Data Offset", + &format_args!("{:#x}", self.peb_init_data_offset), + ) .field( "Guest Heap Offset", &format_args!("{:#x}", self.peb_heap_data_offset), @@ -181,6 +200,10 @@ impl Debug for SandboxMemoryLayout { "Guest User Stack Buffer Offset", &format_args!("{:#x}", self.guest_user_stack_buffer_offset), ) + .field( + "Init Data Offset", + &format_args!("{:#x}", self.init_data_offset), + ) .field( "Page Table Size", &format_args!("{:#x}", self.total_page_table_size), @@ -231,6 +254,8 @@ impl SandboxMemoryLayout { code_size: usize, stack_size: usize, heap_size: usize, + init_data_size: usize, + init_data_permissions: Option, ) -> Result { let total_page_table_size = Self::get_total_page_table_size(cfg, code_size, stack_size, heap_size); @@ -244,6 +269,7 @@ impl SandboxMemoryLayout { let peb_code_pointer_offset = peb_offset + offset_of!(HyperlightPEB, code_ptr); let peb_input_data_offset = peb_offset + offset_of!(HyperlightPEB, input_stack); let peb_output_data_offset = peb_offset + offset_of!(HyperlightPEB, output_stack); + let peb_init_data_offset = peb_offset + offset_of!(HyperlightPEB, init_data); let peb_heap_data_offset = peb_offset + offset_of!(HyperlightPEB, guest_heap); let peb_guest_stack_data_offset = peb_offset + offset_of!(HyperlightPEB, guest_stack); let peb_host_function_definitions_offset = @@ -275,6 +301,7 @@ impl SandboxMemoryLayout { let guest_user_stack_buffer_offset = guard_page_offset + PAGE_SIZE_USIZE; // round up stack size to page size. This is needed for MemoryRegion let stack_size_rounded = round_up_to(stack_size, PAGE_SIZE_USIZE); + let init_data_offset = guest_user_stack_buffer_offset + stack_size_rounded; Ok(Self { peb_offset, @@ -286,6 +313,7 @@ impl SandboxMemoryLayout { peb_host_function_definitions_offset, peb_input_data_offset, peb_output_data_offset, + peb_init_data_offset, peb_heap_data_offset, peb_guest_stack_data_offset, sandbox_memory_config: cfg, @@ -299,6 +327,9 @@ impl SandboxMemoryLayout { guard_page_offset, total_page_table_size, guest_code_offset, + init_data_offset, + init_data_size, + init_data_permissions, }) } @@ -325,6 +356,13 @@ impl SandboxMemoryLayout { self.peb_host_function_definitions_offset + size_of::() } + /// Get the offset in guest memory to the init data size + #[instrument(skip_all, parent = Span::current(), level= "Trace")] + pub(super) fn get_init_data_size_offset(&self) -> usize { + // The init data size is the first field in the `GuestMemoryRegion` struct + self.peb_init_data_offset + } + /// Get the offset in guest memory to the minimum guest stack address. #[instrument(skip_all, parent = Span::current(), level= "Trace")] fn get_min_guest_stack_address_offset(&self) -> usize { @@ -361,6 +399,14 @@ impl SandboxMemoryLayout { self.get_output_data_size_offset() + size_of::() } + /// Get the offset in guest memory to the init data pointer. + #[instrument(skip_all, parent = Span::current(), level= "Trace")] + pub(super) fn get_init_data_pointer_offset(&self) -> usize { + // The init data pointer is immediately after the init data size field, + // which is a `u64`. + self.get_init_data_size_offset() + size_of::() + } + /// Get the offset in guest memory to the start of output data. /// /// This function exists to accommodate the macro that generates C API @@ -444,7 +490,7 @@ impl SandboxMemoryLayout { /// layout. #[instrument(skip_all, parent = Span::current(), level= "Trace")] fn get_unaligned_memory_size(&self) -> usize { - self.get_top_of_user_stack_offset() + self.stack_size + self.init_data_offset + self.init_data_size } /// get the code offset @@ -682,12 +728,31 @@ impl SandboxMemoryLayout { } // stack - let final_offset = builder.push_page_aligned( + let init_data_offset = builder.push_page_aligned( self.get_guest_stack_size(), MemoryRegionFlags::READ | MemoryRegionFlags::WRITE, Stack, ); + 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 + )); + } + + let final_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 expected_final_offset = TryInto::::try_into(self.get_memory_size()?)?; if final_offset != expected_final_offset { @@ -701,6 +766,16 @@ impl SandboxMemoryLayout { Ok(builder.build()) } + #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] + pub(crate) fn write_init_data( + &self, + shared_mem: &mut ExclusiveSharedMemory, + bytes: &[u8], + ) -> Result<()> { + shared_mem.copy_from_slice(bytes, self.init_data_offset)?; + Ok(()) + } + /// Write the finished memory layout to `shared_mem` and return /// `Ok` if successful. /// @@ -767,6 +842,14 @@ impl SandboxMemoryLayout { let addr = get_address!(output_data_buffer_offset); shared_mem.write_u64(self.get_output_data_pointer_offset(), addr)?; + // Set up init data pointer + shared_mem.write_u64( + self.get_init_data_size_offset(), + (self.get_unaligned_memory_size() - self.init_data_offset).try_into()?, + )?; + let addr = get_address!(init_data_offset); + shared_mem.write_u64(self.get_init_data_pointer_offset(), addr)?; + // Set up heap buffer pointer let addr = get_address!(guest_heap_buffer_offset); shared_mem.write_u64(self.get_heap_size_offset(), self.heap_size.try_into()?)?; @@ -869,7 +952,8 @@ mod tests { #[test] fn test_get_memory_size() { let sbox_cfg = SandboxConfiguration::default(); - let sbox_mem_layout = SandboxMemoryLayout::new(sbox_cfg, 4096, 2048, 4096).unwrap(); + let sbox_mem_layout = + SandboxMemoryLayout::new(sbox_cfg, 4096, 2048, 4096, 0, None).unwrap(); assert_eq!( sbox_mem_layout.get_memory_size().unwrap(), get_expected_memory_size(&sbox_mem_layout) diff --git a/src/hyperlight_host/src/mem/memory_region.rs b/src/hyperlight_host/src/mem/memory_region.rs index 1ddb842e1..7db8f31c4 100644 --- a/src/hyperlight_host/src/mem/memory_region.rs +++ b/src/hyperlight_host/src/mem/memory_region.rs @@ -43,6 +43,10 @@ use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region}; #[cfg(target_os = "windows")] use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE}; +use super::mgr::{PAGE_NX, PAGE_PRESENT, PAGE_RW, PAGE_USER}; + +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)] @@ -60,6 +64,30 @@ bitflags! { } } +impl MemoryRegionFlags { + pub(crate) fn translate_flags(&self) -> u64 { + let mut page_flags = 0; + + page_flags |= PAGE_PRESENT; // Mark page as present + + if self.contains(MemoryRegionFlags::WRITE) { + page_flags |= PAGE_RW; // Allow read/write + } + + if self.contains(MemoryRegionFlags::STACK_GUARD) { + page_flags |= PAGE_RW; // The guard page is marked RW so that if it gets written to we can detect it in the host + } + + if self.contains(MemoryRegionFlags::EXECUTE) { + page_flags |= PAGE_USER; // Allow user access + } else { + page_flags |= PAGE_NX; // Mark as non-executable if EXECUTE is not set + } + + page_flags + } +} + impl std::fmt::Display for MemoryRegionFlags { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.is_empty() { @@ -129,6 +157,8 @@ pub enum MemoryRegionType { PageTables, /// 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 Host Function Definitions diff --git a/src/hyperlight_host/src/mem/mgr.rs b/src/hyperlight_host/src/mem/mgr.rs index 5c82acfd2..def9bbca4 100644 --- a/src/hyperlight_host/src/mem/mgr.rs +++ b/src/hyperlight_host/src/mem/mgr.rs @@ -28,13 +28,14 @@ use tracing::{Span, instrument}; use super::exe::ExeInfo; use super::layout::SandboxMemoryLayout; -use super::memory_region::{MemoryRegion, MemoryRegionType}; +use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryRegionType}; use super::ptr::{GuestPtr, RawPtr}; use super::ptr_offset::Offset; use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, HostSharedMemory, SharedMemory}; use super::shared_mem_snapshot::SharedMemorySnapshot; use crate::error::HyperlightError::NoMemorySnapshot; use crate::sandbox::SandboxConfiguration; +use crate::sandbox::uninitialized::GuestBlob; use crate::{HyperlightError, Result, log_then_return, new_error}; /// Paging Flags @@ -43,10 +44,10 @@ use crate::{HyperlightError, Result, log_then_return, new_error}; /// /// * Very basic description: https://stackoverflow.com/a/26945892 /// * More in-depth descriptions: https://wiki.osdev.org/Paging -const PAGE_PRESENT: u64 = 1; // Page is Present -const PAGE_RW: u64 = 1 << 1; // Page is Read/Write (if not set page is read only so long as the WP bit in CR0 is set to 1 - which it is in Hyperlight) -const PAGE_USER: u64 = 1 << 2; // User/Supervisor (if this bit is set then the page is accessible by user mode code) -const PAGE_NX: u64 = 1 << 63; // Execute Disable (if this bit is set then data in the page cannot be executed) +pub(crate) const PAGE_PRESENT: u64 = 1; // Page is Present +pub(crate) const PAGE_RW: u64 = 1 << 1; // Page is Read/Write (if not set page is read only so long as the WP bit in CR0 is set to 1 - which it is in Hyperlight) +pub(crate) const PAGE_USER: u64 = 1 << 2; // User/Supervisor (if this bit is set then the page is accessible by user mode code) +pub(crate) const PAGE_NX: u64 = 1 << 63; // Execute Disable (if this bit is set then data in the page cannot be executed) // The amount of memory that can be mapped per page table pub(super) const AMOUNT_OF_MEMORY_PER_PT: usize = 0x200_000; @@ -158,6 +159,11 @@ where // TODO: We parse and load the exe according to its sections and then // have the correct flags set rather than just marking the entire binary as executable MemoryRegionType::Code => PAGE_PRESENT | PAGE_RW | PAGE_USER, + MemoryRegionType::InitData => self + .layout + .init_data_permissions + .map(|perm| perm.translate_flags()) + .unwrap_or(DEFAULT_GUEST_BLOB_MEM_FLAGS.translate_flags()), MemoryRegionType::Stack => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX, #[cfg(feature = "executable_heap")] MemoryRegionType::Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER, @@ -286,12 +292,18 @@ impl SandboxMemoryManager { pub(crate) fn load_guest_binary_into_memory( cfg: SandboxConfiguration, exe_info: &mut ExeInfo, + guest_blob: Option<&GuestBlob>, ) -> Result { + let guest_blob_size = guest_blob.map(|b| b.data.len()).unwrap_or(0); + let guest_blob_mem_flags = guest_blob.map(|b| b.permissions); + let layout = SandboxMemoryLayout::new( cfg, exe_info.loaded_size(), usize::try_from(cfg.get_stack_size(exe_info))?, usize::try_from(cfg.get_heap_size(exe_info))?, + guest_blob_size, + guest_blob_mem_flags, )?; let mut shared_mem = ExclusiveSharedMemory::new(layout.get_memory_size()?)?; diff --git a/src/hyperlight_host/src/sandbox/mem_mgr.rs b/src/hyperlight_host/src/sandbox/mem_mgr.rs index a36492356..eb25af9da 100644 --- a/src/hyperlight_host/src/sandbox/mem_mgr.rs +++ b/src/hyperlight_host/src/sandbox/mem_mgr.rs @@ -98,6 +98,15 @@ impl MemMgrWrapper { let mem_size = shared_mem.mem_size(); layout.write(shared_mem, SandboxMemoryLayout::BASE_ADDRESS, mem_size) } + + #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] + pub(super) fn write_init_data(&mut self, user_memory: &[u8]) -> Result<()> { + let mgr = self.unwrap_mgr_mut(); + let layout = mgr.layout; + let shared_mem = mgr.get_shared_mem_mut(); + layout.write_init_data(shared_mem, user_memory)?; + Ok(()) + } } impl MemMgrWrapper { diff --git a/src/hyperlight_host/src/sandbox/outb.rs b/src/hyperlight_host/src/sandbox/outb.rs index 5836f18b1..dcdd96589 100644 --- a/src/hyperlight_host/src/sandbox/outb.rs +++ b/src/hyperlight_host/src/sandbox/outb.rs @@ -241,9 +241,12 @@ mod tests { let new_mgr = || { let mut exe_info = simple_guest_exe_info().unwrap(); - let mut mgr = - SandboxMemoryManager::load_guest_binary_into_memory(sandbox_cfg, &mut exe_info) - .unwrap(); + let mut mgr = SandboxMemoryManager::load_guest_binary_into_memory( + sandbox_cfg, + &mut exe_info, + None, + ) + .unwrap(); let mem_size = mgr.get_shared_mem_mut().mem_size(); let layout = mgr.layout; let shared_mem = mgr.get_shared_mem_mut(); @@ -353,9 +356,12 @@ mod tests { tracing::subscriber::with_default(subscriber.clone(), || { let new_mgr = || { let mut exe_info = simple_guest_exe_info().unwrap(); - let mut mgr = - SandboxMemoryManager::load_guest_binary_into_memory(sandbox_cfg, &mut exe_info) - .unwrap(); + let mut mgr = SandboxMemoryManager::load_guest_binary_into_memory( + sandbox_cfg, + &mut exe_info, + None, + ) + .unwrap(); let mem_size = mgr.get_shared_mem_mut().mem_size(); let layout = mgr.layout; let shared_mem = mgr.get_shared_mem_mut(); diff --git a/src/hyperlight_host/src/sandbox/uninitialized.rs b/src/hyperlight_host/src/sandbox/uninitialized.rs index 624d894d8..e27f91ff2 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized.rs @@ -30,6 +30,7 @@ use crate::func::{ParameterTuple, SupportedReturnType}; #[cfg(feature = "build-metadata")] use crate::log_build_details; use crate::mem::exe::ExeInfo; +use crate::mem::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegionFlags}; use crate::mem::mgr::{STACK_COOKIE_LEN, SandboxMemoryManager}; use crate::mem::shared_mem::ExclusiveSharedMemory; use crate::sandbox::SandboxConfiguration; @@ -123,15 +124,62 @@ impl } } -/// A `GuestBinary` is either a buffer containing the binary or a path to the binary +/// A `GuestBinary` is either a buffer or the file path to some data (e.g., a guest binary). #[derive(Debug)] pub enum GuestBinary<'a> { - /// A buffer containing the guest binary + /// A buffer containing the GuestBinary Buffer(&'a [u8]), - /// A path to the guest binary + /// A path to the GuestBinary FilePath(String), } +/// 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, + } + } +} + +/// A `GuestEnvironment` is a structure that contains the guest binary and an optional GuestBinary. +#[derive(Debug)] +pub struct GuestEnvironment<'a, 'b> { + /// The guest binary, which can be a file path or a buffer. + pub guest_binary: GuestBinary<'a>, + /// An optional guest blob, which can be used to provide additional data to the guest. + pub init_data: Option>, +} + +impl<'a, 'b> GuestEnvironment<'a, 'b> { + /// Creates a new `GuestEnvironment` with the given guest binary and an optional guest blob. + pub fn new(guest_binary: GuestBinary<'a>, init_data: Option<&'b [u8]>) -> Self { + GuestEnvironment { + guest_binary, + init_data: init_data.map(GuestBlob::from), + } + } +} + +impl<'a> From> for GuestEnvironment<'a, '_> { + fn from(guest_binary: GuestBinary<'a>) -> Self { + GuestEnvironment { + guest_binary, + init_data: None, + } + } +} + impl UninitializedSandbox { /// Create a new sandbox configured to run the binary at path /// `bin_path`. @@ -142,10 +190,13 @@ impl UninitializedSandbox { /// The err attribute is used to emit an error should the Result be an error, it uses the std::`fmt::Debug trait` to print the error. #[instrument( err(Debug), - skip(guest_binary), + skip(env), parent = Span::current() )] - pub fn new(guest_binary: GuestBinary, cfg: Option) -> Result { + pub fn new<'a, 'b>( + env: impl Into>, + cfg: Option, + ) -> Result { #[cfg(feature = "build-metadata")] log_build_details(); @@ -153,6 +204,10 @@ impl UninitializedSandbox { #[cfg(target_os = "windows")] check_windows_version()?; + let env: GuestEnvironment<'_, '_> = env.into(); + let guest_binary = env.guest_binary; + let guest_blob = env.init_data; + // If the guest binary is a file make sure it exists let guest_binary = match guest_binary { GuestBinary::FilePath(binary_path) => { @@ -196,7 +251,12 @@ impl UninitializedSandbox { }; let mut mem_mgr_wrapper = { - let mut mgr = UninitializedSandbox::load_guest_binary(sandbox_cfg, &guest_binary)?; + let mut mgr = UninitializedSandbox::load_guest_binary( + sandbox_cfg, + &guest_binary, + guest_blob.as_ref(), + )?; + let stack_guard = Self::create_stack_guard(); mgr.set_stack_guard(&stack_guard)?; MemMgrWrapper::new(mgr, stack_guard) @@ -204,6 +264,11 @@ impl UninitializedSandbox { mem_mgr_wrapper.write_memory_layout()?; + // if env has a guest blob, load it into shared mem + if let Some(blob) = guest_blob { + mem_mgr_wrapper.write_init_data(blob.data)?; + } + let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default())); let mut sandbox = Self { @@ -242,13 +307,14 @@ impl UninitializedSandbox { pub(super) fn load_guest_binary( cfg: SandboxConfiguration, guest_binary: &GuestBinary, + guest_blob: Option<&GuestBlob>, ) -> Result> { let mut exe_info = match guest_binary { GuestBinary::FilePath(bin_path_str) => ExeInfo::from_file(bin_path_str)?, GuestBinary::Buffer(buffer) => ExeInfo::from_buf(buffer)?, }; - SandboxMemoryManager::load_guest_binary_into_memory(cfg, &mut exe_info) + SandboxMemoryManager::load_guest_binary_into_memory(cfg, &mut exe_info, guest_blob) } /// Set the max log level to be used by the guest. @@ -366,11 +432,28 @@ mod tests { use hyperlight_testing::simple_guest_as_string; use crate::sandbox::SandboxConfiguration; - use crate::sandbox::uninitialized::GuestBinary; + use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment}; use crate::sandbox_state::sandbox::EvolvableSandbox; use crate::sandbox_state::transition::Noop; use crate::{MultiUseSandbox, Result, UninitializedSandbox, new_error}; + #[test] + fn test_load_extra_blob() { + let binary_path = simple_guest_as_string().unwrap(); + 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: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default()).unwrap(); + + let res = sandbox + .call_guest_function_by_name::>("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 @@ -430,8 +513,12 @@ mod tests { let simple_guest_path = simple_guest_as_string().unwrap(); - UninitializedSandbox::load_guest_binary(cfg, &GuestBinary::FilePath(simple_guest_path)) - .unwrap(); + UninitializedSandbox::load_guest_binary( + cfg, + &GuestBinary::FilePath(simple_guest_path), + None.as_ref(), + ) + .unwrap(); } #[test] diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index cfbb10ece..1cd276dd6 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -46,7 +46,7 @@ use hyperlight_guest::exit::{abort_with_code, abort_with_code_and_message}; use hyperlight_guest_bin::guest_function::definition::GuestFunctionDefinition; use hyperlight_guest_bin::guest_function::register::register_function; use hyperlight_guest_bin::host_comm::{ - call_host_function, call_host_function_without_returning_result, + call_host_function, call_host_function_without_returning_result, read_n_bytes_from_user_memory, }; use hyperlight_guest_bin::memory::malloc; use hyperlight_guest_bin::{MIN_STACK_ADDRESS, guest_logger}; @@ -750,8 +750,42 @@ fn large_parameters(function_call: &FunctionCall) -> Result> { } } +fn read_from_user_memory(function_call: &FunctionCall) -> Result> { + if let (ParameterValue::ULong(num), ParameterValue::VecBytes(expected)) = ( + function_call.parameters.clone().unwrap()[0].clone(), + function_call.parameters.clone().unwrap()[1].clone(), + ) { + 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(get_flatbuffer_result(&*bytes)) + } else { + Err(HyperlightGuestError::new( + ErrorCode::GuestFunctionParameterTypeMismatch, + "Invalid parameters passed to read_from_user_memory".to_string(), + )) + } +} + #[no_mangle] pub extern "C" fn hyperlight_main() { + let read_from_user_memory_def = GuestFunctionDefinition::new( + "ReadFromUserMemory".to_string(), + Vec::from(&[ParameterType::ULong, ParameterType::VecBytes]), + ReturnType::VecBytes, + read_from_user_memory as usize, + ); + + register_function(read_from_user_memory_def); + let set_static_def = GuestFunctionDefinition::new( "SetStatic".to_string(), Vec::new(),