Skip to content

Commit ae1637b

Browse files
committed
[guest/{chkstk,entrypoint},host/{drivers,sandbox_builder},common/peb,simpleguest] fixed stack setup
(1) PE guests must have the RSP be 16-byte aligned, added stack alignment on init_rsp and re-setup to account for it. (2) We're now properly updating the min stack address needed for chkstk via the HyperlightPEB. (3) Added tests for PE guests. Signed-off-by: danbugs <[email protected]>
1 parent 04e8e96 commit ae1637b

File tree

9 files changed

+248
-219
lines changed

9 files changed

+248
-219
lines changed

src/hyperlight_common/src/peb.rs

+13
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub struct MemoryRegion {
2727
#[repr(C)]
2828
#[derive(Clone, Default)]
2929
pub struct HyperlightPEB {
30+
pub min_stack_address: u64,
3031
// - Host configured fields
3132
/// Hyperlight supports two primary modes:
3233
/// 1. Hypervisor mode
@@ -86,6 +87,7 @@ impl HyperlightPEB {
8687
/// Creates a new HyperlightPEB with the basic configuration based on the provided guest memory
8788
/// layout and default guest heap/stack sizes. The guest can later fill additional fields.
8889
pub fn new(
90+
min_stack_address: u64,
8991
run_mode: RunMode,
9092
guest_heap_size: u64,
9193
guest_stack_size: u64,
@@ -94,6 +96,7 @@ impl HyperlightPEB {
9496
guest_memory_size: u64,
9597
) -> Self {
9698
Self {
99+
min_stack_address,
97100
run_mode,
98101
outb_ptr: 0,
99102
outb_ptr_ctx: 0,
@@ -222,6 +225,16 @@ impl HyperlightPEB {
222225
region.offset.unwrap() + self.guest_memory_base_address + region.size
223226
}
224227

228+
/// Calculate the minimum guest stack address (start of guest stack data region in the guest
229+
/// address space).
230+
pub fn calculate_min_stack_address(&self) -> u64 {
231+
let region = self
232+
.guest_stack_data
233+
.as_ref()
234+
.expect("Guest stack data region not set");
235+
region.offset.unwrap() + self.guest_memory_base_address
236+
}
237+
225238
/// Sets the guest heap data region.
226239
/// - HyperlightPEB is always set with a default size for heap from the guest binary, there's an
227240
/// option to override this size with the `size_override` parameter.

src/hyperlight_guest/src/chkstk.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@ limitations under the License.
1515
*/
1616

1717
use core::arch::global_asm;
18-
use core::mem::size_of;
18+
use core::mem::{offset_of, size_of};
1919

20-
use hyperlight_common::peb::RunMode;
21-
use hyperlight_common::RUNNING_MODE;
20+
use hyperlight_common::peb::{HyperlightPEB, RunMode};
21+
use hyperlight_common::{PEB, RUNNING_MODE};
2222

2323
use crate::guest_error::{set_invalid_runmode_error, set_stack_allocate_error};
24-
use crate::MIN_STACK_ADDRESS;
2524

2625
extern "win64" {
2726
fn __chkstk();
@@ -52,20 +51,22 @@ global_asm!(
5251
5352
handle_hypervisor:
5453
/* Load the minimum stack address from the PEB */
55-
mov r11, [rip+{min_stack_addr}]
54+
/* min_stack_address is offset 0x0 in the PEB struct */
55+
mov r11, [rip+{peb_ptr}]
56+
mov r11, qword ptr [r11]
5657
5758
/* Get the current stack pointer */
58-
lea r10, [rsp+0x18]
59+
lea r10, [rsp+0x18]
5960
6061
/* Calculate what the new stack pointer will be */
6162
sub r10, rax
62-
63+
6364
/* If result is negative, cause StackOverflow */
6465
js call_set_error
65-
66+
6667
/* Compare the new stack pointer with the minimum stack address */
67-
cmp r10, r11
68-
/* If the new stack pointer is greater or equal to the minimum stack address,
68+
cmp r10, r11
69+
/* If the new stack pointer is greater or equal to the minimum stack address,
6970
then we are good. Otherwise set the error code to 9 (stack overflow) call set_error and halt */
7071
jae cs_ret
7172
@@ -90,7 +91,7 @@ global_asm!(
9091
cmp r10, r11
9192
jne csip_stackprobe
9293
cs_ret:
93-
/* Restore RAX, R11 */
94+
/* Restore R10, R11 */
9495
pop r11
9596
pop r10
9697
ret
@@ -104,7 +105,7 @@ global_asm!(
104105
handle_invalid:
105106
call {invalid_runmode}",
106107
run_mode = sym RUNNING_MODE,
107-
min_stack_addr = sym MIN_STACK_ADDRESS,
108+
peb_ptr = sym PEB,
108109
set_error = sym set_stack_allocate_error,
109110
invalid_runmode = sym set_invalid_runmode_error
110111
);
@@ -118,4 +119,5 @@ const _: () = {
118119
assert!(RunMode::InProcessWindows as u64 == 2);
119120
assert!(RunMode::InProcessLinux as u64 == 3);
120121
assert!(RunMode::Invalid as u64 == 4);
122+
assert!(offset_of!(HyperlightPEB, min_stack_address) == 0x0);
121123
};

src/hyperlight_guest/src/entrypoint.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::gdt::load_gdt;
2727
use crate::guest_function_call::dispatch_function;
2828
use crate::guest_logger::init_logger;
2929
use crate::idtr::load_idt;
30-
use crate::{__security_cookie, HEAP_ALLOCATOR, MIN_STACK_ADDRESS};
30+
use crate::{__security_cookie, HEAP_ALLOCATOR};
3131

3232
#[inline(never)]
3333
pub fn halt() {
@@ -111,12 +111,6 @@ pub extern "win64" fn entrypoint(peb_address: u64, seed: u64, max_log_level: u64
111111

112112
match RUNNING_MODE {
113113
RunMode::Hypervisor => {
114-
// This static is to make it easier to implement the __chkstk function in assembly.
115-
// It also means that, should we change the layout of the struct in the future, we
116-
// don't have to change the assembly code. Plus, while this could be accessible via
117-
// the PEB, we don't want to expose it entirely to user code.
118-
MIN_STACK_ADDRESS = (*PEB).get_stack_data_address();
119-
120114
// Setup GDT and IDT
121115
load_gdt();
122116
load_idt();

src/hyperlight_guest/src/lib.rs

-2
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,5 @@ pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty();
8585
#[no_mangle]
8686
pub(crate) static mut __security_cookie: u64 = 0;
8787

88-
pub static mut MIN_STACK_ADDRESS: u64 = 0;
89-
9088
pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister =
9189
GuestFunctionRegister::new();

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

+7-67
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use super::{
6262
use crate::hypervisor::hypervisor_handler::HypervisorHandler;
6363
use crate::hypervisor::HyperlightExit;
6464
use crate::mem::ptr::{GuestPtr, RawPtr};
65-
use crate::sandbox::sandbox_builder::{MemoryRegionFlags, SandboxMemorySections};
65+
use crate::sandbox::sandbox_builder::{MemoryRegionFlags, SandboxMemorySections, STACK_ALIGNMENT};
6666
#[cfg(gdb)]
6767
use crate::HyperlightError;
6868
use crate::{log_then_return, new_error, Result};
@@ -489,13 +489,17 @@ impl Hypervisor for HypervLinuxDriver {
489489
)?;
490490

491491
// The guest may have chosen a different stack region. If so, we drop usage of our tmp stack.
492-
let hyperlight_peb = self.mem_sections.read_hyperlight_peb()?;
492+
let mut hyperlight_peb = self.mem_sections.read_hyperlight_peb()?;
493493

494494
if let Some(guest_stack_data) = &hyperlight_peb.get_guest_stack_data_region() {
495495
if guest_stack_data.offset.is_some() {
496496
// If we got here, it means the guest has set up a new stack
497497
let rsp = hyperlight_peb.get_top_of_guest_stack_data();
498-
self.orig_rsp = GuestPtr::try_from(RawPtr::from(rsp))?;
498+
self.orig_rsp = GuestPtr::try_from(RawPtr::from(rsp - STACK_ALIGNMENT))?;
499+
500+
// Need to update the min stack address from tmp_stack address to the new stack
501+
hyperlight_peb.min_stack_address = hyperlight_peb.calculate_min_stack_address();
502+
self.mem_sections.write_hyperlight_peb(hyperlight_peb)?;
499503
}
500504
}
501505

@@ -730,67 +734,3 @@ impl Drop for HypervLinuxDriver {
730734
}
731735
}
732736
}
733-
734-
// TODO(danbugs:297): bring back
735-
// #[cfg(test)]
736-
// mod tests {
737-
// use super::*;
738-
// use crate::mem::memory_region::MemoryRegionVecBuilder;
739-
// use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
740-
//
741-
// #[rustfmt::skip]
742-
// const CODE: [u8; 12] = [
743-
// 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
744-
// 0x00, 0xd8, /* add %bl, %al */
745-
// 0x04, b'0', /* add $'0', %al */
746-
// 0xee, /* out %al, (%dx) */
747-
// /* send a 0 to indicate we're done */
748-
// 0xb0, b'\0', /* mov $'\0', %al */
749-
// 0xee, /* out %al, (%dx) */
750-
// 0xf4, /* HLT */
751-
// ];
752-
//
753-
// fn shared_mem_with_code(
754-
// code: &[u8],
755-
// mem_size: usize,
756-
// load_offset: usize,
757-
// ) -> Result<Box<ExclusiveSharedMemory>> {
758-
// if load_offset > mem_size {
759-
// log_then_return!(
760-
// "code load offset ({}) > memory size ({})",
761-
// load_offset,
762-
// mem_size
763-
// );
764-
// }
765-
// let mut shared_mem = ExclusiveSharedMemory::new(mem_size)?;
766-
// shared_mem.copy_from_slice(code, load_offset)?;
767-
// Ok(Box::new(shared_mem))
768-
// }
769-
//
770-
// #[test]
771-
// fn create_driver() {
772-
// if !super::is_hypervisor_present() {
773-
// return;
774-
// }
775-
// const MEM_SIZE: usize = 0x3000;
776-
// let gm = shared_mem_with_code(CODE.as_slice(), MEM_SIZE, 0).unwrap();
777-
// let rsp_ptr = GuestPtr::try_from(0).unwrap();
778-
// let pml4_ptr = GuestPtr::try_from(0).unwrap();
779-
// let entrypoint_ptr = GuestPtr::try_from(0).unwrap();
780-
// let mut regions = MemoryRegionVecBuilder::new(0, gm.base_addr());
781-
// regions.push_page_aligned(
782-
// MEM_SIZE,
783-
// MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE,
784-
// crate::mem::memory_region::MemoryRegionType::Code,
785-
// );
786-
// super::HypervLinuxDriver::new(
787-
// regions.build(),
788-
// entrypoint_ptr,
789-
// rsp_ptr,
790-
// pml4_ptr,
791-
// #[cfg(gdb)]
792-
// None,
793-
// )
794-
// .unwrap();
795-
// }
796-
// }

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

+32-29
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT;
4444
use crate::hypervisor::hypervisor_handler::HypervisorHandler;
4545
use crate::hypervisor::wrappers::WHvGeneralRegisters;
4646
use crate::mem::ptr::{GuestPtr, RawPtr};
47-
use crate::sandbox::sandbox_builder::{MemoryRegionFlags, SandboxMemorySections};
47+
use crate::sandbox::sandbox_builder::{MemoryRegionFlags, SandboxMemorySections, STACK_ALIGNMENT};
4848
use crate::{debug, new_error, Result};
4949

5050
/// A Hypervisor driver for HyperV-on-Windows.
@@ -339,13 +339,17 @@ impl Hypervisor for HypervWindowsDriver {
339339
)?;
340340

341341
// The guest may have chosen a different stack region. If so, we drop usage of our tmp stack.
342-
let hyperlight_peb = self.mem_sections.read_hyperlight_peb()?;
342+
let mut hyperlight_peb = self.mem_sections.read_hyperlight_peb()?;
343343

344344
if let Some(guest_stack_data) = &hyperlight_peb.get_guest_stack_data_region() {
345345
if guest_stack_data.offset.is_some() {
346346
// If we got here, it means the guest has set up a new stack
347347
let rsp = hyperlight_peb.get_top_of_guest_stack_data();
348-
self.orig_rsp = GuestPtr::try_from(RawPtr::from(rsp))?;
348+
self.orig_rsp = GuestPtr::try_from(RawPtr::from(rsp - STACK_ALIGNMENT))?;
349+
350+
// Need to update the min stack address from tmp_stack address to the new stack
351+
hyperlight_peb.min_stack_address = hyperlight_peb.calculate_min_stack_address();
352+
self.mem_sections.write_hyperlight_peb(hyperlight_peb)?;
349353
}
350354
}
351355

@@ -504,29 +508,28 @@ impl Hypervisor for HypervWindowsDriver {
504508
}
505509
}
506510

507-
// TODO(danbugs:297): bring back
508-
// #[cfg(test)]
509-
// pub mod tests {
510-
// use std::sync::{Arc, Mutex};
511-
//
512-
// use serial_test::serial;
513-
//
514-
// use crate::hypervisor::handlers::{MemAccessHandler, OutBHandler};
515-
// use crate::hypervisor::tests::test_initialise;
516-
// use crate::Result;
517-
//
518-
// #[test]
519-
// #[serial]
520-
// fn test_init() {
521-
// let outb_handler = {
522-
// let func: Box<dyn FnMut(u16, u64) -> Result<()> + Send> =
523-
// Box::new(|_, _| -> Result<()> { Ok(()) });
524-
// Arc::new(Mutex::new(OutBHandler::from(func)))
525-
// };
526-
// let mem_access_handler = {
527-
// let func: Box<dyn FnMut() -> Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) });
528-
// Arc::new(Mutex::new(MemAccessHandler::from(func)))
529-
// };
530-
// test_initialise(outb_handler, mem_access_handler).unwrap();
531-
// }
532-
// }
511+
#[cfg(test)]
512+
pub mod tests {
513+
use std::sync::{Arc, Mutex};
514+
515+
use serial_test::serial;
516+
517+
use crate::hypervisor::handlers::{MemAccessHandler, OutBHandler};
518+
use crate::hypervisor::tests::test_initialise;
519+
use crate::Result;
520+
521+
#[test]
522+
#[serial]
523+
fn test_init() {
524+
let outb_handler = {
525+
let func: Box<dyn FnMut(u16, u64) -> Result<()> + Send> =
526+
Box::new(|_, _| -> Result<()> { Ok(()) });
527+
Arc::new(Mutex::new(OutBHandler::from(func)))
528+
};
529+
let mem_access_handler = {
530+
let func: Box<dyn FnMut() -> Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) });
531+
Arc::new(Mutex::new(MemAccessHandler::from(func)))
532+
};
533+
test_initialise(outb_handler, mem_access_handler).unwrap();
534+
}
535+
}

0 commit comments

Comments
 (0)