From 3ebb69ad7cda14667e92ced64e4122f744406a7c Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 11:48:49 -0700 Subject: [PATCH 01/25] Update cargo hash in flake.nix Signed-off-by: adamperlin --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index fb6de20dd..299fc4778 100644 --- a/flake.nix +++ b/flake.nix @@ -107,7 +107,7 @@ pname = "hyperlight"; version = "0.0.0"; src = lib.cleanSource ./.; - cargoHash = "sha256-mNKnsaSKVz4khzWO7VhmN0cR+Ed5ML7fD1PJJCeQQ6E="; + cargoHash = "sha256-VRyh3mSF9Fms6rt4usSQGHxzmQleqbomd/KAsG9m6DY="; nativeBuildInputs = [ azure-cli From 7ab4735b676e2220054a8d120dbeeb042b15241d Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 28 Aug 2025 15:09:53 -0700 Subject: [PATCH 02/25] Remove allocations in panic handler by formatting panic message using a new FixedStringBuf type backed by a static mut array. Adds a test to verify that StackOverflow no longer occurs on OOM panic Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 63 +++++++++++++++++++ src/hyperlight_common/src/lib.rs | 2 + src/hyperlight_guest_bin/src/lib.rs | 36 +++++++++-- src/hyperlight_host/tests/integration_test.rs | 30 +++++++++ src/tests/rust_guests/simpleguest/src/main.rs | 22 +++++++ 5 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 src/hyperlight_common/src/fixed_buf.rs diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs new file mode 100644 index 000000000..49b7881d2 --- /dev/null +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -0,0 +1,63 @@ +use core::fmt; +use core::result::Result; +use core::ffi::{CStr, FromBytesUntilNulError}; + +pub struct FixedStringBuf<'a> { + pub buf: &'a mut [u8], + pub pos: usize, +} + +impl<'a> fmt::Write for FixedStringBuf<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + // we always reserve 1 byte for the null terminator, + // as the buffer must be convertible to a CStr. + let buf_end = self.buf.len() - 1; + if self.pos + s.len() > buf_end { + return Err(fmt::Error) + } + + self.buf[self.pos..self.pos + s.len()].copy_from_slice(s.as_bytes()); + self.pos += s.len(); + Ok(()) + } +} + +impl <'a> FixedStringBuf<'a> { + pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> { + core::str::from_utf8(&self.buf[..self.pos]) + } + + pub fn new(buf: &'a mut [u8]) -> FixedStringBuf<'a> { + assert!(buf.len() > 0); + FixedStringBuf { + buf, + pos: 0, + } + } + + pub fn as_c_str(&mut self) -> Result<&CStr, FromBytesUntilNulError> { + // null terminate buffer. + // we are guaranteed to have enough space since we always reserve one extra + // byte for null in write_str + self.buf[self.pos] = 0; + CStr::from_bytes_until_nul(&self.buf[..self.pos + 1]) + } +} + +mod test { + use super::{FixedStringBuf}; + use core::fmt::Write; + use core::fmt; + #[test] + fn test_fixed_buf() { + let mut bs = [0; 21]; + let mut buf = FixedStringBuf::new(&mut bs); + write!(&mut buf, "{}", "0123456789").expect("Failed to write to FixedBuf"); + write!(&mut buf, "{}", "0123456789").expect("Failed to write to FixedBuf"); + assert_eq!(buf.as_str().unwrap(), "01234567890123456789"); + assert_eq!(buf.pos, 20); + + let res = write!(&mut buf, "10"); + assert_eq!(res, Err(fmt::Error)); + } +} \ No newline at end of file diff --git a/src/hyperlight_common/src/lib.rs b/src/hyperlight_common/src/lib.rs index e22cf6417..eb5cd7cee 100644 --- a/src/hyperlight_common/src/lib.rs +++ b/src/hyperlight_common/src/lib.rs @@ -42,3 +42,5 @@ pub mod outb; /// cbindgen:ignore pub mod resource; + +pub mod fixed_buf; diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 27918ad3f..36caaf8aa 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -18,8 +18,9 @@ limitations under the License. // === Dependencies === extern crate alloc; -use alloc::string::ToString; +use core::fmt::{Write}; +use alloc::string::ToString; use buddy_system_allocator::LockedHeap; #[cfg(target_arch = "x86_64")] use exceptions::{gdt::load_gdt, idtr::load_idt}; @@ -30,11 +31,12 @@ use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::mem::HyperlightPEB; #[cfg(feature = "mem_profile")] use hyperlight_common::outb::OutBAction; +use hyperlight_common::fixed_buf::{FixedStringBuf}; use hyperlight_guest::exit::{abort_with_code_and_message, halt}; use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest_tracing::{trace, trace_function}; use log::LevelFilter; -use spin::Once; +use spin::{Once}; // === Modules === #[cfg(target_arch = "x86_64")] @@ -139,7 +141,33 @@ pub static mut OS_PAGE_SIZE: u32 = 0; // to satisfy the clippy when cfg == test #[allow(dead_code)] fn panic(info: &core::panic::PanicInfo) -> ! { - let msg = info.to_string(); + _panic_handler(info) +} + + +static mut PANIC_MSG: [u8; 512] = [0u8; 512]; + +#[allow(static_mut_refs)] +static mut PANIC_BUF: FixedStringBuf = FixedStringBuf{ + buf: unsafe { &mut PANIC_MSG }, + pos: 0, +}; + + +#[inline(always)] +#[allow(static_mut_refs)] +fn _panic_handler(info: &core::panic::PanicInfo) -> ! { + let panic_buf: &mut FixedStringBuf = unsafe { &mut PANIC_BUF }; + write!(panic_buf, "{}", info).expect("panic: message format failed"); + + // create a CStr from the underlying array in panic_buf. + // Note that we do NOT use CString here to avoid allocating + let c_string = panic_buf.as_c_str().expect("panic: failed to convert to CStr"); + unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string.as_ptr()) } +} + +fn _panic_handler_old(info: &core::panic::PanicInfo) { +let msg = info.to_string(); let c_string = alloc::ffi::CString::new(msg) .unwrap_or_else(|_| alloc::ffi::CString::new("panic (invalid utf8)").unwrap()); @@ -213,4 +241,4 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve }); halt(); -} +} \ No newline at end of file diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index eceabd479..76ec387a3 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -533,6 +533,36 @@ fn guest_malloc_abort() { )); } +#[test] +fn guest_panic_no_alloc() { + let heap_size = 0x4000; + + let mut cfg = SandboxConfiguration::default(); + cfg.set_heap_size(heap_size); + let uninit = UninitializedSandbox::new( + GuestBinary::FilePath(simple_guest_as_string().unwrap()), + Some(cfg), + ) + .unwrap(); + let mut sbox: MultiUseSandbox = uninit.evolve().unwrap(); + + let res = sbox.call::( + "ExhaustHeap", // uses the rust allocator to allocate small blocks on the heap until OOM + () + ).unwrap_err(); + println!("{:?}", res); + + if let HyperlightError::StackOverflow() = res { + panic!("panic on OOM caused stack overflow, this implies allocation in panic handler"); + } + + assert!(matches!( + res, + HyperlightError::GuestAborted(code, msg) if code == ErrorCode::UnknownError as u8 && msg.contains("memory allocation of ") && msg.contains("bytes failed") + )); + +} + // Tests libc alloca #[test] fn dynamic_stack_allocate_c_guest() { diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index c3b17ad58..9603cae51 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -29,9 +29,11 @@ use alloc::boxed::Box; use alloc::string::ToString; use alloc::vec::Vec; use alloc::{format, vec}; +use core::alloc::Layout; use core::ffi::c_char; use core::hint::black_box; use core::ptr::write_volatile; +use core::sync::atomic::AtomicPtr; use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType}; use hyperlight_common::flatbuffer_wrappers::function_types::{ @@ -507,6 +509,18 @@ fn call_malloc(function_call: &FunctionCall) -> Result> { } } +#[hyperlight_guest_tracing::trace_function] +unsafe fn exhaust_heap(_: &FunctionCall) -> ! { + let layout: Layout = Layout::new::(); + let mut ptr = alloc::alloc::alloc_zeroed(layout); + while !ptr.is_null() { + black_box(ptr); + ptr = alloc::alloc::alloc_zeroed(layout); + } + + panic!("function should have panicked before due to OOM") +} + #[hyperlight_guest_tracing::trace_function] fn malloc_and_free(function_call: &FunctionCall) -> Result> { if let ParameterValue::Int(size) = function_call.parameters.clone().unwrap()[0].clone() { @@ -1023,6 +1037,14 @@ pub extern "C" fn hyperlight_main() { ); register_function(call_malloc_def); + let call_malloc_and_panic_def = GuestFunctionDefinition::new( + "ExhaustHeap".to_string(), + Vec::new(), + ReturnType::Int, + exhaust_heap as usize, + ); + register_function(call_malloc_and_panic_def); + let malloc_and_free_def = GuestFunctionDefinition::new( "MallocAndFree".to_string(), Vec::from(&[ParameterType::Int]), From 65d167659ccf2349b1d0b3e33874d5a060e1147e Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 12:55:22 -0700 Subject: [PATCH 03/25] Clear buffer before recursive panics Add some docstrings Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 16 ++++++++-- src/hyperlight_guest_bin/src/lib.rs | 43 ++++++++++++++------------ 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs index 49b7881d2..717ffe2e2 100644 --- a/src/hyperlight_common/src/fixed_buf.rs +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -2,6 +2,9 @@ use core::fmt; use core::result::Result; use core::ffi::{CStr, FromBytesUntilNulError}; +/// FixedStringBuf is a buffer that can hold a fixed-size string. +/// It is meant to be used with a slice that the user has pre-allocated +/// to avoid extra allocations during string formatting. pub struct FixedStringBuf<'a> { pub buf: &'a mut [u8], pub pos: usize, @@ -10,7 +13,7 @@ pub struct FixedStringBuf<'a> { impl<'a> fmt::Write for FixedStringBuf<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { // we always reserve 1 byte for the null terminator, - // as the buffer must be convertible to a CStr. + // as the buffer must be convertible to CStr. let buf_end = self.buf.len() - 1; if self.pos + s.len() > buf_end { return Err(fmt::Error) @@ -35,10 +38,17 @@ impl <'a> FixedStringBuf<'a> { } } + pub fn reset(&mut self) { + self.pos = 0; + } + + /// Null terminates the underlying buffer, + /// and converts to a CStr which borrows the underlying buffer's slice. pub fn as_c_str(&mut self) -> Result<&CStr, FromBytesUntilNulError> { - // null terminate buffer. + // null terminate the buffer. // we are guaranteed to have enough space since we always reserve one extra - // byte for null in write_str + // byte for null in write_str, and assert buf.len() > 0 in the constructor. + assert!(self.buf.len() > 0 && self.pos < self.buf.len()); self.buf[self.pos] = 0; CStr::from_bytes_until_nul(&self.buf[..self.pos + 1]) } diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 36caaf8aa..1b1f060b4 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -36,7 +36,7 @@ use hyperlight_guest::exit::{abort_with_code_and_message, halt}; use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest_tracing::{trace, trace_function}; use log::LevelFilter; -use spin::{Once}; +use spin::{Once, Mutex, MutexGuard}; // === Modules === #[cfg(target_arch = "x86_64")] @@ -144,34 +144,37 @@ fn panic(info: &core::panic::PanicInfo) -> ! { _panic_handler(info) } - static mut PANIC_MSG: [u8; 512] = [0u8; 512]; #[allow(static_mut_refs)] -static mut PANIC_BUF: FixedStringBuf = FixedStringBuf{ +static PANIC_BUF: Mutex = Mutex::new(FixedStringBuf{ buf: unsafe { &mut PANIC_MSG }, pos: 0, -}; - +}); #[inline(always)] -#[allow(static_mut_refs)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { - let panic_buf: &mut FixedStringBuf = unsafe { &mut PANIC_BUF }; - write!(panic_buf, "{}", info).expect("panic: message format failed"); - - // create a CStr from the underlying array in panic_buf. - // Note that we do NOT use CString here to avoid allocating - let c_string = panic_buf.as_c_str().expect("panic: failed to convert to CStr"); - unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string.as_ptr()) } -} - -fn _panic_handler_old(info: &core::panic::PanicInfo) { -let msg = info.to_string(); - let c_string = alloc::ffi::CString::new(msg) - .unwrap_or_else(|_| alloc::ffi::CString::new("panic (invalid utf8)").unwrap()); + let mut panic_buf_guard: MutexGuard<'_, FixedStringBuf<'static>> = PANIC_BUF.lock(); + let write_res = write!(panic_buf_guard, "{}", info); + if let Err(_) = write_res { + // reset the buffer to ensure there is space + // for the new panic message below + panic_buf_guard.reset(); + panic!("panic: message format failed"); + } - unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string.as_ptr()) } + // create a CStr from the underlying array in PANIC_BUF using the as_cstr method. + // this wraps CStr::from_bytes_until_nul which takes a borrowed byte slice + // and does not allocate. + let c_string_res = panic_buf_guard.as_c_str(); + if let Err(_) = c_string_res { + // reset the buffer here as well, to ensure there is space + // in the buffer to write the new panic message below. + panic_buf_guard.reset(); + panic!("panic: failed to convert to CStr"); + } + + unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string_res.unwrap().as_ptr()) } } // === Entrypoint === From 6a3b88712e81b85feecddd5e0432a22a234c5cf2 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 14:07:12 -0700 Subject: [PATCH 04/25] Update cargoHash in flake.nix Signed-off-by: adamperlin --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 299fc4778..d700857ac 100644 --- a/flake.nix +++ b/flake.nix @@ -107,7 +107,7 @@ pname = "hyperlight"; version = "0.0.0"; src = lib.cleanSource ./.; - cargoHash = "sha256-VRyh3mSF9Fms6rt4usSQGHxzmQleqbomd/KAsG9m6DY="; + cargoHash = "sha256-hoeJEBdxaoyLlhQQ4X4Wk5X1QVtQ7RRQYaxkiGg8rWA="; nativeBuildInputs = [ azure-cli From b7d9e400a78c4b4f9c4eb959a69e4926d0eaaaa9 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 14:15:29 -0700 Subject: [PATCH 05/25] Remove unused imports Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs index 717ffe2e2..c457a08ed 100644 --- a/src/hyperlight_common/src/fixed_buf.rs +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -55,9 +55,9 @@ impl <'a> FixedStringBuf<'a> { } mod test { - use super::{FixedStringBuf}; - use core::fmt::Write; - use core::fmt; + + + #[test] fn test_fixed_buf() { let mut bs = [0; 21]; From 33450d3787010ad0566bedec2a62a3232a8babaa Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 14:39:52 -0700 Subject: [PATCH 06/25] Add copyright header to fixed_buf.rrs Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs index c457a08ed..af8e026c7 100644 --- a/src/hyperlight_common/src/fixed_buf.rs +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -1,3 +1,19 @@ +/* +Copyright 2025 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use core::fmt; use core::result::Result; use core::ffi::{CStr, FromBytesUntilNulError}; From f96c81e76695f698b0fac624c0a64e7c5d738e7d Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 14:47:01 -0700 Subject: [PATCH 07/25] Call abort_with_code_and_message directly in case of format failure in panic handler to avoid any possible recursive panic Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 1b1f060b4..fa930ec98 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -157,10 +157,7 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { let mut panic_buf_guard: MutexGuard<'_, FixedStringBuf<'static>> = PANIC_BUF.lock(); let write_res = write!(panic_buf_guard, "{}", info); if let Err(_) = write_res { - // reset the buffer to ensure there is space - // for the new panic message below - panic_buf_guard.reset(); - panic!("panic: message format failed"); + unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], b"panic: message format failed\0".as_ptr() as *const i8)} } // create a CStr from the underlying array in PANIC_BUF using the as_cstr method. @@ -168,10 +165,7 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { // and does not allocate. let c_string_res = panic_buf_guard.as_c_str(); if let Err(_) = c_string_res { - // reset the buffer here as well, to ensure there is space - // in the buffer to write the new panic message below. - panic_buf_guard.reset(); - panic!("panic: failed to convert to CStr"); + unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], b"panic: failed to convert to CStr\0".as_ptr() as *const i8)} } unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string_res.unwrap().as_ptr()) } From 6754a7ec30d47978359c6468aa4f73b9d093793c Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Fri, 22 Aug 2025 14:48:57 -0700 Subject: [PATCH 08/25] Update src/hyperlight_host/tests/integration_test.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Adam Perlin --- src/hyperlight_host/tests/integration_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index 76ec387a3..fc6248127 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -548,7 +548,7 @@ fn guest_panic_no_alloc() { let res = sbox.call::( "ExhaustHeap", // uses the rust allocator to allocate small blocks on the heap until OOM - () + () ).unwrap_err(); println!("{:?}", res); From 4b13017b45156eecdb75fb9afa12d3d797ca427d Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Fri, 22 Aug 2025 14:49:25 -0700 Subject: [PATCH 09/25] Update src/tests/rust_guests/simpleguest/src/main.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Adam Perlin --- src/tests/rust_guests/simpleguest/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index 9603cae51..cb78afa6d 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -33,7 +33,6 @@ use core::alloc::Layout; use core::ffi::c_char; use core::hint::black_box; use core::ptr::write_volatile; -use core::sync::atomic::AtomicPtr; use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType}; use hyperlight_common::flatbuffer_wrappers::function_types::{ From abe4d0f53209a9bc5ad5af0212cc0734f83a36a2 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 22 Aug 2025 15:42:59 -0700 Subject: [PATCH 10/25] Make FixedBuf take ownership of underlying byte slice Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 46 ++++++++++++++------------ src/hyperlight_guest_bin/src/lib.rs | 10 ++---- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs index af8e026c7..5bc177684 100644 --- a/src/hyperlight_common/src/fixed_buf.rs +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -16,17 +16,17 @@ limitations under the License. use core::fmt; use core::result::Result; -use core::ffi::{CStr, FromBytesUntilNulError}; -/// FixedStringBuf is a buffer that can hold a fixed-size string. + +/// FixedStringBuf is a buffer that can hold a fixed-size string of capacity N. /// It is meant to be used with a slice that the user has pre-allocated /// to avoid extra allocations during string formatting. -pub struct FixedStringBuf<'a> { - pub buf: &'a mut [u8], +pub struct FixedStringBuf { + pub buf: [u8; N], pub pos: usize, } -impl<'a> fmt::Write for FixedStringBuf<'a> { +impl<'a, const N: usize> fmt::Write for FixedStringBuf { fn write_str(&mut self, s: &str) -> fmt::Result { // we always reserve 1 byte for the null terminator, // as the buffer must be convertible to CStr. @@ -41,43 +41,44 @@ impl<'a> fmt::Write for FixedStringBuf<'a> { } } -impl <'a> FixedStringBuf<'a> { +impl FixedStringBuf { pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> { core::str::from_utf8(&self.buf[..self.pos]) } - pub fn new(buf: &'a mut [u8]) -> FixedStringBuf<'a> { - assert!(buf.len() > 0); - FixedStringBuf { - buf, + pub const fn new() -> Self { + return FixedStringBuf{ + buf: [0u8; N], pos: 0, } } - pub fn reset(&mut self) { - self.pos = 0; - } - /// Null terminates the underlying buffer, /// and converts to a CStr which borrows the underlying buffer's slice. - pub fn as_c_str(&mut self) -> Result<&CStr, FromBytesUntilNulError> { + pub fn as_c_str(&mut self) -> Result<&core::ffi::CStr, core::ffi::FromBytesUntilNulError> { // null terminate the buffer. // we are guaranteed to have enough space since we always reserve one extra // byte for null in write_str, and assert buf.len() > 0 in the constructor. assert!(self.buf.len() > 0 && self.pos < self.buf.len()); self.buf[self.pos] = 0; - CStr::from_bytes_until_nul(&self.buf[..self.pos + 1]) + core::ffi::CStr::from_bytes_until_nul(&self.buf[..self.pos + 1]) } } + mod test { - - - + // disable unused import warnings + #![allow(unused_imports)] + use core::fmt::Write; + use core::fmt; + use super::FixedStringBuf; + #[test] fn test_fixed_buf() { - let mut bs = [0; 21]; - let mut buf = FixedStringBuf::new(&mut bs); + let mut buf = FixedStringBuf::<21>::new(); + + assert_eq!(buf.as_str().unwrap(), ""); + write!(&mut buf, "{}", "0123456789").expect("Failed to write to FixedBuf"); write!(&mut buf, "{}", "0123456789").expect("Failed to write to FixedBuf"); assert_eq!(buf.as_str().unwrap(), "01234567890123456789"); @@ -85,5 +86,8 @@ mod test { let res = write!(&mut buf, "10"); assert_eq!(res, Err(fmt::Error)); + + let c_str = buf.as_c_str().unwrap(); + assert_eq!(c_str.to_bytes(), b"01234567890123456789"); } } \ No newline at end of file diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index fa930ec98..3207e2992 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -144,17 +144,11 @@ fn panic(info: &core::panic::PanicInfo) -> ! { _panic_handler(info) } -static mut PANIC_MSG: [u8; 512] = [0u8; 512]; - -#[allow(static_mut_refs)] -static PANIC_BUF: Mutex = Mutex::new(FixedStringBuf{ - buf: unsafe { &mut PANIC_MSG }, - pos: 0, -}); +static PANIC_BUF: Mutex> = Mutex::new(FixedStringBuf::new()); #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { - let mut panic_buf_guard: MutexGuard<'_, FixedStringBuf<'static>> = PANIC_BUF.lock(); + let mut panic_buf_guard = PANIC_BUF.lock(); let write_res = write!(panic_buf_guard, "{}", info); if let Err(_) = write_res { unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], b"panic: message format failed\0".as_ptr() as *const i8)} From a5ba136d9d1b12a042d4e75a78b05c5240778159 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 25 Aug 2025 10:11:04 -0700 Subject: [PATCH 11/25] Apply cargo fmt Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 19 +++++----- src/hyperlight_guest_bin/src/lib.rs | 35 +++++++++++++------ src/hyperlight_host/tests/integration_test.rs | 11 +++--- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs index 5bc177684..539f56480 100644 --- a/src/hyperlight_common/src/fixed_buf.rs +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -17,7 +17,6 @@ limitations under the License. use core::fmt; use core::result::Result; - /// FixedStringBuf is a buffer that can hold a fixed-size string of capacity N. /// It is meant to be used with a slice that the user has pre-allocated /// to avoid extra allocations during string formatting. @@ -28,11 +27,11 @@ pub struct FixedStringBuf { impl<'a, const N: usize> fmt::Write for FixedStringBuf { fn write_str(&mut self, s: &str) -> fmt::Result { - // we always reserve 1 byte for the null terminator, + // we always reserve 1 byte for the null terminator, // as the buffer must be convertible to CStr. let buf_end = self.buf.len() - 1; if self.pos + s.len() > buf_end { - return Err(fmt::Error) + return Err(fmt::Error); } self.buf[self.pos..self.pos + s.len()].copy_from_slice(s.as_bytes()); @@ -41,22 +40,22 @@ impl<'a, const N: usize> fmt::Write for FixedStringBuf { } } -impl FixedStringBuf { +impl FixedStringBuf { pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> { core::str::from_utf8(&self.buf[..self.pos]) } pub const fn new() -> Self { - return FixedStringBuf{ + return FixedStringBuf { buf: [0u8; N], pos: 0, - } + }; } /// Null terminates the underlying buffer, /// and converts to a CStr which borrows the underlying buffer's slice. pub fn as_c_str(&mut self) -> Result<&core::ffi::CStr, core::ffi::FromBytesUntilNulError> { - // null terminate the buffer. + // null terminate the buffer. // we are guaranteed to have enough space since we always reserve one extra // byte for null in write_str, and assert buf.len() > 0 in the constructor. assert!(self.buf.len() > 0 && self.pos < self.buf.len()); @@ -65,12 +64,12 @@ impl FixedStringBuf { } } - mod test { // disable unused import warnings #![allow(unused_imports)] - use core::fmt::Write; use core::fmt; + use core::fmt::Write; + use super::FixedStringBuf; #[test] @@ -90,4 +89,4 @@ mod test { let c_str = buf.as_c_str().unwrap(); assert_eq!(c_str.to_bytes(), b"01234567890123456789"); } -} \ No newline at end of file +} diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 3207e2992..5372fc85b 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -18,25 +18,25 @@ limitations under the License. // === Dependencies === extern crate alloc; -use core::fmt::{Write}; - use alloc::string::ToString; +use core::fmt::Write; + use buddy_system_allocator::LockedHeap; #[cfg(target_arch = "x86_64")] use exceptions::{gdt::load_gdt, idtr::load_idt}; use guest_function::call::dispatch_function; use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; +use hyperlight_common::fixed_buf::FixedStringBuf; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::mem::HyperlightPEB; #[cfg(feature = "mem_profile")] use hyperlight_common::outb::OutBAction; -use hyperlight_common::fixed_buf::{FixedStringBuf}; use hyperlight_guest::exit::{abort_with_code_and_message, halt}; use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest_tracing::{trace, trace_function}; use log::LevelFilter; -use spin::{Once, Mutex, MutexGuard}; +use spin::{Mutex, MutexGuard, Once}; // === Modules === #[cfg(target_arch = "x86_64")] @@ -151,18 +151,33 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { let mut panic_buf_guard = PANIC_BUF.lock(); let write_res = write!(panic_buf_guard, "{}", info); if let Err(_) = write_res { - unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], b"panic: message format failed\0".as_ptr() as *const i8)} + unsafe { + abort_with_code_and_message( + &[ErrorCode::UnknownError as u8], + b"panic: message format failed\0".as_ptr() as *const i8, + ) + } } // create a CStr from the underlying array in PANIC_BUF using the as_cstr method. // this wraps CStr::from_bytes_until_nul which takes a borrowed byte slice - // and does not allocate. + // and does not allocate. let c_string_res = panic_buf_guard.as_c_str(); if let Err(_) = c_string_res { - unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], b"panic: failed to convert to CStr\0".as_ptr() as *const i8)} + unsafe { + abort_with_code_and_message( + &[ErrorCode::UnknownError as u8], + b"panic: failed to convert to CStr\0".as_ptr() as *const i8, + ) + } + } + + unsafe { + abort_with_code_and_message( + &[ErrorCode::UnknownError as u8], + c_string_res.unwrap().as_ptr(), + ) } - - unsafe { abort_with_code_and_message(&[ErrorCode::UnknownError as u8], c_string_res.unwrap().as_ptr()) } } // === Entrypoint === @@ -232,4 +247,4 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve }); halt(); -} \ No newline at end of file +} diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index fc6248127..2d98e4428 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -546,10 +546,12 @@ fn guest_panic_no_alloc() { .unwrap(); let mut sbox: MultiUseSandbox = uninit.evolve().unwrap(); - let res = sbox.call::( - "ExhaustHeap", // uses the rust allocator to allocate small blocks on the heap until OOM - () - ).unwrap_err(); + let res = sbox + .call::( + "ExhaustHeap", // uses the rust allocator to allocate small blocks on the heap until OOM + (), + ) + .unwrap_err(); println!("{:?}", res); if let HyperlightError::StackOverflow() = res { @@ -560,7 +562,6 @@ fn guest_panic_no_alloc() { res, HyperlightError::GuestAborted(code, msg) if code == ErrorCode::UnknownError as u8 && msg.contains("memory allocation of ") && msg.contains("bytes failed") )); - } // Tests libc alloca From e3cdbc77d94515738f4832d3f00b649272c64cc3 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 25 Aug 2025 10:14:44 -0700 Subject: [PATCH 12/25] Remove unused import Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 5372fc85b..363cb574f 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -36,7 +36,7 @@ use hyperlight_guest::exit::{abort_with_code_and_message, halt}; use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest_tracing::{trace, trace_function}; use log::LevelFilter; -use spin::{Mutex, MutexGuard, Once}; +use spin::{Mutex, Once}; // === Modules === #[cfg(target_arch = "x86_64")] From afb1ee59cc91178158bbaa38baf6c4dffe532715 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 25 Aug 2025 14:43:02 -0700 Subject: [PATCH 13/25] 1. Add vec allocation after alloc::alloc failure to ensure panic in integration test 2. Resolve clippy errors Signed-off-by: adamperlin --- src/hyperlight_common/src/fixed_buf.rs | 18 ++++++++++++------ src/hyperlight_guest_bin/src/lib.rs | 9 ++++----- src/tests/rust_guests/simpleguest/src/main.rs | 5 +++++ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs index 539f56480..da586dc35 100644 --- a/src/hyperlight_common/src/fixed_buf.rs +++ b/src/hyperlight_common/src/fixed_buf.rs @@ -25,7 +25,7 @@ pub struct FixedStringBuf { pub pos: usize, } -impl<'a, const N: usize> fmt::Write for FixedStringBuf { +impl fmt::Write for FixedStringBuf { fn write_str(&mut self, s: &str) -> fmt::Result { // we always reserve 1 byte for the null terminator, // as the buffer must be convertible to CStr. @@ -40,16 +40,22 @@ impl<'a, const N: usize> fmt::Write for FixedStringBuf { } } +impl Default for FixedStringBuf { + fn default() -> Self { + FixedStringBuf::::new() + } +} + impl FixedStringBuf { pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> { core::str::from_utf8(&self.buf[..self.pos]) } pub const fn new() -> Self { - return FixedStringBuf { + FixedStringBuf { buf: [0u8; N], pos: 0, - }; + } } /// Null terminates the underlying buffer, @@ -58,7 +64,7 @@ impl FixedStringBuf { // null terminate the buffer. // we are guaranteed to have enough space since we always reserve one extra // byte for null in write_str, and assert buf.len() > 0 in the constructor. - assert!(self.buf.len() > 0 && self.pos < self.buf.len()); + assert!(!self.buf.is_empty() && self.pos < self.buf.len()); self.buf[self.pos] = 0; core::ffi::CStr::from_bytes_until_nul(&self.buf[..self.pos + 1]) } @@ -78,8 +84,8 @@ mod test { assert_eq!(buf.as_str().unwrap(), ""); - write!(&mut buf, "{}", "0123456789").expect("Failed to write to FixedBuf"); - write!(&mut buf, "{}", "0123456789").expect("Failed to write to FixedBuf"); + write!(&mut buf, "0123456789").expect("Failed to write to FixedBuf"); + write!(&mut buf, "0123456789").expect("Failed to write to FixedBuf"); assert_eq!(buf.as_str().unwrap(), "01234567890123456789"); assert_eq!(buf.pos, 20); diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 363cb574f..90b8b89a2 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -18,7 +18,6 @@ limitations under the License. // === Dependencies === extern crate alloc; -use alloc::string::ToString; use core::fmt::Write; use buddy_system_allocator::LockedHeap; @@ -150,11 +149,11 @@ static PANIC_BUF: Mutex> = Mutex::new(FixedStringBuf::new()) fn _panic_handler(info: &core::panic::PanicInfo) -> ! { let mut panic_buf_guard = PANIC_BUF.lock(); let write_res = write!(panic_buf_guard, "{}", info); - if let Err(_) = write_res { + if write_res.is_err() { unsafe { abort_with_code_and_message( &[ErrorCode::UnknownError as u8], - b"panic: message format failed\0".as_ptr() as *const i8, + c"panic: message format failed".as_ptr(), ) } } @@ -163,11 +162,11 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { // this wraps CStr::from_bytes_until_nul which takes a borrowed byte slice // and does not allocate. let c_string_res = panic_buf_guard.as_c_str(); - if let Err(_) = c_string_res { + if c_string_res.is_err() { unsafe { abort_with_code_and_message( &[ErrorCode::UnknownError as u8], - b"panic: failed to convert to CStr\0".as_ptr() as *const i8, + c"panic: failed to convert to CStr".as_ptr(), ) } } diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index cb78afa6d..8b8897cbe 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -517,6 +517,11 @@ unsafe fn exhaust_heap(_: &FunctionCall) -> ! { ptr = alloc::alloc::alloc_zeroed(layout); } + // after alloc::alloc_zeroed failure (null return when called in loop above) + // allocate a Vec to ensure OOM panic + let vec = Vec::::with_capacity(1); + black_box(vec); + panic!("function should have panicked before due to OOM") } From fe3eb063fa62bb63e744441bd0613bb59338bc17 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 25 Aug 2025 22:09:46 -0700 Subject: [PATCH 14/25] Use heapless crate instead of custom fixed string buffer for panic string formatting Signed-off-by: adamperlin --- Cargo.lock | 20 ++++ src/hyperlight_common/src/fixed_buf.rs | 98 ------------------- src/hyperlight_common/src/lib.rs | 4 +- src/hyperlight_guest_bin/Cargo.toml | 1 + src/hyperlight_guest_bin/src/lib.rs | 27 +++-- src/hyperlight_host/tests/integration_test.rs | 1 - .../rust_guests/callbackguest/Cargo.lock | 32 ++++++ src/tests/rust_guests/dummyguest/Cargo.lock | 32 ++++++ src/tests/rust_guests/simpleguest/Cargo.lock | 32 ++++++ src/tests/rust_guests/witguest/Cargo.lock | 32 ++++++ 10 files changed, 168 insertions(+), 111 deletions(-) delete mode 100644 src/hyperlight_common/src/fixed_buf.rs diff --git a/Cargo.lock b/Cargo.lock index 0bd280065..61594f5fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,6 +1235,15 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -1245,6 +1254,16 @@ dependencies = [ "serde", ] +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -1410,6 +1429,7 @@ dependencies = [ "cc", "cfg-if", "glob", + "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", diff --git a/src/hyperlight_common/src/fixed_buf.rs b/src/hyperlight_common/src/fixed_buf.rs deleted file mode 100644 index da586dc35..000000000 --- a/src/hyperlight_common/src/fixed_buf.rs +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright 2025 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use core::fmt; -use core::result::Result; - -/// FixedStringBuf is a buffer that can hold a fixed-size string of capacity N. -/// It is meant to be used with a slice that the user has pre-allocated -/// to avoid extra allocations during string formatting. -pub struct FixedStringBuf { - pub buf: [u8; N], - pub pos: usize, -} - -impl fmt::Write for FixedStringBuf { - fn write_str(&mut self, s: &str) -> fmt::Result { - // we always reserve 1 byte for the null terminator, - // as the buffer must be convertible to CStr. - let buf_end = self.buf.len() - 1; - if self.pos + s.len() > buf_end { - return Err(fmt::Error); - } - - self.buf[self.pos..self.pos + s.len()].copy_from_slice(s.as_bytes()); - self.pos += s.len(); - Ok(()) - } -} - -impl Default for FixedStringBuf { - fn default() -> Self { - FixedStringBuf::::new() - } -} - -impl FixedStringBuf { - pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> { - core::str::from_utf8(&self.buf[..self.pos]) - } - - pub const fn new() -> Self { - FixedStringBuf { - buf: [0u8; N], - pos: 0, - } - } - - /// Null terminates the underlying buffer, - /// and converts to a CStr which borrows the underlying buffer's slice. - pub fn as_c_str(&mut self) -> Result<&core::ffi::CStr, core::ffi::FromBytesUntilNulError> { - // null terminate the buffer. - // we are guaranteed to have enough space since we always reserve one extra - // byte for null in write_str, and assert buf.len() > 0 in the constructor. - assert!(!self.buf.is_empty() && self.pos < self.buf.len()); - self.buf[self.pos] = 0; - core::ffi::CStr::from_bytes_until_nul(&self.buf[..self.pos + 1]) - } -} - -mod test { - // disable unused import warnings - #![allow(unused_imports)] - use core::fmt; - use core::fmt::Write; - - use super::FixedStringBuf; - - #[test] - fn test_fixed_buf() { - let mut buf = FixedStringBuf::<21>::new(); - - assert_eq!(buf.as_str().unwrap(), ""); - - write!(&mut buf, "0123456789").expect("Failed to write to FixedBuf"); - write!(&mut buf, "0123456789").expect("Failed to write to FixedBuf"); - assert_eq!(buf.as_str().unwrap(), "01234567890123456789"); - assert_eq!(buf.pos, 20); - - let res = write!(&mut buf, "10"); - assert_eq!(res, Err(fmt::Error)); - - let c_str = buf.as_c_str().unwrap(); - assert_eq!(c_str.to_bytes(), b"01234567890123456789"); - } -} diff --git a/src/hyperlight_common/src/lib.rs b/src/hyperlight_common/src/lib.rs index eb5cd7cee..913d89aa2 100644 --- a/src/hyperlight_common/src/lib.rs +++ b/src/hyperlight_common/src/lib.rs @@ -41,6 +41,4 @@ pub mod mem; pub mod outb; /// cbindgen:ignore -pub mod resource; - -pub mod fixed_buf; +pub mod resource; \ No newline at end of file diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index c21d00c91..4993b26a5 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -27,6 +27,7 @@ hyperlight-guest-tracing = { workspace = true, default-features = false } buddy_system_allocator = "0.11.0" log = { version = "0.4", default-features = false } spin = "0.10.0" +heapless = "0.9.1" [lints] workspace = true diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 90b8b89a2..bfc295b00 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -18,6 +18,7 @@ limitations under the License. // === Dependencies === extern crate alloc; +use core::ffi::CStr; use core::fmt::Write; use buddy_system_allocator::LockedHeap; @@ -26,7 +27,7 @@ use exceptions::{gdt::load_gdt, idtr::load_idt}; use guest_function::call::dispatch_function; use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; -use hyperlight_common::fixed_buf::FixedStringBuf; +use heapless::String; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::mem::HyperlightPEB; #[cfg(feature = "mem_profile")] @@ -143,7 +144,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { _panic_handler(info) } -static PANIC_BUF: Mutex> = Mutex::new(FixedStringBuf::new()); +static PANIC_BUF: Mutex> = Mutex::new(String::new()); #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { @@ -158,15 +159,23 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { } } - // create a CStr from the underlying array in PANIC_BUF using the as_cstr method. - // this wraps CStr::from_bytes_until_nul which takes a borrowed byte slice - // and does not allocate. - let c_string_res = panic_buf_guard.as_c_str(); - if c_string_res.is_err() { + let append_res = panic_buf_guard.push('\0'); + if append_res.is_err() { unsafe { abort_with_code_and_message( &[ErrorCode::UnknownError as u8], - c"panic: failed to convert to CStr".as_ptr(), + c"panic: message too long".as_ptr(), + ) + } + } + + let c_str_res = CStr::from_bytes_with_nul(panic_buf_guard.as_bytes()); + + if c_str_res.is_err() { + unsafe { + abort_with_code_and_message( + &[ErrorCode::UnknownError as u8], + c"panic: failed to convert to CString".as_ptr(), ) } } @@ -174,7 +183,7 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { unsafe { abort_with_code_and_message( &[ErrorCode::UnknownError as u8], - c_string_res.unwrap().as_ptr(), + c_str_res.unwrap().as_ptr(), ) } } diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index 2d98e4428..004eafc1a 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -552,7 +552,6 @@ fn guest_panic_no_alloc() { (), ) .unwrap_err(); - println!("{:?}", res); if let HyperlightError::StackOverflow() = res { panic!("panic on OOM caused stack overflow, this implies allocation in panic handler"); diff --git a/src/tests/rust_guests/callbackguest/Cargo.lock b/src/tests/rust_guests/callbackguest/Cargo.lock index 1d537c09c..e7cdb93f9 100644 --- a/src/tests/rust_guests/callbackguest/Cargo.lock +++ b/src/tests/rust_guests/callbackguest/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "callbackguest" version = "0.1.0" @@ -70,6 +76,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "hyperlight-common" version = "0.8.0" @@ -99,6 +124,7 @@ dependencies = [ "cc", "cfg-if", "glob", + "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -253,6 +279,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "2.0.106" diff --git a/src/tests/rust_guests/dummyguest/Cargo.lock b/src/tests/rust_guests/dummyguest/Cargo.lock index 77734d32c..cbd373e1a 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.lock +++ b/src/tests/rust_guests/dummyguest/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.2.34" @@ -68,6 +74,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "hyperlight-common" version = "0.8.0" @@ -97,6 +122,7 @@ dependencies = [ "cc", "cfg-if", "glob", + "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -251,6 +277,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "2.0.106" diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index 9a9f82278..aa4aecd68 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.2.34" @@ -60,6 +66,25 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "hyperlight-common" version = "0.8.0" @@ -89,6 +114,7 @@ dependencies = [ "cc", "cfg-if", "glob", + "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -254,6 +280,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "2.0.106" diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index 5687337e0..119dca4b4 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -88,6 +88,12 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.2.34" @@ -166,6 +172,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -176,6 +191,16 @@ dependencies = [ "serde", ] +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "hyperlight-common" version = "0.8.0" @@ -232,6 +257,7 @@ dependencies = [ "cc", "cfg-if", "glob", + "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -496,6 +522,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "2.0.106" From 51269c7bfef6cd105f99f425dcfcbd1cec776d58 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 26 Aug 2025 09:57:09 -0700 Subject: [PATCH 15/25] Downgrade heapless to 0.8.0 to fix cargo crash Signed-off-by: adamperlin --- Cargo.lock | 4 ++-- src/hyperlight_guest_bin/Cargo.toml | 2 +- src/tests/rust_guests/callbackguest/Cargo.lock | 4 ++-- src/tests/rust_guests/dummyguest/Cargo.lock | 4 ++-- src/tests/rust_guests/simpleguest/Cargo.lock | 4 ++-- src/tests/rust_guests/witguest/Cargo.lock | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61594f5fe..a64bd5db3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,9 +1256,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32", "stable_deref_trait", diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index 4993b26a5..e3e102c60 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -27,7 +27,7 @@ hyperlight-guest-tracing = { workspace = true, default-features = false } buddy_system_allocator = "0.11.0" log = { version = "0.4", default-features = false } spin = "0.10.0" -heapless = "0.9.1" +heapless = "0.8.0" [lints] workspace = true diff --git a/src/tests/rust_guests/callbackguest/Cargo.lock b/src/tests/rust_guests/callbackguest/Cargo.lock index e7cdb93f9..78bb01aa2 100644 --- a/src/tests/rust_guests/callbackguest/Cargo.lock +++ b/src/tests/rust_guests/callbackguest/Cargo.lock @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32", "stable_deref_trait", diff --git a/src/tests/rust_guests/dummyguest/Cargo.lock b/src/tests/rust_guests/dummyguest/Cargo.lock index cbd373e1a..1f52f0ac2 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.lock +++ b/src/tests/rust_guests/dummyguest/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32", "stable_deref_trait", diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index aa4aecd68..377100d05 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32", "stable_deref_trait", diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index 119dca4b4..6aeb4b712 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -193,9 +193,9 @@ dependencies = [ [[package]] name = "heapless" -version = "0.9.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ "hash32", "stable_deref_trait", From 246518c8d84c7512985d1de46ab74d3a6ac01990 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 26 Aug 2025 10:22:31 -0700 Subject: [PATCH 16/25] apply cargo fmt Signed-off-by: adamperlin --- src/hyperlight_common/src/lib.rs | 2 +- src/hyperlight_guest_bin/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hyperlight_common/src/lib.rs b/src/hyperlight_common/src/lib.rs index 913d89aa2..e22cf6417 100644 --- a/src/hyperlight_common/src/lib.rs +++ b/src/hyperlight_common/src/lib.rs @@ -41,4 +41,4 @@ pub mod mem; pub mod outb; /// cbindgen:ignore -pub mod resource; \ No newline at end of file +pub mod resource; diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index bfc295b00..a3cf64242 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -144,7 +144,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { _panic_handler(info) } -static PANIC_BUF: Mutex> = Mutex::new(String::new()); +static PANIC_BUF: Mutex> = Mutex::new(String::new()); #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { From c58668db70374153ea167101945b144edeb0ab83 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 28 Aug 2025 15:22:19 -0700 Subject: [PATCH 17/25] Remove whitespace in panic handler and rename test function in simpleguest Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 1 - src/tests/rust_guests/simpleguest/src/main.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index a3cf64242..49c946db8 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -170,7 +170,6 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { } let c_str_res = CStr::from_bytes_with_nul(panic_buf_guard.as_bytes()); - if c_str_res.is_err() { unsafe { abort_with_code_and_message( diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index 8b8897cbe..dddff3bf9 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -1041,13 +1041,13 @@ pub extern "C" fn hyperlight_main() { ); register_function(call_malloc_def); - let call_malloc_and_panic_def = GuestFunctionDefinition::new( + let exhaust_heap_def = GuestFunctionDefinition::new( "ExhaustHeap".to_string(), Vec::new(), ReturnType::Int, exhaust_heap as usize, ); - register_function(call_malloc_and_panic_def); + register_function(exhaust_heap_def); let malloc_and_free_def = GuestFunctionDefinition::new( "MallocAndFree".to_string(), From 1df1be6b987b355bb250fe0365407458a643a33a Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 29 Aug 2025 12:33:32 -0700 Subject: [PATCH 18/25] Write null terminator as part of initial panic message format Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 49c946db8..3a7b519aa 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -149,7 +149,7 @@ static PANIC_BUF: Mutex> = Mutex::new(String::new()); #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { let mut panic_buf_guard = PANIC_BUF.lock(); - let write_res = write!(panic_buf_guard, "{}", info); + let write_res = write!(panic_buf_guard, "{}\0", info); if write_res.is_err() { unsafe { abort_with_code_and_message( @@ -159,16 +159,6 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { } } - let append_res = panic_buf_guard.push('\0'); - if append_res.is_err() { - unsafe { - abort_with_code_and_message( - &[ErrorCode::UnknownError as u8], - c"panic: message too long".as_ptr(), - ) - } - } - let c_str_res = CStr::from_bytes_with_nul(panic_buf_guard.as_bytes()); if c_str_res.is_err() { unsafe { From 43e5480a0059936ade145ee373cdf03f84056552 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 29 Aug 2025 15:53:59 -0700 Subject: [PATCH 19/25] Stack allocate panic message buffer to avoid use of mutex on static Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 3a7b519aa..beca76e7c 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -36,7 +36,7 @@ use hyperlight_guest::exit::{abort_with_code_and_message, halt}; use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest_tracing::{trace, trace_function}; use log::LevelFilter; -use spin::{Mutex, Once}; +use spin::Once; // === Modules === #[cfg(target_arch = "x86_64")] @@ -144,12 +144,11 @@ fn panic(info: &core::panic::PanicInfo) -> ! { _panic_handler(info) } -static PANIC_BUF: Mutex> = Mutex::new(String::new()); - #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { - let mut panic_buf_guard = PANIC_BUF.lock(); - let write_res = write!(panic_buf_guard, "{}\0", info); + // stack allocate a 256-byte message buffer. + let mut panic_buf = String::<256>::new(); + let write_res = write!(panic_buf, "{}\0", info); if write_res.is_err() { unsafe { abort_with_code_and_message( @@ -159,7 +158,7 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { } } - let c_str_res = CStr::from_bytes_with_nul(panic_buf_guard.as_bytes()); + let c_str_res = CStr::from_bytes_with_nul(panic_buf.as_bytes()); if c_str_res.is_err() { unsafe { abort_with_code_and_message( From cf5efdbbdd37d2c4bffb4ea7f5b9cd2e83668cc0 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 2 Sep 2025 19:40:07 +0000 Subject: [PATCH 20/25] Increase panic buffer size and add description to format error message Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index beca76e7c..2513ce8e5 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -146,14 +146,14 @@ fn panic(info: &core::panic::PanicInfo) -> ! { #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { - // stack allocate a 256-byte message buffer. - let mut panic_buf = String::<256>::new(); + // stack allocate a 512-byte message buffer. + let mut panic_buf = String::<512>::new(); let write_res = write!(panic_buf, "{}\0", info); if write_res.is_err() { unsafe { abort_with_code_and_message( &[ErrorCode::UnknownError as u8], - c"panic: message format failed".as_ptr(), + c"panic: message format failed (limit: 512 bytes)".as_ptr(), ) } } From e9f5ace38795cd46d0b132bed0e734d4869732fd Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 3 Sep 2025 16:43:29 -0700 Subject: [PATCH 21/25] Create an fmt::Write implementation that writes formatted string to port-based io directly to stream error messages to the host and avoid needing a fixed-size buffer in the panic handler. Signed-off-by: adamperlin --- src/hyperlight_guest/src/exit.rs | 8 +++++ src/hyperlight_guest_bin/src/lib.rs | 51 ++++++++++++++++------------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/hyperlight_guest/src/exit.rs b/src/hyperlight_guest/src/exit.rs index 715086c7b..a945e44c0 100644 --- a/src/hyperlight_guest/src/exit.rs +++ b/src/hyperlight_guest/src/exit.rs @@ -67,6 +67,14 @@ pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_cha } } +/// This function exists to give the guest more manual control +/// over the abort sequence. For example, in the panic handler, +/// we have a message of unknown length that we want to stream +/// to the host, which requires sending the message in chunks +pub unsafe fn write_abort(code: &[u8]) { + outb(OutBAction::Abort as u16, code); +} + /// OUT bytes to the host through multiple exits. #[hyperlight_guest_tracing::trace_function] pub(crate) fn outb(port: u16, data: &[u8]) { diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 2513ce8e5..b1d291010 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -18,7 +18,6 @@ limitations under the License. // === Dependencies === extern crate alloc; -use core::ffi::CStr; use core::fmt::Write; use buddy_system_allocator::LockedHeap; @@ -27,12 +26,11 @@ use exceptions::{gdt::load_gdt, idtr::load_idt}; use guest_function::call::dispatch_function; use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; -use heapless::String; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::mem::HyperlightPEB; #[cfg(feature = "mem_profile")] use hyperlight_common::outb::OutBAction; -use hyperlight_guest::exit::{abort_with_code_and_message, halt}; +use hyperlight_guest::exit::{halt, write_abort}; use hyperlight_guest::guest_handle::handle::GuestHandle; use hyperlight_guest_tracing::{trace, trace_function}; use log::LevelFilter; @@ -144,35 +142,42 @@ fn panic(info: &core::panic::PanicInfo) -> ! { _panic_handler(info) } -#[inline(always)] -fn _panic_handler(info: &core::panic::PanicInfo) -> ! { - // stack allocate a 512-byte message buffer. - let mut panic_buf = String::<512>::new(); - let write_res = write!(panic_buf, "{}\0", info); - if write_res.is_err() { +/// A writer that sends all output to the hyperlight host +/// using output ports. This allows us to not impose a +/// buffering limit on error message size on the guest end, +/// though one exists for the host. +struct HyperlightAbortWriter; +impl core::fmt::Write for HyperlightAbortWriter { + fn write_str(&mut self, s: &str) -> core::fmt::Result { unsafe { - abort_with_code_and_message( - &[ErrorCode::UnknownError as u8], - c"panic: message format failed (limit: 512 bytes)".as_ptr(), - ) + write_abort(s.as_bytes()); } + Ok(()) } +} - let c_str_res = CStr::from_bytes_with_nul(panic_buf.as_bytes()); - if c_str_res.is_err() { +#[inline(always)] +fn _panic_handler(info: &core::panic::PanicInfo) -> ! { + let mut w = HyperlightAbortWriter; + + // begin abort sequence by writing the error code + unsafe { + write_abort( + &[ErrorCode::UnknownError as u8]); + } + + let write_res = write!(w, "{}", info); + if write_res.is_err() { unsafe { - abort_with_code_and_message( - &[ErrorCode::UnknownError as u8], - c"panic: failed to convert to CString".as_ptr(), - ) + write_abort("panic: message format failed".as_bytes()); } } + // write abort terminator to finish the abort + // and signal to the host that the message can now be read unsafe { - abort_with_code_and_message( - &[ErrorCode::UnknownError as u8], - c_str_res.unwrap().as_ptr(), - ) + write_abort(&[0xFF]); + unreachable!(); } } From 12f46bae5bca7b948df21c57f11538ba8dae7d12 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 3 Sep 2025 16:57:59 -0700 Subject: [PATCH 22/25] Remove heapless dependency Signed-off-by: adamperlin --- Cargo.lock | 20 ------------ src/hyperlight_guest_bin/Cargo.toml | 1 - .../rust_guests/callbackguest/Cargo.lock | 32 ------------------- src/tests/rust_guests/dummyguest/Cargo.lock | 32 ------------------- src/tests/rust_guests/simpleguest/Cargo.lock | 32 ------------------- src/tests/rust_guests/witguest/Cargo.lock | 32 ------------------- 6 files changed, 149 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6de86f0f4..9770b25e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,15 +1235,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1254,16 +1245,6 @@ dependencies = [ "serde", ] -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - [[package]] name = "heck" version = "0.5.0" @@ -1429,7 +1410,6 @@ dependencies = [ "cc", "cfg-if", "glob", - "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index e3e102c60..c21d00c91 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -27,7 +27,6 @@ hyperlight-guest-tracing = { workspace = true, default-features = false } buddy_system_allocator = "0.11.0" log = { version = "0.4", default-features = false } spin = "0.10.0" -heapless = "0.8.0" [lints] workspace = true diff --git a/src/tests/rust_guests/callbackguest/Cargo.lock b/src/tests/rust_guests/callbackguest/Cargo.lock index b8d023f1a..fa07b3e56 100644 --- a/src/tests/rust_guests/callbackguest/Cargo.lock +++ b/src/tests/rust_guests/callbackguest/Cargo.lock @@ -29,12 +29,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "callbackguest" version = "0.9.0" @@ -76,25 +70,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.9.0" @@ -124,7 +99,6 @@ dependencies = [ "cc", "cfg-if", "glob", - "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -279,12 +253,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.106" diff --git a/src/tests/rust_guests/dummyguest/Cargo.lock b/src/tests/rust_guests/dummyguest/Cargo.lock index 09136071c..e93ad50ef 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.lock +++ b/src/tests/rust_guests/dummyguest/Cargo.lock @@ -29,12 +29,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -74,25 +68,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.9.0" @@ -122,7 +97,6 @@ dependencies = [ "cc", "cfg-if", "glob", - "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -277,12 +251,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.106" diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index 81e5e3cbb..1900d8955 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -29,12 +29,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -66,25 +60,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.9.0" @@ -114,7 +89,6 @@ dependencies = [ "cc", "cfg-if", "glob", - "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -280,12 +254,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.106" diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index f6db6d09a..8bf176fa6 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -88,12 +88,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cc" version = "1.2.34" @@ -172,15 +166,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -191,16 +176,6 @@ dependencies = [ "serde", ] -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - [[package]] name = "hyperlight-common" version = "0.9.0" @@ -257,7 +232,6 @@ dependencies = [ "cc", "cfg-if", "glob", - "heapless", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-tracing", @@ -522,12 +496,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "syn" version = "2.0.106" From 92b1ca75702f0d234b0f593f80aec595046a9945 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 3 Sep 2025 17:01:42 -0700 Subject: [PATCH 23/25] fmt-apply Signed-off-by: adamperlin --- src/hyperlight_guest_bin/src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index b1d291010..506d4f89e 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -143,9 +143,9 @@ fn panic(info: &core::panic::PanicInfo) -> ! { } /// A writer that sends all output to the hyperlight host -/// using output ports. This allows us to not impose a +/// using output ports. This allows us to not impose a /// buffering limit on error message size on the guest end, -/// though one exists for the host. +/// though one exists for the host. struct HyperlightAbortWriter; impl core::fmt::Write for HyperlightAbortWriter { fn write_str(&mut self, s: &str) -> core::fmt::Result { @@ -158,12 +158,11 @@ impl core::fmt::Write for HyperlightAbortWriter { #[inline(always)] fn _panic_handler(info: &core::panic::PanicInfo) -> ! { - let mut w = HyperlightAbortWriter; + let mut w = HyperlightAbortWriter; // begin abort sequence by writing the error code unsafe { - write_abort( - &[ErrorCode::UnknownError as u8]); + write_abort(&[ErrorCode::UnknownError as u8]); } let write_res = write!(w, "{}", info); From b894870c7a9e9a56aa49e8daccfcd929cfa4185e Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 3 Sep 2025 17:22:24 -0700 Subject: [PATCH 24/25] Remove unnecessary unsafe in write_abort Signed-off-by: adamperlin --- src/hyperlight_guest/src/exit.rs | 2 +- src/hyperlight_guest_bin/src/lib.rs | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/hyperlight_guest/src/exit.rs b/src/hyperlight_guest/src/exit.rs index a945e44c0..ab6edcd66 100644 --- a/src/hyperlight_guest/src/exit.rs +++ b/src/hyperlight_guest/src/exit.rs @@ -71,7 +71,7 @@ pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_cha /// over the abort sequence. For example, in the panic handler, /// we have a message of unknown length that we want to stream /// to the host, which requires sending the message in chunks -pub unsafe fn write_abort(code: &[u8]) { +pub fn write_abort(code: &[u8]) { outb(OutBAction::Abort as u16, code); } diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 506d4f89e..8686aab5e 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -149,9 +149,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { struct HyperlightAbortWriter; impl core::fmt::Write for HyperlightAbortWriter { fn write_str(&mut self, s: &str) -> core::fmt::Result { - unsafe { - write_abort(s.as_bytes()); - } + write_abort(s.as_bytes()); Ok(()) } } @@ -161,23 +159,17 @@ fn _panic_handler(info: &core::panic::PanicInfo) -> ! { let mut w = HyperlightAbortWriter; // begin abort sequence by writing the error code - unsafe { - write_abort(&[ErrorCode::UnknownError as u8]); - } + write_abort(&[ErrorCode::UnknownError as u8]); let write_res = write!(w, "{}", info); if write_res.is_err() { - unsafe { - write_abort("panic: message format failed".as_bytes()); - } + write_abort("panic: message format failed".as_bytes()); } // write abort terminator to finish the abort // and signal to the host that the message can now be read - unsafe { - write_abort(&[0xFF]); - unreachable!(); - } + write_abort(&[0xFF]); + unreachable!(); } // === Entrypoint === From 4844ee3f54fc9232b19ef767751dbb26a1fc873b Mon Sep 17 00:00:00 2001 From: James Sturtevant Date: Tue, 9 Sep 2025 16:57:38 -0700 Subject: [PATCH 25/25] Update src/hyperlight_guest/src/exit.rs Co-authored-by: Dan Chiarlone Signed-off-by: James Sturtevant --- src/hyperlight_guest/src/exit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hyperlight_guest/src/exit.rs b/src/hyperlight_guest/src/exit.rs index ab6edcd66..a64ae390a 100644 --- a/src/hyperlight_guest/src/exit.rs +++ b/src/hyperlight_guest/src/exit.rs @@ -68,7 +68,7 @@ pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_cha } /// This function exists to give the guest more manual control -/// over the abort sequence. For example, in the panic handler, +/// over the abort sequence. For example, in `hyperlight_guest_bin`'s panic handler, /// we have a message of unknown length that we want to stream /// to the host, which requires sending the message in chunks pub fn write_abort(code: &[u8]) {