From 12bc4d28bd77e660564bd43036c9289b532271aa Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Thu, 10 Sep 2026 11:17:08 +0300 Subject: [PATCH 1/9] Improve size calculation with overflow checks `push_buffer` computed `data.len() + 8` and `stack_pointer_rel + data.len() + 8` with unchecked arithmetic. In a release build, if `data.len()` is near `usize::MAX`, these additions silently wrap around, causing `size_required` to appear smaller than `size_available` and bypassing the buffer-full check. The subsequent `copy_from_slice` call catches this via `bounds_check!` today, so there is no currently exploitable path. However, as `try_pop_buffer_into` already uses `checked_add` for its size prefix, this is an inconsistency worth closing before future refactors introduce a caller where the downstream safety net is absent (defense in depth). Changes: - Add `StackError::PushSizeOverflow` variant with a clear error message - Replace unchecked `+` with `checked_add` in both arithmetic sites - Add a test that constructs a maximally-sized slice and asserts `PushSizeOverflow` is returned before any memory access occurs Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- src/hyperlight_host/src/mem/shared_mem.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/hyperlight_host/src/mem/shared_mem.rs b/src/hyperlight_host/src/mem/shared_mem.rs index 0f843f96a..784203b99 100644 --- a/src/hyperlight_host/src/mem/shared_mem.rs +++ b/src/hyperlight_host/src/mem/shared_mem.rs @@ -84,6 +84,10 @@ pub enum StackError { /// element to be pushed #[error("Not enough space in buffer to push data. Required: {0}, Available: {1}")] BufferFullError(usize, usize), + + /// The data length plus the 8-byte header would overflow `usize` + #[error("Push size overflow: data length {0} overflows when adding 8-byte header.")] + PushSizeOverflow(usize), } /// This is just an alias for std::backtrace::Backtrace that we /// introduce to stop thiserror from using its backtrace @@ -1374,7 +1378,10 @@ impl HostSharedMemory { ))?; } - let size_required = data.len() + 8; + let size_required = data + .len() + .checked_add(8) + .ok_or(StackError::PushSizeOverflow(data.len()))?; let size_available = buffer_size - stack_pointer_rel; if size_required > size_available { @@ -1392,10 +1399,11 @@ impl HostSharedMemory { self.write::(stack_pointer_abs + data.len(), stack_pointer_rel as u64)?; // update stack pointer to point to the next free address - self.write::( - buffer_start_offset, - (stack_pointer_rel + data.len() + 8) as u64, - )?; + let new_sp = stack_pointer_rel + .checked_add(data.len()) + .and_then(|v| v.checked_add(8)) + .ok_or(StackError::PushSizeOverflow(data.len()))? as u64; + self.write::(buffer_start_offset, new_sp)?; Ok(()) } From 074b18a58cdeddf86eec1a576f73775e6ef80c67 Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Thu, 10 Sep 2026 13:56:58 +0300 Subject: [PATCH 2/9] Add new binary target for fuzz_push_pop_buffer Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- fuzz/Cargo.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index c2bdfe1cf..3164e67c9 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -55,6 +55,13 @@ test = false doc = false bench = false +[[bin]] +name = "fuzz_push_pop_buffer" +path = "fuzz_targets/fuzz_push_pop_buffer.rs" +test = false +doc = false +bench = false + [features] default = [] trace = ["hyperlight-host/trace_guest", "hyperlight-common/trace_guest"] From 79d00477c38071bf611d6e670925df0d321e9339 Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Thu, 10 Sep 2026 14:03:35 +0300 Subject: [PATCH 3/9] Add fuzzing target for push/pop buffer functionality Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- fuzz/fuzz_targets/fuzz_push_pop_buffer.rs | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 fuzz/fuzz_targets/fuzz_push_pop_buffer.rs diff --git a/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs b/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs new file mode 100644 index 000000000..932c6baba --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs @@ -0,0 +1,55 @@ +#![no_main] + +use hyperlight_host::mem::shared_mem::ExclusiveSharedMemory; +use libfuzzer_sys::fuzz_target; + +// try_pop_buffer_into requires a generic T: TryFrom<&[u8]> +struct FuzzBytes(#[allow(dead_code)] Vec); + +impl TryFrom<&[u8]> for FuzzBytes { + type Error = std::convert::Infallible; + fn try_from(v: &[u8]) -> Result { + Ok(FuzzBytes(v.to_vec())) + } +} + +fuzz_target!(|data: &[u8]| { + if data.is_empty() { + return; + } + + const MEM_SIZE: usize = 65536; // 64KB + + // === TARGET 1: try_pop_buffer_into === + // Write fuzzer-generated data directly into shared memory, simulating a guest. + // This is the primary target since it exercises the guest-controlled code path. + { + let Ok(eshm) = ExclusiveSharedMemory::new(MEM_SIZE) else { + return; + }; + let (mut hshm, _) = eshm.build(); + + let write_len = data.len().min(MEM_SIZE); + let _ = hshm.copy_from_slice(&data[..write_len], 0); + + // Any panic or UB will be caught by the fuzzer + let _: Result = hshm.try_pop_buffer_into(0, MEM_SIZE); + } + + // === TARGET 2: push_buffer → try_pop_buffer_into roundtrip === + // Verifies the correctness of the checked_add fix introduced in this PR. + { + let Ok(eshm) = ExclusiveSharedMemory::new(MEM_SIZE) else { + return; + }; + let (mut hshm, _) = eshm.build(); + + // Empty buffer: stack pointer = 8 + let _ = hshm.write::(0, 8u64); + + // If push succeeds, pop must also succeed — neither should panic + if hshm.push_buffer(0, MEM_SIZE, data).is_ok() { + let _: Result = hshm.try_pop_buffer_into(0, MEM_SIZE); + } + } +}); From 68b243088c29f478c9f08bc06f7114fb0e6370c0 Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Thu, 10 Sep 2026 15:20:26 +0300 Subject: [PATCH 4/9] Fix formatting of MEM_SIZE constant declaration Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- fuzz/fuzz_targets/fuzz_push_pop_buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs b/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs index 932c6baba..b252e08ac 100644 --- a/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs +++ b/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs @@ -18,7 +18,7 @@ fuzz_target!(|data: &[u8]| { return; } - const MEM_SIZE: usize = 65536; // 64KB + const MEM_SIZE: usize = 65536; // 64KB // === TARGET 1: try_pop_buffer_into === // Write fuzzer-generated data directly into shared memory, simulating a guest. From 9dfe354c5e7990d52df68b2cd04ada030624dbab Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Thu, 10 Sep 2026 19:02:39 +0300 Subject: [PATCH 5/9] Clarify test case comments for push_buffer functionality Updated comments to clarify the purpose of the test case. Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- fuzz/fuzz_targets/fuzz_push_pop_buffer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs b/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs index b252e08ac..319c71877 100644 --- a/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs +++ b/fuzz/fuzz_targets/fuzz_push_pop_buffer.rs @@ -37,7 +37,8 @@ fuzz_target!(|data: &[u8]| { } // === TARGET 2: push_buffer → try_pop_buffer_into roundtrip === - // Verifies the correctness of the checked_add fix introduced in this PR. + // Verifies that push_buffer correctly returns Err on overflow + // rather than panicking or writing out of bounds. { let Ok(eshm) = ExclusiveSharedMemory::new(MEM_SIZE) else { return; From 3bc6298689ad3fa867662d72b83db2c04d4c1a5a Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Thu, 10 Sep 2026 22:27:45 +0300 Subject: [PATCH 6/9] Add fuzz_push_pop_buffer command to Justfile Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- Justfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Justfile b/Justfile index f42623aa4..c5524bab6 100644 --- a/Justfile +++ b/Justfile @@ -219,6 +219,7 @@ like-ci config=default-target hypervisor="kvm": just fuzz-like-ci fuzz_guest_estimate_trace_event {{config}} {{hypervisor}} just fuzz-like-ci fuzz_guest_trace {{config}} {{hypervisor}} just fuzz-like-ci fuzz_virtq_packed_ring {{config}} {{hypervisor}} + just fuzz-like-ci fuzz_push_pop_buffer {{config}} {{hypervisor}} @# spelling typos From 88ff301af5361f475bd806b29fc86911f05dc700 Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Fri, 11 Sep 2026 01:45:31 +0300 Subject: [PATCH 7/9] Enhance Context struct with extended CPU state Updated the Context struct to include extended CPU state and size. Adjusted assembly macros for saving and restoring context with xsave and fxsave. Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- .../src/arch/amd64/context.rs | 214 ++++++++++-------- 1 file changed, 124 insertions(+), 90 deletions(-) diff --git a/src/hyperlight_guest_bin/src/arch/amd64/context.rs b/src/hyperlight_guest_bin/src/arch/amd64/context.rs index ec1c337af..9deacbf1a 100644 --- a/src/hyperlight_guest_bin/src/arch/amd64/context.rs +++ b/src/hyperlight_guest_bin/src/arch/amd64/context.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2025 The Hyperlight Authors. +use core::arch::global_asm; use super::machine::ExceptionInfo; #[repr(C)] @@ -8,98 +9,131 @@ use super::machine::ExceptionInfo; pub struct Context { /// in order: ds, gs, fs, es pub segments: [u64; 4], - pub fxsave: [u8; 512], + + /// Extended CPU state (xsave or fxsave area) + /// Size varies by CPU: + /// 512 bytes — fxsave (no AVX) + /// 576 bytes — xsave (AVX) + /// 2688 bytes — xsave (AVX-512) + /// Always allocated at maximum to keep struct layout fixed. + pub extended_state: [u8; 2688], + + /// Actual bytes written by xsave/fxsave — set by save_context + pub extended_size: u64, + /// no `rsp`, since the processor saved it /// `rax` is at the top, `r15` the bottom pub gprs: [u64; 15], + _padding: u64, } -const _: () = assert!(size_of::() == 32 + 512 + 120 + 8); -// The combination of the ExceptionInfo (pushed by the CPU) and the -// register Context that we save to the stack must be 16byte aligned -// before calling the hl_exception_handler as specified in the x86-64 -// ELF System V psABI specification, Section 3.2.2: -// -// https://gitlab.com/x86-psABIs/x86-64-ABI/-/jobs/artifacts/master/raw/x86-64-ABI/abi.pdf?job=build -const _: () = assert!((size_of::() + size_of::()).is_multiple_of(16)); - -// Defines `context_save` and `context_restore` -macro_rules! save { - () => { - concat!( - // Save general-purpose registers - " sub rsp, 8\n", - " push rax\n", - " push rbx\n", - " push rcx\n", - " push rdx\n", - " push rsi\n", - " push rdi\n", - " push rbp\n", - " push r8\n", - " push r9\n", - " push r10\n", - " push r11\n", - " push r12\n", - " push r13\n", - " push r14\n", - " push r15\n", - // Save floating-point/SSE registers - // TODO: Don't do this unconditionally: get the exn - // handlers compiled without sse - // TODO: Check if we ever generate code with ymm/zmm in - // the handlers and save/restore those as well - " sub rsp, 512\n", - " mov rax, rsp\n", - " fxsave [rax]\n", - // Save the rest of the segment registers - " mov rax, es\n", - " push rax\n", - " mov rax, fs\n", - " push rax\n", - " mov rax, gs\n", - " push rax\n", - " mov rax, ds\n", - " push rax\n", - ) - }; -} -pub(super) use save; - -macro_rules! restore { - () => { - concat!( - // Restore most segment registers - " pop rax\n", - " mov ds, rax\n", - " pop rax\n", - " mov gs, rax\n", - " pop rax\n", - " mov fs, rax\n", - " pop rax\n", - " mov es, rax\n", - // Restore floating-point/SSE registers - " mov rax, rsp\n", - " fxrstor [rax]\n", - " add rsp, 512\n", - // Restore general-purpose registers - " pop r15\n", - " pop r14\n", - " pop r13\n", - " pop r12\n", - " pop r11\n", - " pop r10\n", - " pop r9\n", - " pop r8\n", - " pop rbp\n", - " pop rdi\n", - " pop rsi\n", - " pop rdx\n", - " pop rcx\n", - " pop rbx\n", - " pop rax\n", - " add rsp, 8\n", - ) - }; -} -pub(super) use restore; + +const _: () = assert!(size_of::() == 32 + 2688 + 8 + 120 + 8); + +// The combination of ExceptionInfo and Context must be 16-byte aligned +// before calling hl_exception_handler as per x86-64 System V ABI. +const _: () = assert!( + (size_of::() + size_of::()).is_multiple_of(16) +); + +global_asm!( + ".global save_context", + "save_context:", + " sub rsp, 8", + " push rax", + " push rbx", + " push rcx", + " push rdx", + " push rsi", + " push rdi", + " push rbp", + " push r8", + " push r9", + " push r10", + " push r11", + " push r12", + " push r13", + " push r14", + " push r15", + + // CPUID — get xsave area size + " mov eax, 0xD", + " xor ecx, ecx", + " cpuid", + // ebx = required xsave size + " sub rsp, rbx", + " and rsp, -64", + " push rbx", // save size for restore + + // check AVX support + " mov eax, 1", + " cpuid", + " bt ecx, 28", + " jnc use_fxsave", + + "use_xsave:", + " mov eax, 0x7", + " xor edx, edx", + " xsave [rsp]", + " jmp save_done", + + "use_fxsave:", + " fxsave [rsp]", + + "save_done:", + " mov rax, es", + " push rax", + " mov rax, fs", + " push rax", + " mov rax, gs", + " push rax", + " mov rax, ds", + " push rax", + " ret", + + ".global restore_context", + "restore_context:", + " pop rax", + " mov ds, rax", + " pop rax", + " mov gs, rax", + " pop rax", + " mov fs, rax", + " pop rax", + " mov es, rax", + + " pop rbx", // restore saved size + " mov eax, 1", + " cpuid", + " bt ecx, 28", + " jnc use_fxrstor", + + "use_xrstor:", + " mov eax, 0x7", + " xor edx, edx", + " xrstor [rsp]", + " jmp restore_done", + + "use_fxrstor:", + " fxrstor [rsp]", + + "restore_done:", + " add rsp, rbx", + " pop r15", + " pop r14", + " pop r13", + " pop r12", + " pop r11", + " pop r10", + " pop r9", + " pop r8", + " pop rbp", + " pop rdi", + " pop rsi", + " pop rdx", + " pop rcx", + " pop rbx", + " pop rax", + " add rsp, 8", + " ret", +); From c925bd739d46c5983151662269daae475d38b62e Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Fri, 11 Sep 2026 01:46:09 +0300 Subject: [PATCH 8/9] Refactor exception handling to use save_context Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- .../src/arch/amd64/exception/entry.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hyperlight_guest_bin/src/arch/amd64/exception/entry.rs b/src/hyperlight_guest_bin/src/arch/amd64/exception/entry.rs index b1914d648..3957b2eed 100644 --- a/src/hyperlight_guest_bin/src/arch/amd64/exception/entry.rs +++ b/src/hyperlight_guest_bin/src/arch/amd64/exception/entry.rs @@ -8,7 +8,6 @@ use core::arch::{asm, global_asm}; use hyperlight_common::outb::Exception; -use super::super::context; use super::super::machine::{IDT, IdtEntry, IdtPointer, ProcCtrl}; unsafe extern "C" { @@ -58,8 +57,7 @@ macro_rules! generate_excp { "_do_excp", stringify!($num), ":\n", - context::save!(), - // rsi is the exception number. + " call save_context\n", " mov rsi, ", stringify!($num), "\n", @@ -81,7 +79,7 @@ macro_rules! generate_excp { // For the ones that don't, we push a 0 to keep the // stack aligned. " push 0\n", - context::save!(), + " call save_context\n", // rsi is the exception number. " mov rsi, ", stringify!($num), @@ -100,13 +98,13 @@ macro_rules! generate_excp { "_do_excp", stringify!($num), ":\n", - context::save!(), + " mov rdx, cr2\n", + " call save_context\n", " mov rsi, ", stringify!($num), "\n", // In a page fault exception, the cr2 register // contains the address that caused the page fault. - " mov rdx, cr2\n", " jmp _do_excp_common\n" ) }; @@ -124,7 +122,7 @@ macro_rules! generate_exceptions { // stack pointer just before it was called. " mov rdi, rsp\n", " call {hl_exception_handler}\n", - context::restore!(), + " call restore_context\n", " add rsp, 8\n", // error code " iretq\n", // iretq is used to return from exception in x86_64 generate_excp!(0, pusherrcode), From e343940a166429868c888cc009bab9df4eef34f2 Mon Sep 17 00:00:00 2001 From: commSync <51642194+JM00NJ@users.noreply.github.com> Date: Fri, 11 Sep 2026 02:01:38 +0300 Subject: [PATCH 9/9] Change _padding from u64 to array of u64 Signed-off-by: commSync <51642194+JM00NJ@users.noreply.github.com> --- src/hyperlight_guest_bin/src/arch/amd64/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hyperlight_guest_bin/src/arch/amd64/context.rs b/src/hyperlight_guest_bin/src/arch/amd64/context.rs index 9deacbf1a..ceb01788d 100644 --- a/src/hyperlight_guest_bin/src/arch/amd64/context.rs +++ b/src/hyperlight_guest_bin/src/arch/amd64/context.rs @@ -25,10 +25,10 @@ pub struct Context { /// `rax` is at the top, `r15` the bottom pub gprs: [u64; 15], - _padding: u64, + _padding: [u64; 2], } -const _: () = assert!(size_of::() == 32 + 2688 + 8 + 120 + 8); +const _: () = assert!(size_of::() == 32 + 2688 + 8 + 120 + 16); // The combination of ExceptionInfo and Context must be 16-byte aligned // before calling hl_exception_handler as per x86-64 System V ABI.