diff --git a/src/hyperlight_common/src/lib.rs b/src/hyperlight_common/src/lib.rs index f05baa9f9..590da589b 100644 --- a/src/hyperlight_common/src/lib.rs +++ b/src/hyperlight_common/src/lib.rs @@ -36,3 +36,6 @@ pub mod flatbuffer_wrappers; mod flatbuffers; /// cbindgen:ignore pub mod mem; + +/// cbindgen:ignore +pub mod outb; diff --git a/src/hyperlight_common/src/mem/mod.rs b/src/hyperlight_common/src/mem.rs similarity index 92% rename from src/hyperlight_common/src/mem/mod.rs rename to src/hyperlight_common/src/mem.rs index 3ae9a3688..f9fcd8f04 100644 --- a/src/hyperlight_common/src/mem/mod.rs +++ b/src/hyperlight_common/src/mem.rs @@ -74,12 +74,6 @@ pub struct GuestStackData { pub bootStackAddress: u64, } -#[repr(C)] -pub struct GuestPanicContextData { - pub guestPanicContextDataSize: u64, - pub guestPanicContextDataBuffer: *mut c_void, -} - #[repr(C)] pub struct HyperlightPEB { pub security_cookie_seed: u64, @@ -90,7 +84,6 @@ pub struct HyperlightPEB { pub runMode: RunMode, pub inputdata: InputData, pub outputdata: OutputData, - pub guestPanicContextData: GuestPanicContextData, pub guestheapData: GuestHeapData, pub gueststackData: GuestStackData, } diff --git a/src/hyperlight_common/src/outb.rs b/src/hyperlight_common/src/outb.rs new file mode 100644 index 000000000..36b944775 --- /dev/null +++ b/src/hyperlight_common/src/outb.rs @@ -0,0 +1,95 @@ +use core::convert::TryFrom; + +use anyhow::{anyhow, Error}; + +/// Exception codes for the x86 architecture. +/// These are helpful to identify the type of exception that occurred +/// together with OutBAction::Abort. +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +pub enum Exception { + DivideByZero = 0, + Debug = 1, + NonMaskableInterrupt = 2, + Breakpoint = 3, + Overflow = 4, + BoundRangeExceeded = 5, + InvalidOpcode = 6, + DeviceNotAvailable = 7, + DoubleFault = 8, + CoprocessorSegmentOverrun = 9, + InvalidTSS = 10, + SegmentNotPresent = 11, + StackSegmentFault = 12, + GeneralProtectionFault = 13, + PageFault = 14, + Reserved = 15, + X87FloatingPointException = 16, + AlignmentCheck = 17, + MachineCheck = 18, + SIMDFloatingPointException = 19, + VirtualizationException = 20, + SecurityException = 30, + NoException = 0xFF, +} + +impl TryFrom for Exception { + type Error = Error; + + fn try_from(value: u8) -> Result { + use Exception::*; + let exception = match value { + 0 => DivideByZero, + 1 => Debug, + 2 => NonMaskableInterrupt, + 3 => Breakpoint, + 4 => Overflow, + 5 => BoundRangeExceeded, + 6 => InvalidOpcode, + 7 => DeviceNotAvailable, + 8 => DoubleFault, + 9 => CoprocessorSegmentOverrun, + 10 => InvalidTSS, + 11 => SegmentNotPresent, + 12 => StackSegmentFault, + 13 => GeneralProtectionFault, + 14 => PageFault, + 15 => Reserved, + 16 => X87FloatingPointException, + 17 => AlignmentCheck, + 18 => MachineCheck, + 19 => SIMDFloatingPointException, + 20 => VirtualizationException, + 30 => SecurityException, + 0x7F => NoException, + _ => return Err(anyhow!("Unknown exception code: {:#x}", value)), + }; + + Ok(exception) + } +} + +/// Supported actions when issuing an OUTB actions by Hyperlight. +/// - Log: for logging, +/// - CallFunction: makes a call to a host function, +/// - Abort: aborts the execution of the guest, +/// - DebugPrint: prints a message to the host +pub enum OutBAction { + Log = 99, + CallFunction = 101, + Abort = 102, + DebugPrint = 103, +} + +impl TryFrom for OutBAction { + type Error = anyhow::Error; + fn try_from(val: u16) -> anyhow::Result { + match val { + 99 => Ok(OutBAction::Log), + 101 => Ok(OutBAction::CallFunction), + 102 => Ok(OutBAction::Abort), + 103 => Ok(OutBAction::DebugPrint), + _ => Err(anyhow::anyhow!("Invalid OutBAction value: {}", val)), + } + } +} diff --git a/src/hyperlight_guest/src/entrypoint.rs b/src/hyperlight_guest/src/entrypoint.rs index 657f35543..5f1e47741 100644 --- a/src/hyperlight_guest/src/entrypoint.rs +++ b/src/hyperlight_guest/src/entrypoint.rs @@ -16,16 +16,16 @@ limitations under the License. use core::arch::asm; use core::ffi::{c_char, c_void, CStr}; -use core::ptr::copy_nonoverlapping; use hyperlight_common::mem::{HyperlightPEB, RunMode}; +use hyperlight_common::outb::OutBAction; use log::LevelFilter; use spin::Once; use crate::gdt::load_gdt; use crate::guest_function_call::dispatch_function; use crate::guest_logger::init_logger; -use crate::host_function_call::{outb, OutBAction}; +use crate::host_function_call::{debug_print, outb}; use crate::idtr::load_idt; use crate::{ __security_cookie, HEAP_ALLOCATOR, MIN_STACK_ADDRESS, OS_PAGE_SIZE, OUTB_PTR, @@ -43,11 +43,11 @@ pub fn halt() { #[no_mangle] pub extern "C" fn abort() -> ! { - abort_with_code(0) + abort_with_code(&[0]) } -pub fn abort_with_code(code: i32) -> ! { - outb(OutBAction::Abort as u16, code as u8); +pub fn abort_with_code(code: &[u8]) -> ! { + outb(OutBAction::Abort as u16, code); unreachable!() } @@ -55,14 +55,14 @@ pub fn abort_with_code(code: i32) -> ! { /// /// # Safety /// This function is unsafe because it dereferences a raw pointer. -pub unsafe fn abort_with_code_and_message(code: i32, message_ptr: *const c_char) -> ! { - let peb_ptr = P_PEB.unwrap(); - copy_nonoverlapping( - message_ptr, - (*peb_ptr).guestPanicContextData.guestPanicContextDataBuffer as *mut c_char, - CStr::from_ptr(message_ptr).count_bytes() + 1, // +1 for null terminator +pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_char) -> ! { + debug_print( + CStr::from_ptr(message_ptr) + .to_str() + .expect("Invalid UTF-8 string"), ); - outb(OutBAction::Abort as u16, code as u8); + + outb(OutBAction::Abort as u16, code); unreachable!() } @@ -114,7 +114,7 @@ pub extern "win64" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_ RUNNING_MODE = (*peb_ptr).runMode; OUTB_PTR = { - let outb_ptr: extern "win64" fn(u16, u8) = + let outb_ptr: extern "win64" fn(u16, *const u8, u64) = core::mem::transmute((*peb_ptr).pOutb); Some(outb_ptr) }; @@ -124,8 +124,12 @@ pub extern "win64" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_ } OUTB_PTR_WITH_CONTEXT = { - let outb_ptr_with_context: extern "win64" fn(*mut c_void, u16, u8) = - core::mem::transmute((*peb_ptr).pOutb); + let outb_ptr_with_context: extern "win64" fn( + *mut c_void, + u16, + *const u8, + u64, + ) = core::mem::transmute((*peb_ptr).pOutb); Some(outb_ptr_with_context) }; } diff --git a/src/hyperlight_guest/src/guest_error.rs b/src/hyperlight_guest/src/guest_error.rs index bb4e32748..4057c7c09 100644 --- a/src/hyperlight_guest/src/guest_error.rs +++ b/src/hyperlight_guest/src/guest_error.rs @@ -19,9 +19,10 @@ use alloc::vec::Vec; use core::ffi::{c_char, CStr}; use hyperlight_common::flatbuffer_wrappers::guest_error::{ErrorCode, GuestError}; +use hyperlight_common::outb::OutBAction; use crate::entrypoint::halt; -use crate::host_function_call::{outb, OutBAction}; +use crate::host_function_call::outb; use crate::shared_output_data::push_shared_output_data; pub(crate) fn write_error(error_code: ErrorCode, message: Option<&str>) { @@ -48,7 +49,7 @@ pub(crate) fn set_error_and_halt(error_code: ErrorCode, message: &str) { #[no_mangle] pub(crate) extern "win64" fn set_stack_allocate_error() { - outb(OutBAction::Abort as u16, ErrorCode::StackOverflow as u8); + outb(OutBAction::Abort as u16, &[ErrorCode::StackOverflow as u8]); } #[no_mangle] diff --git a/src/hyperlight_guest/src/host_function_call.rs b/src/hyperlight_guest/src/host_function_call.rs index a583cc86a..feed39710 100644 --- a/src/hyperlight_guest/src/host_function_call.rs +++ b/src/hyperlight_guest/src/host_function_call.rs @@ -26,18 +26,13 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; use hyperlight_common::mem::RunMode; +use hyperlight_common::outb::OutBAction; use crate::error::{HyperlightGuestError, Result}; use crate::shared_input_data::try_pop_shared_input_data_into; use crate::shared_output_data::push_shared_output_data; use crate::{OUTB_PTR, OUTB_PTR_WITH_CONTEXT, P_PEB, RUNNING_MODE}; -pub enum OutBAction { - Log = 99, - CallFunction = 101, - Abort = 102, -} - /// Get a return value from a host function call. /// This usually requires a host function to be called first using `call_host_function`. pub fn get_host_return_value>() -> Result { @@ -75,24 +70,44 @@ pub fn call_host_function( push_shared_output_data(host_function_call_buffer)?; - outb(OutBAction::CallFunction as u16, 0); + outb(OutBAction::CallFunction as u16, &[0]); Ok(()) } -pub fn outb(port: u16, value: u8) { +pub fn outb(port: u16, data: &[u8]) { unsafe { match RUNNING_MODE { RunMode::Hypervisor => { - hloutb(port, value); + for chunk in data.chunks(4) { + // Process the data in chunks of 4 bytes. If a chunk has fewer than 4 bytes, + // pad it with 0x7F to ensure it can be converted into a 4-byte array. + // The choice of 0x7F as the padding value is arbitrary and does not carry + // any special meaning; it simply ensures consistent chunk size. + let val = match chunk { + [a, b, c, d] => u32::from_le_bytes([*a, *b, *c, *d]), + [a, b, c] => u32::from_le_bytes([*a, *b, *c, 0x7F]), + [a, b] => u32::from_le_bytes([*a, *b, 0x7F, 0x7F]), + [a] => u32::from_le_bytes([*a, 0x7F, 0x7F, 0x7F]), + [] => break, + _ => unreachable!(), + }; + + hloutd(val, port); + } } RunMode::InProcessLinux | RunMode::InProcessWindows => { if let Some(outb_func) = OUTB_PTR_WITH_CONTEXT { if let Some(peb_ptr) = P_PEB { - outb_func((*peb_ptr).pOutbContext, port, value); + outb_func( + (*peb_ptr).pOutbContext, + port, + data.as_ptr(), + data.len() as u64, + ); } } else if let Some(outb_func) = OUTB_PTR { - outb_func(port, value); + outb_func(port, data.as_ptr(), data.len() as u64); } else { panic!("Tried to call outb without hypervisor and without outb function ptrs"); } @@ -105,7 +120,7 @@ pub fn outb(port: u16, value: u8) { } extern "win64" { - fn hloutb(port: u16, value: u8); + fn hloutd(value: u32, port: u16); } pub fn print_output_as_guest_function(function_call: &FunctionCall) -> Result> { @@ -125,13 +140,15 @@ pub fn print_output_as_guest_function(function_call: &FunctionCall) -> Result ! { - unsafe { - let peb_ptr = P_PEB.unwrap(); - copy_nonoverlapping( - info.to_string().as_ptr(), - (*peb_ptr).guestPanicContextData.guestPanicContextDataBuffer as *mut u8, - (*peb_ptr).guestPanicContextData.guestPanicContextDataSize as usize, - ); - } - outb(OutBAction::Abort as u16, ErrorCode::UnknownError as u8); + debug_print(info.to_string().as_str()); + outb(OutBAction::Abort as u16, &[ErrorCode::UnknownError as u8]); unsafe { unreachable_unchecked() } } @@ -96,9 +90,9 @@ pub(crate) static mut P_PEB: Option<*mut HyperlightPEB> = None; pub static mut MIN_STACK_ADDRESS: u64 = 0; pub static mut OS_PAGE_SIZE: u32 = 0; -pub(crate) static mut OUTB_PTR: Option = None; +pub(crate) static mut OUTB_PTR: Option = None; pub(crate) static mut OUTB_PTR_WITH_CONTEXT: Option< - extern "win64" fn(*mut core::ffi::c_void, u16, u8), + extern "win64" fn(*mut core::ffi::c_void, u16, *const u8, u64), > = None; pub static mut RUNNING_MODE: RunMode = RunMode::None; diff --git a/src/hyperlight_guest/src/logging.rs b/src/hyperlight_guest/src/logging.rs index 45cee65dd..ed5a833bc 100644 --- a/src/hyperlight_guest/src/logging.rs +++ b/src/hyperlight_guest/src/logging.rs @@ -19,8 +19,9 @@ use alloc::vec::Vec; use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData; use hyperlight_common::flatbuffer_wrappers::guest_log_level::LogLevel; +use hyperlight_common::outb::OutBAction; -use crate::host_function_call::{outb, OutBAction}; +use crate::host_function_call::outb; use crate::shared_output_data::push_shared_output_data; fn write_log_data( @@ -56,5 +57,5 @@ pub fn log_message( line: u32, ) { write_log_data(log_level, message, source, caller, source_file, line); - outb(OutBAction::Log as u16, 0); + outb(OutBAction::Log as u16, &[0]); } diff --git a/src/hyperlight_guest/src/memory.rs b/src/hyperlight_guest/src/memory.rs index 70390cbdc..9e0a71a6d 100644 --- a/src/hyperlight_guest/src/memory.rs +++ b/src/hyperlight_guest/src/memory.rs @@ -67,7 +67,7 @@ unsafe fn alloc_helper(size: usize, zero: bool) -> *mut c_void { false => alloc::alloc::alloc(layout), }; if raw_ptr.is_null() { - abort_with_code(ErrorCode::MallocFailed as i32); + abort_with_code(&[ErrorCode::MallocFailed as u8]); } else { let layout_ptr = raw_ptr as *mut Layout; layout_ptr.write(layout); @@ -148,7 +148,7 @@ pub unsafe extern "C" fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void { if new_block_start.is_null() { // Realloc failed - abort_with_code(ErrorCode::MallocFailed as i32); + abort_with_code(&[ErrorCode::MallocFailed as u8]); } else { // Update the stored Layout, then return ptr to memory right after the Layout. new_block_start.write(new_layout); diff --git a/src/hyperlight_guest_capi/src/error.rs b/src/hyperlight_guest_capi/src/error.rs index 948abae3a..ea87b6c6d 100644 --- a/src/hyperlight_guest_capi/src/error.rs +++ b/src/hyperlight_guest_capi/src/error.rs @@ -12,10 +12,10 @@ pub extern "C" fn hl_set_error(err: ErrorCode, message: *const c_char) { #[no_mangle] pub extern "C" fn hl_abort_with_code(err: i32) { - hyperlight_guest::entrypoint::abort_with_code(err); + hyperlight_guest::entrypoint::abort_with_code(&[err as u8]); } #[no_mangle] pub extern "C" fn hl_abort_with_code_and_message(err: i32, message: *const c_char) { - unsafe { hyperlight_guest::entrypoint::abort_with_code_and_message(err, message) }; + unsafe { hyperlight_guest::entrypoint::abort_with_code_and_message(&[err as u8], message) }; } diff --git a/src/hyperlight_host/src/func/guest_dispatch.rs b/src/hyperlight_host/src/func/guest_dispatch.rs index a7849bc99..3f1bf66e2 100644 --- a/src/hyperlight_host/src/func/guest_dispatch.rs +++ b/src/hyperlight_host/src/func/guest_dispatch.rs @@ -494,7 +494,7 @@ mod tests { match res.unwrap_err() { HyperlightError::GuestAborted(_, msg) => { // msg should indicate we got an invalid opcode exception - assert!(msg.contains("EXCEPTION: 0x6")); + assert!(msg.contains("InvalidOpcode")); } e => panic!( "Expected HyperlightError::GuestExecutionError but got {:?}", diff --git a/src/hyperlight_host/src/hypervisor/handlers.rs b/src/hyperlight_host/src/hypervisor/handlers.rs index 3ffd64441..4cc3bcb51 100644 --- a/src/hyperlight_host/src/hypervisor/handlers.rs +++ b/src/hyperlight_host/src/hypervisor/handlers.rs @@ -25,7 +25,7 @@ use crate::{new_error, Result}; /// has initiated an outb operation. pub trait OutBHandlerCaller: Sync + Send { /// Function that gets called when an outb operation has occurred. - fn call(&mut self, port: u16, payload: u64) -> Result<()>; + fn call(&mut self, port: u16, payload: Vec) -> Result<()>; } /// A convenient type representing a common way `OutBHandler` implementations @@ -36,7 +36,7 @@ pub trait OutBHandlerCaller: Sync + Send { /// a &mut self). pub type OutBHandlerWrapper = Arc>; -pub(crate) type OutBHandlerFunction = Box Result<()> + Send>; +pub(crate) type OutBHandlerFunction = Box) -> Result<()> + Send>; /// A `OutBHandler` implementation using a `OutBHandlerFunction` /// @@ -52,7 +52,7 @@ impl From for OutBHandler { impl OutBHandlerCaller for OutBHandler { #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn call(&mut self, port: u16, payload: u64) -> Result<()> { + fn call(&mut self, port: u16, payload: Vec) -> Result<()> { let mut func = self .0 .try_lock() diff --git a/src/hyperlight_host/src/hypervisor/hyperv_linux.rs b/src/hyperlight_host/src/hypervisor/hyperv_linux.rs index 8419cb073..b271e156a 100644 --- a/src/hyperlight_host/src/hypervisor/hyperv_linux.rs +++ b/src/hyperlight_host/src/hypervisor/hyperv_linux.rs @@ -542,11 +542,10 @@ impl Hypervisor for HypervLinuxDriver { instruction_length: u64, outb_handle_fn: OutBHandlerWrapper, ) -> Result<()> { - let payload = data[..8].try_into()?; outb_handle_fn .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .call(port, u64::from_le_bytes(payload))?; + .call(port, data)?; // update rip self.vcpu_fd.set_reg(&[hv_register_assoc { diff --git a/src/hyperlight_host/src/hypervisor/hyperv_windows.rs b/src/hyperlight_host/src/hypervisor/hyperv_windows.rs index 47fbaaa9d..44256d1fe 100644 --- a/src/hyperlight_host/src/hypervisor/hyperv_windows.rs +++ b/src/hyperlight_host/src/hypervisor/hyperv_windows.rs @@ -390,11 +390,10 @@ impl Hypervisor for HypervWindowsDriver { instruction_length: u64, outb_handle_fn: OutBHandlerWrapper, ) -> Result<()> { - let payload = data[..8].try_into()?; outb_handle_fn .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .call(port, u64::from_le_bytes(payload))?; + .call(port, data)?; let mut regs = self.processor.get_regs()?; regs.rip = rip + instruction_length; @@ -506,7 +505,7 @@ pub mod tests { #[serial] fn test_init() { let outb_handler = { - let func: Box Result<()> + Send> = + let func: Box) -> Result<()> + Send> = Box::new(|_, _| -> Result<()> { Ok(()) }); Arc::new(Mutex::new(OutBHandler::from(func))) }; diff --git a/src/hyperlight_host/src/hypervisor/kvm.rs b/src/hyperlight_host/src/hypervisor/kvm.rs index 7425a0097..903c078ca 100644 --- a/src/hyperlight_host/src/hypervisor/kvm.rs +++ b/src/hyperlight_host/src/hypervisor/kvm.rs @@ -497,11 +497,10 @@ impl Hypervisor for KVMDriver { if data.is_empty() { log_then_return!("no data was given in IO interrupt"); } else { - let payload_u64 = u64::from(data[0]); outb_handle_fn .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .call(port, payload_u64)?; + .call(port, data)?; } Ok(()) @@ -665,7 +664,7 @@ mod tests { } let outb_handler: Arc> = { - let func: Box Result<()> + Send> = + let func: Box) -> Result<()> + Send> = Box::new(|_, _| -> Result<()> { Ok(()) }); Arc::new(Mutex::new(OutBHandler::from(func))) }; diff --git a/src/hyperlight_host/src/mem/layout.rs b/src/hyperlight_host/src/mem/layout.rs index 6f30d2d28..52949a255 100644 --- a/src/hyperlight_host/src/mem/layout.rs +++ b/src/hyperlight_host/src/mem/layout.rs @@ -21,7 +21,7 @@ use rand::{rng, RngCore}; use tracing::{instrument, Span}; use super::memory_region::MemoryRegionType::{ - Code, GuardPage, Heap, InputData, OutputData, PageTables, PanicContext, Peb, Stack, + Code, GuardPage, Heap, InputData, OutputData, PageTables, Peb, Stack, }; use super::memory_region::{MemoryRegion, MemoryRegionFlags, MemoryRegionVecBuilder}; use super::mgr::AMOUNT_OF_MEMORY_PER_PT; @@ -37,8 +37,6 @@ use crate::{log_then_return, new_error, Result}; // +-------------------------------------------+ // | Guest Heap | // +-------------------------------------------+ -// | Guest Panic Context | -// +-------------------------------------------+ // | Output Data | // +-------------------------------------------+ // | Input Data | @@ -70,10 +68,6 @@ use crate::{log_then_return, new_error, Result}; /// the stack might be slightly bigger or smaller than this value since total memory /// size is rounded up to the nearest 4K, and there is a 16-byte stack guard written /// to the top of the stack. (see below for more details) -/// -/// - `GuestPanicContext` - contains a buffer for context associated with any guest -/// panic that occurred. -/// the length of this field is returned by the `guest_panic_context_size()` fn of this struct. #[derive(Copy, Clone)] pub(crate) struct SandboxMemoryLayout { @@ -92,7 +86,6 @@ pub(crate) struct SandboxMemoryLayout { peb_runmode_offset: usize, peb_input_data_offset: usize, peb_output_data_offset: usize, - peb_guest_panic_context_offset: usize, peb_heap_data_offset: usize, peb_guest_stack_data_offset: usize, @@ -100,7 +93,6 @@ pub(crate) struct SandboxMemoryLayout { // that are written to the PEB struct pub(super) input_data_buffer_offset: usize, pub(super) output_data_buffer_offset: usize, - guest_panic_context_buffer_offset: usize, guest_heap_buffer_offset: usize, guard_page_offset: usize, guest_user_stack_buffer_offset: usize, // the lowest address of the user stack @@ -146,10 +138,6 @@ impl Debug for SandboxMemoryLayout { "Output Data Offset", &format_args!("{:#x}", self.peb_output_data_offset), ) - .field( - "Guest Panic Context Offset", - &format_args!("{:#x}", self.peb_guest_panic_context_offset), - ) .field( "Guest Heap Offset", &format_args!("{:#x}", self.peb_heap_data_offset), @@ -166,10 +154,6 @@ impl Debug for SandboxMemoryLayout { "Output Data Buffer Offset", &format_args!("{:#x}", self.output_data_buffer_offset), ) - .field( - "Guest Panic Context Buffer Offset", - &format_args!("{:#x}", self.guest_panic_context_buffer_offset), - ) .field( "Guest Heap Buffer Offset", &format_args!("{:#x}", self.guest_heap_buffer_offset), @@ -246,8 +230,6 @@ impl SandboxMemoryLayout { let peb_runmode_offset = peb_offset + offset_of!(HyperlightPEB, runMode); let peb_input_data_offset = peb_offset + offset_of!(HyperlightPEB, inputdata); let peb_output_data_offset = peb_offset + offset_of!(HyperlightPEB, outputdata); - let peb_guest_panic_context_offset = - peb_offset + offset_of!(HyperlightPEB, guestPanicContextData); let peb_heap_data_offset = peb_offset + offset_of!(HyperlightPEB, guestheapData); let peb_guest_stack_data_offset = peb_offset + offset_of!(HyperlightPEB, gueststackData); @@ -262,13 +244,9 @@ impl SandboxMemoryLayout { input_data_buffer_offset + cfg.get_input_data_size(), PAGE_SIZE_USIZE, ); - let guest_panic_context_buffer_offset = round_up_to( - output_data_buffer_offset + cfg.get_output_data_size(), - PAGE_SIZE_USIZE, - ); // make sure heap buffer starts at 4K boundary let guest_heap_buffer_offset = round_up_to( - guest_panic_context_buffer_offset + cfg.get_guest_panic_context_buffer_size(), + output_data_buffer_offset + cfg.get_output_data_size(), PAGE_SIZE_USIZE, ); // make sure guard page starts at 4K boundary @@ -287,7 +265,6 @@ impl SandboxMemoryLayout { peb_runmode_offset, peb_input_data_offset, peb_output_data_offset, - peb_guest_panic_context_offset, peb_heap_data_offset, peb_guest_stack_data_offset, sandbox_memory_config: cfg, @@ -297,7 +274,6 @@ impl SandboxMemoryLayout { guest_heap_buffer_offset, guest_user_stack_buffer_offset, peb_address, - guest_panic_context_buffer_offset, guard_page_offset, total_page_table_size, guest_code_offset, @@ -425,33 +401,6 @@ impl SandboxMemoryLayout { self.get_min_guest_stack_address_offset() + size_of::() } - // Get the offset in guest memory to the start of the guest panic context data - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn get_guest_panic_context_offset(&self) -> usize { - self.peb_guest_panic_context_offset - } - - // Get the offset to the guest panic context buffer size - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn get_guest_panic_context_size_offset(&self) -> usize { - // The size field is the first field in the `GuestPanicContext` data - self.peb_guest_panic_context_offset - } - - /// Get the offset to the guest panic context buffer pointer - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn get_guest_panic_context_buffer_pointer_offset(&self) -> usize { - // The guest panic data pointer is immediately after the guest - // panic data size field in the `GuestPanicContext` data which is a `u64` - self.get_guest_panic_context_size_offset() + size_of::() - } - - /// Get the offset to the guest panic context buffer pointer - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub(crate) fn get_guest_panic_context_buffer_offset(&self) -> usize { - self.guest_panic_context_buffer_offset - } - /// Get the offset to the guest guard page #[instrument(skip_all, parent = Span::current(), level= "Trace")] pub fn get_guard_page_offset(&self) -> usize { @@ -508,8 +457,6 @@ impl SandboxMemoryLayout { total_mapped_memory_size += round_up_to(heap_size, PAGE_SIZE_USIZE); total_mapped_memory_size += round_up_to(cfg.get_input_data_size(), PAGE_SIZE_USIZE); total_mapped_memory_size += round_up_to(cfg.get_output_data_size(), PAGE_SIZE_USIZE); - total_mapped_memory_size += - round_up_to(cfg.get_guest_panic_context_buffer_size(), PAGE_SIZE_USIZE); total_mapped_memory_size += round_up_to(size_of::(), PAGE_SIZE_USIZE); // Add the base address of the sandbox @@ -624,31 +571,12 @@ impl SandboxMemoryLayout { } // guest output data - let guest_panic_context_offset = builder.push_page_aligned( + let heap_offset = builder.push_page_aligned( self.sandbox_memory_config.get_output_data_size(), MemoryRegionFlags::READ | MemoryRegionFlags::WRITE, OutputData, ); - let expected_guest_panic_context_offset = - TryInto::::try_into(self.guest_panic_context_buffer_offset)?; - - if guest_panic_context_offset != expected_guest_panic_context_offset { - return Err(new_error!( - "Guest Panic Context offset does not match expected Guest Panic Context offset expected: {}, actual: {}", - expected_guest_panic_context_offset, - guest_panic_context_offset - )); - } - - // guest panic context - let heap_offset = builder.push_page_aligned( - self.sandbox_memory_config - .get_guest_panic_context_buffer_size(), - MemoryRegionFlags::READ | MemoryRegionFlags::WRITE, - PanicContext, - ); - let expected_heap_offset = TryInto::::try_into(self.guest_heap_buffer_offset)?; if heap_offset != expected_heap_offset { @@ -799,16 +727,6 @@ impl SandboxMemoryLayout { let addr = get_address!(output_data_buffer_offset); shared_mem.write_u64(self.get_output_data_pointer_offset(), addr)?; - // Set up the guest panic context buffer - let addr = get_address!(guest_panic_context_buffer_offset); - shared_mem.write_u64( - self.get_guest_panic_context_size_offset(), - self.sandbox_memory_config - .get_guest_panic_context_buffer_size() - .try_into()?, - )?; - shared_mem.write_u64(self.get_guest_panic_context_buffer_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()?)?; @@ -897,8 +815,6 @@ mod tests { expected_size += round_up_to(cfg.get_output_data_size(), PAGE_SIZE_USIZE); - expected_size += round_up_to(cfg.get_guest_panic_context_buffer_size(), PAGE_SIZE_USIZE); - expected_size += round_up_to(layout.heap_size, PAGE_SIZE_USIZE); expected_size += PAGE_SIZE_USIZE; // guard page diff --git a/src/hyperlight_host/src/mem/memory_region.rs b/src/hyperlight_host/src/mem/memory_region.rs index 89ff27a3f..46177bb4b 100644 --- a/src/hyperlight_host/src/mem/memory_region.rs +++ b/src/hyperlight_host/src/mem/memory_region.rs @@ -135,8 +135,6 @@ pub enum MemoryRegionType { InputData, /// The region contains the Output Data OutputData, - /// The region contains the Panic Context - PanicContext, /// 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 9ff566364..1855e0ff2 100644 --- a/src/hyperlight_host/src/mem/mgr.rs +++ b/src/hyperlight_host/src/mem/mgr.rs @@ -198,7 +198,6 @@ where MemoryRegionType::InputData => PAGE_PRESENT | PAGE_RW | PAGE_NX, MemoryRegionType::OutputData => PAGE_PRESENT | PAGE_RW | PAGE_NX, MemoryRegionType::Peb => PAGE_PRESENT | PAGE_RW | PAGE_NX, - MemoryRegionType::PanicContext => PAGE_PRESENT | PAGE_RW | PAGE_NX, MemoryRegionType::PageTables => PAGE_PRESENT | PAGE_RW | PAGE_NX, }, // If there is an error then the address isn't mapped so mark it as not present @@ -596,22 +595,6 @@ impl SandboxMemoryManager { self.layout.sandbox_memory_config.get_output_data_size(), ) } - - /// Read guest panic data from the `SharedMemory` contained within `self` - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn read_guest_panic_context_data(&self) -> Result> { - let offset = self.layout.get_guest_panic_context_buffer_offset(); - let buffer_size = { - let size_u64 = self - .shared_mem - .read::(self.layout.get_guest_panic_context_size_offset())?; - usize::try_from(size_u64) - }?; - let mut vec_out = vec![0; buffer_size]; - self.shared_mem - .copy_to_slice(vec_out.as_mut_slice(), offset)?; - Ok(vec_out) - } } #[cfg(test)] diff --git a/src/hyperlight_host/src/sandbox/config.rs b/src/hyperlight_host/src/sandbox/config.rs index 363c5e471..1689fb5d0 100644 --- a/src/hyperlight_host/src/sandbox/config.rs +++ b/src/hyperlight_host/src/sandbox/config.rs @@ -81,9 +81,6 @@ pub struct SandboxConfiguration { // field should be represented as an `Option`, that type is not // FFI-safe, so it cannot be. max_initialization_time: u16, - /// The size of the memory buffer that is made available for serializing - /// guest panic context - guest_panic_context_buffer_size: usize, } impl SandboxConfiguration { @@ -113,10 +110,6 @@ impl SandboxConfiguration { pub const MIN_MAX_WAIT_FOR_CANCELLATION: u8 = 10; /// The maximum value for max wait for cancellation (in milliseconds) pub const MAX_MAX_WAIT_FOR_CANCELLATION: u8 = u8::MAX; - /// The default and minimum values for guest panic context data - pub const DEFAULT_GUEST_PANIC_CONTEXT_BUFFER_SIZE: usize = 0x400; - /// The minimum value for guest panic context data - pub const MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE: usize = 0x400; #[allow(clippy::too_many_arguments)] /// Create a new configuration for a sandbox with the given sizes. @@ -129,7 +122,6 @@ impl SandboxConfiguration { max_execution_time: Option, max_initialization_time: Option, max_wait_for_cancellation: Option, - guest_panic_context_buffer_size: usize, #[cfg(gdb)] guest_debug_info: Option, ) -> Self { Self { @@ -184,10 +176,6 @@ impl SandboxConfiguration { None => Self::DEFAULT_MAX_INITIALIZATION_TIME, } }, - guest_panic_context_buffer_size: max( - guest_panic_context_buffer_size, - Self::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE, - ), #[cfg(gdb)] guest_debug_info, } @@ -275,15 +263,6 @@ impl SandboxConfiguration { } } - /// Set the size of the memory buffer that is made available for serializing guest panic context - /// the minimum value is MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE - pub fn set_guest_panic_context_buffer_size(&mut self, guest_panic_context_buffer_size: usize) { - self.guest_panic_context_buffer_size = max( - guest_panic_context_buffer_size, - Self::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE, - ); - } - /// Sets the configuration for the guest debug #[cfg(gdb)] #[instrument(skip_all, parent = Span::current(), level= "Trace")] @@ -301,11 +280,6 @@ impl SandboxConfiguration { self.output_data_size } - #[instrument(skip_all, parent = Span::current(), level="Trace")] - pub(crate) fn get_guest_panic_context_buffer_size(&self) -> usize { - self.guest_panic_context_buffer_size - } - #[instrument(skip_all, parent = Span::current(), level= "Trace")] pub(crate) fn get_max_execution_time(&self) -> u16 { self.max_execution_time @@ -364,7 +338,6 @@ impl Default for SandboxConfiguration { None, None, None, - Self::DEFAULT_GUEST_PANIC_CONTEXT_BUFFER_SIZE, #[cfg(gdb)] None, ) @@ -387,7 +360,6 @@ mod tests { const MAX_EXECUTION_TIME_OVERRIDE: u16 = 1010; const MAX_WAIT_FOR_CANCELLATION_OVERRIDE: u8 = 200; const MAX_INITIALIZATION_TIME_OVERRIDE: u16 = 2000; - const GUEST_PANIC_CONTEXT_BUFFER_SIZE_OVERRIDE: usize = 0x4005; let mut cfg = SandboxConfiguration::new( INPUT_DATA_SIZE_OVERRIDE, OUTPUT_DATA_SIZE_OVERRIDE, @@ -400,7 +372,6 @@ mod tests { Some(Duration::from_millis( MAX_WAIT_FOR_CANCELLATION_OVERRIDE as u64, )), - GUEST_PANIC_CONTEXT_BUFFER_SIZE_OVERRIDE, #[cfg(gdb)] None, ); @@ -429,10 +400,6 @@ mod tests { MAX_WAIT_FOR_CANCELLATION_OVERRIDE, cfg.max_wait_for_cancellation ); - assert_eq!( - GUEST_PANIC_CONTEXT_BUFFER_SIZE_OVERRIDE, - cfg.guest_panic_context_buffer_size - ); } #[test] @@ -451,7 +418,6 @@ mod tests { Some(Duration::from_millis( SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION as u64 - 1, )), - SandboxConfiguration::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE - 1, #[cfg(gdb)] None, ); @@ -467,10 +433,6 @@ mod tests { SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION, cfg.max_wait_for_cancellation ); - assert_eq!( - SandboxConfiguration::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE, - cfg.guest_panic_context_buffer_size - ); assert_eq!( SandboxConfiguration::MIN_MAX_EXECUTION_TIME, cfg.max_initialization_time @@ -487,9 +449,6 @@ mod tests { cfg.set_max_execution_cancel_wait_time(Duration::from_millis( SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION as u64 - 1, )); - cfg.set_guest_panic_context_buffer_size( - SandboxConfiguration::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE - 1, - ); assert_eq!(SandboxConfiguration::MIN_INPUT_SIZE, cfg.input_data_size); assert_eq!(SandboxConfiguration::MIN_OUTPUT_SIZE, cfg.output_data_size); @@ -501,10 +460,6 @@ mod tests { SandboxConfiguration::MIN_MAX_WAIT_FOR_CANCELLATION, cfg.max_wait_for_cancellation ); - assert_eq!( - SandboxConfiguration::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE, - cfg.guest_panic_context_buffer_size - ); } mod proptests { @@ -529,13 +484,6 @@ mod tests { prop_assert_eq!(size, cfg.get_output_data_size()); } - #[test] - fn guest_panic_context_buffer_size(size in SandboxConfiguration::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE..=SandboxConfiguration::MIN_GUEST_PANIC_CONTEXT_BUFFER_SIZE * 10) { - let mut cfg = SandboxConfiguration::default(); - cfg.set_guest_panic_context_buffer_size(size); - prop_assert_eq!(size, cfg.get_guest_panic_context_buffer_size()); - } - #[test] fn max_execution_time(time in SandboxConfiguration::MIN_MAX_EXECUTION_TIME..=SandboxConfiguration::MIN_MAX_EXECUTION_TIME * 10) { let mut cfg = SandboxConfiguration::default(); diff --git a/src/hyperlight_host/src/sandbox/leaked_outb.rs b/src/hyperlight_host/src/sandbox/leaked_outb.rs index 87fc6c344..e989eee8e 100644 --- a/src/hyperlight_host/src/sandbox/leaked_outb.rs +++ b/src/hyperlight_host/src/sandbox/leaked_outb.rs @@ -29,13 +29,19 @@ use crate::mem::shared_mem::GuestSharedMemory; /// /// NOTE: This is not part of the C Hyperlight API , it is intended only to be /// called in proc through a pointer passed to the guest. -extern "win64" fn call_outb(ptr: *mut Arc>, port: u16, data: u64) { +extern "win64" fn call_outb( + ptr: *mut Arc>, + port: u16, + data_ptr: *const u8, + data_len: u64, +) { let outb_handlercaller = unsafe { Box::from_raw(ptr) }; + let slice = unsafe { std::slice::from_raw_parts(data_ptr, data_len as usize) }; let res = outb_handlercaller .try_lock() .map_err(|_| crate::new_error!("Error locking")) .unwrap() - .call(port, data); + .call(port, slice.to_vec()); // TODO, handle the case correctly when res is an error assert!( res.is_ok(), diff --git a/src/hyperlight_host/src/sandbox/outb.rs b/src/hyperlight_host/src/sandbox/outb.rs index 3679cb2e9..4847c5c8c 100644 --- a/src/hyperlight_host/src/sandbox/outb.rs +++ b/src/hyperlight_host/src/sandbox/outb.rs @@ -19,6 +19,7 @@ use std::sync::{Arc, Mutex}; use hyperlight_common::flatbuffer_wrappers::function_types::ParameterValue; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData; +use hyperlight_common::outb::{Exception, OutBAction}; use log::{Level, Record}; use tracing::{instrument, Span}; use tracing_log::format_trace; @@ -30,25 +31,6 @@ use crate::mem::mgr::SandboxMemoryManager; use crate::mem::shared_mem::HostSharedMemory; use crate::{new_error, HyperlightError, Result}; -pub(super) enum OutBAction { - Log, - CallFunction, - Abort, -} - -impl TryFrom for OutBAction { - type Error = HyperlightError; - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn try_from(val: u16) -> Result { - match val { - 99 => Ok(OutBAction::Log), - 101 => Ok(OutBAction::CallFunction), - 102 => Ok(OutBAction::Abort), - _ => Err(new_error!("Invalid OutB value: {}", val)), - } - } -} - #[instrument(err(Debug), skip_all, parent = Span::current(), level="Trace")] pub(super) fn outb_log(mgr: &mut SandboxMemoryManager) -> Result<()> { // This code will create either a logging record or a tracing record for the GuestLogData depending on if the host has set up a tracing subscriber. @@ -117,7 +99,7 @@ fn handle_outb_impl( mem_mgr: &mut MemMgrWrapper, host_funcs: Arc>, port: u16, - byte: u64, + data: Vec, ) -> Result<()> { match port.try_into()? { OutBAction::Log => outb_log(mem_mgr.as_mut()), @@ -136,23 +118,31 @@ fn handle_outb_impl( Ok(()) } OutBAction::Abort => { + let byte = u64::from(data[0]); let guest_error = ErrorCode::from(byte); - let panic_context = mem_mgr.as_mut().read_guest_panic_context_data()?; - // trim off trailing \0 bytes if they exist - let index_opt = panic_context.iter().position(|&x| x == 0x00); - let trimmed = match index_opt { - Some(n) => &panic_context[0..n], - None => &panic_context, - }; - let s = String::from_utf8_lossy(trimmed); + match guest_error { ErrorCode::StackOverflow => Err(HyperlightError::StackOverflow()), - _ => Err(HyperlightError::GuestAborted( - byte as u8, - s.trim().to_string(), - )), + _ => { + let message = match data.get(1) { + Some(&exception_code) => match Exception::try_from(exception_code) { + Ok(exception) => format!("Exception: {:?}", exception), + Err(e) => { + format!("Unknown exception code: {:#x} ({})", exception_code, e) + } + }, + None => "See stderr for panic context".into(), + }; + + Err(HyperlightError::GuestAborted(byte as u8, message)) + } } } + OutBAction::DebugPrint => { + let s = String::from_utf8_lossy(&data); + eprint!("{}", s); + Ok(()) + } } } diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index 6359909b0..03e4ee8f5 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -63,7 +63,7 @@ fn guest_abort() { .unwrap_err(); println!("{:?}", res); assert!( - matches!(res, HyperlightError::GuestAborted(code, message) if (code == error_code && message.is_empty()) ) + matches!(res, HyperlightError::GuestAborted(code, message) if (code == error_code && message.contains("NoException")) ) ); } @@ -83,7 +83,7 @@ fn guest_abort_with_context1() { .unwrap_err(); println!("{:?}", res); assert!( - matches!(res, HyperlightError::GuestAborted(code, context) if (code == 25 && context == "Oh no")) + matches!(res, HyperlightError::GuestAborted(code, context) if (code == 25 && context.contains("NoException"))) ); } @@ -135,7 +135,7 @@ fn guest_abort_with_context2() { .unwrap_err(); println!("{:?}", res); assert!( - matches!(res, HyperlightError::GuestAborted(_, context) if context.contains(&abort_message[..400])) + matches!(res, HyperlightError::GuestAborted(_, context) if context.contains("NoException")) ); } @@ -161,7 +161,7 @@ fn guest_abort_c_guest() { .unwrap_err(); println!("{:?}", res); assert!( - matches!(res, HyperlightError::GuestAborted(code, message) if (code == 75 && message == "This is a test error message")) + matches!(res, HyperlightError::GuestAborted(code, message) if (code == 75 && message.contains("NoException")) ) ); } @@ -181,7 +181,7 @@ fn guest_panic() { .unwrap_err(); println!("{:?}", res); assert!( - matches!(res, HyperlightError::GuestAborted(code, context) if code == ErrorCode::UnknownError as u8 && context.contains("\nError... error...")) + matches!(res, HyperlightError::GuestAborted(code, context) if code == ErrorCode::UnknownError as u8 && context.contains("NoException")) ) } @@ -262,7 +262,7 @@ fn guest_malloc_abort() { assert!(matches!( res.unwrap_err(), // OOM memory errors in rust allocator are panics. Our panic handler returns ErrorCode::UnknownError on panic - HyperlightError::GuestAborted(code, msg) if code == ErrorCode::UnknownError as u8 && msg.contains("memory allocation of ") + HyperlightError::GuestAborted(code, msg) if code == ErrorCode::UnknownError as u8 && msg.contains("NoException") )); } @@ -406,7 +406,7 @@ fn execute_on_stack() { let err = result.to_string(); assert!( // exception that indicates a page fault - err.contains("EXCEPTION: 0xe") + err.contains("PageFault") ); } } @@ -427,7 +427,7 @@ fn execute_on_heap() { assert!(result.is_err()); let err = result.unwrap_err(); - assert!(err.to_string().contains("EXCEPTION: 0xe")); + assert!(err.to_string().contains("PageFault")); } } diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index 2ce10f1fa..1d73d7d7f 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -530,7 +530,7 @@ fn spin(_: &FunctionCall) -> Result> { fn test_abort(function_call: &FunctionCall) -> Result> { if let ParameterValue::Int(code) = function_call.parameters.clone().unwrap()[0].clone() { - abort_with_code(code); + abort_with_code(&[code as u8]); } Ok(get_flatbuffer_result(())) } @@ -541,7 +541,7 @@ fn test_abort_with_code_and_message(function_call: &FunctionCall) -> Result