Skip to content

Commit 3a54e19

Browse files
committed
Avoid passing the guest dispatch pointer in memory
In preparation for the snapshot becoming immutable, this gets rid of on of the last places that assume identity mapping & require early memory writes in the sandbox. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent 447000e commit 3a54e19

10 files changed

Lines changed: 104 additions & 112 deletions

File tree

src/hyperlight_common/src/mem.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ pub struct GuestMemoryRegion {
3131
#[derive(Debug, Clone, Copy)]
3232
#[repr(C)]
3333
pub struct HyperlightPEB {
34-
pub guest_function_dispatch_ptr: u64,
3534
pub input_stack: GuestMemoryRegion,
3635
pub output_stack: GuestMemoryRegion,
3736
pub init_data: GuestMemoryRegion,

src/hyperlight_guest_bin/src/lib.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,13 @@ unsafe extern "C" {
178178
/// Architecture-nonspecific initialisation: set up the heap,
179179
/// coordinate some addresses and configuration with the host, and run
180180
/// user initialisation
181-
pub(crate) extern "C" fn generic_init(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) {
182-
let peb_ptr = unsafe {
181+
pub(crate) extern "C" fn generic_init(
182+
peb_address: u64,
183+
seed: u64,
184+
ops: u64,
185+
max_log_level: u64,
186+
) -> u64 {
187+
unsafe {
183188
GUEST_HANDLE = GuestHandle::init(peb_address as *mut HyperlightPEB);
184189
#[allow(static_mut_refs)]
185190
let peb_ptr = GUEST_HANDLE.peb().unwrap();
@@ -202,8 +207,6 @@ pub(crate) extern "C" fn generic_init(peb_address: u64, seed: u64, ops: u64, max
202207
let guest_start_tsc = hyperlight_guest_tracing::invariant_tsc::read_tsc();
203208

204209
unsafe {
205-
(*peb_ptr).guest_function_dispatch_ptr = dispatch_function as usize as u64;
206-
207210
let srand_seed = (((peb_address << 8) ^ (seed >> 4)) >> 32) as u32;
208211
// Set the seed for the random number generator for C code using rand;
209212
srand(srand_seed);
@@ -231,6 +234,8 @@ pub(crate) extern "C" fn generic_init(peb_address: u64, seed: u64, ops: u64, max
231234
unsafe {
232235
hyperlight_main();
233236
}
237+
238+
dispatch_function as usize as u64
234239
}
235240

236241
#[cfg(feature = "macros")]

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use crate::metrics::{METRIC_ERRONEOUS_VCPU_KICKS, METRIC_GUEST_CANCELLATION};
6868
use crate::sandbox::SandboxConfiguration;
6969
use crate::sandbox::host_funcs::FunctionRegistry;
7070
use crate::sandbox::outb::{HandleOutbError, handle_outb};
71+
use crate::sandbox::snapshot::NextAction;
7172
#[cfg(feature = "mem_profile")]
7273
use crate::sandbox::trace::MemTraceInfo;
7374
#[cfg(crashdump)]
@@ -85,7 +86,7 @@ pub(crate) struct HyperlightVm {
8586
#[cfg(not(gdb))]
8687
vm: Box<dyn VirtualMachine>,
8788
page_size: usize,
88-
entrypoint: Option<u64>, // only present if this vm has not yet been initialised
89+
entrypoint: NextAction, // only present if this vm has not yet been initialised
8990
rsp_gva: u64,
9091
interrupt_handle: Arc<dyn InterruptHandleImpl>,
9192

@@ -120,6 +121,8 @@ pub enum DispatchGuestCallError {
120121
Run(#[from] RunVmError),
121122
#[error("Failed to setup registers: {0}")]
122123
SetupRegs(RegisterError),
124+
#[error("VM was uninitialized")]
125+
Uninitialized,
123126
}
124127

125128
impl DispatchGuestCallError {
@@ -129,7 +132,7 @@ impl DispatchGuestCallError {
129132
// These errors poison the sandbox because they can leave it in an inconsistent state
130133
// by returning before the guest can unwind properly
131134
DispatchGuestCallError::Run(_) => true,
132-
DispatchGuestCallError::SetupRegs(_) => false,
135+
DispatchGuestCallError::SetupRegs(_) | DispatchGuestCallError::Uninitialized => false,
133136
}
134137
}
135138

@@ -338,7 +341,7 @@ impl HyperlightVm {
338341
snapshot_mem: GuestSharedMemory,
339342
scratch_mem: GuestSharedMemory,
340343
_pml4_addr: u64,
341-
entrypoint: Option<u64>,
344+
entrypoint: NextAction,
342345
rsp_gva: u64,
343346
#[cfg_attr(target_os = "windows", allow(unused_variables))] config: &SandboxConfiguration,
344347
#[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
@@ -438,9 +441,9 @@ impl HyperlightVm {
438441
ret.send_dbg_msg(DebugResponse::InterruptHandle(ret.interrupt_handle.clone()))?;
439442
// Add breakpoint to the entry point address, if we are going to initialise
440443
ret.vm.set_debug(true).map_err(VmError::Debug)?;
441-
if let Some(entrypoint) = entrypoint {
444+
if let NextAction::Initialise(initialise) = entrypoint {
442445
ret.vm
443-
.add_hw_breakpoint(entrypoint)
446+
.add_hw_breakpoint(initialise)
444447
.map_err(CreateHyperlightVmError::AddHwBreakpoint)?;
445448
}
446449
}
@@ -462,7 +465,7 @@ impl HyperlightVm {
462465
guest_max_log_level: Option<LevelFilter>,
463466
#[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<SandboxMemoryManager<HostSharedMemory>>>,
464467
) -> std::result::Result<(), InitializeError> {
465-
let Some(entrypoint) = self.entrypoint else {
468+
let NextAction::Initialise(initialise) = self.entrypoint else {
466469
return Ok(());
467470
};
468471

@@ -474,7 +477,7 @@ impl HyperlightVm {
474477
};
475478

476479
let regs = CommonRegisters {
477-
rip: entrypoint,
480+
rip: initialise,
478481
// We usually keep the top of the stack 16-byte
479482
// aligned. However, the ABI requirement is that the stack
480483
// be aligned _before a call instruction_, which means
@@ -508,6 +511,7 @@ impl HyperlightVm {
508511
return Err(InitializeError::InvalidStackPointer(regs.rsp));
509512
}
510513
self.rsp_gva = regs.rsp;
514+
self.entrypoint = NextAction::Call(regs.rax);
511515

512516
Ok(())
513517
}
@@ -631,6 +635,16 @@ impl HyperlightVm {
631635
self.rsp_gva = gva;
632636
}
633637

638+
/// Get the current entrypoint action
639+
pub(crate) fn get_entrypoint(&self) -> NextAction {
640+
self.entrypoint
641+
}
642+
643+
/// Set the current entrypoint action
644+
pub(crate) fn set_entrypoint(&mut self, entrypoint: NextAction) {
645+
self.entrypoint = entrypoint
646+
}
647+
634648
/// Dispatch a call from the host to the guest using the given pointer
635649
/// to the dispatch function _in the guest's address space_.
636650
///
@@ -641,14 +655,16 @@ impl HyperlightVm {
641655
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
642656
pub(crate) fn dispatch_call_from_host(
643657
&mut self,
644-
dispatch_func_addr: RawPtr,
645658
mem_mgr: &mut SandboxMemoryManager<HostSharedMemory>,
646659
host_funcs: &Arc<Mutex<FunctionRegistry>>,
647660
#[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<SandboxMemoryManager<HostSharedMemory>>>,
648661
) -> std::result::Result<(), DispatchGuestCallError> {
662+
let NextAction::Call(dispatch_func_addr) = self.entrypoint else {
663+
return Err(DispatchGuestCallError::Uninitialized);
664+
};
649665
// set RIP and RSP, reset others
650666
let regs = CommonRegisters {
651-
rip: dispatch_func_addr.into(),
667+
rip: dispatch_func_addr,
652668
// We usually keep the top of the stack 16-byte
653669
// aligned. However, the ABI requirement is that the stack
654670
// be aligned _before a call instruction_, which means
@@ -755,13 +771,13 @@ impl HyperlightVm {
755771
match exit_reason {
756772
#[cfg(gdb)]
757773
Ok(VmExit::Debug { dr6, exception }) => {
774+
let initialise = match self.entrypoint {
775+
NextAction::Initialise(initialise) => initialise,
776+
_ => 0,
777+
};
758778
// Handle debug event (breakpoints)
759-
let stop_reason = arch::vcpu_stop_reason(
760-
self.vm.as_mut(),
761-
dr6,
762-
self.entrypoint.unwrap_or(0),
763-
exception,
764-
)?;
779+
let stop_reason =
780+
arch::vcpu_stop_reason(self.vm.as_mut(), dr6, initialise, exception)?;
765781
if let Err(e) = self.handle_debug(dbg_mem_access_fn.clone(), stop_reason) {
766782
break Err(e.into());
767783
}
@@ -1133,14 +1149,19 @@ impl HyperlightVm {
11331149
.and_then(|name| name.to_os_string().into_string().ok())
11341150
});
11351151

1152+
let initialise = match self.entrypoint {
1153+
NextAction::Initialise(initialise) => initialise,
1154+
_ => 0,
1155+
};
1156+
11361157
// Include dynamically mapped regions
11371158
// TODO: include the snapshot and scratch regions
11381159
let regions: Vec<MemoryRegion> = self.get_mapped_regions().cloned().collect();
11391160
Ok(Some(crashdump::CrashDumpContext::new(
11401161
regions,
11411162
regs,
11421163
xsave.to_vec(),
1143-
self.entrypoint.unwrap_or(0),
1164+
initialise,
11441165
self.rt_cfg.binary_path.clone(),
11451166
filename,
11461167
)))

src/hyperlight_host/src/mem/layout.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ pub(crate) struct SandboxMemoryLayout {
9090
/// The following fields are offsets to the actual PEB struct fields.
9191
/// They are used when writing the PEB struct itself
9292
peb_offset: usize,
93-
peb_guest_dispatch_function_ptr_offset: usize, // set by guest in guest entrypoint
9493
peb_input_data_offset: usize,
9594
peb_output_data_offset: usize,
9695
peb_init_data_offset: usize,
@@ -129,10 +128,6 @@ impl Debug for SandboxMemoryLayout {
129128
.field("PEB Address", &format_args!("{:#x}", self.peb_address))
130129
.field("PEB Offset", &format_args!("{:#x}", self.peb_offset))
131130
.field("Code Size", &format_args!("{:#x}", self.code_size))
132-
.field(
133-
"Guest Dispatch Function Pointer Offset",
134-
&format_args!("{:#x}", self.peb_guest_dispatch_function_ptr_offset),
135-
)
136131
.field(
137132
"Input Data Offset",
138133
&format_args!("{:#x}", self.peb_input_data_offset),
@@ -216,8 +211,6 @@ impl SandboxMemoryLayout {
216211
let guest_code_offset = 0;
217212
// The following offsets are to the fields of the PEB struct itself!
218213
let peb_offset = code_size.next_multiple_of(PAGE_SIZE_USIZE);
219-
let peb_guest_dispatch_function_ptr_offset =
220-
peb_offset + offset_of!(HyperlightPEB, guest_function_dispatch_ptr);
221214
let peb_input_data_offset = peb_offset + offset_of!(HyperlightPEB, input_stack);
222215
let peb_output_data_offset = peb_offset + offset_of!(HyperlightPEB, output_stack);
223216
let peb_init_data_offset = peb_offset + offset_of!(HyperlightPEB, init_data);
@@ -238,7 +231,6 @@ impl SandboxMemoryLayout {
238231
Ok(Self {
239232
peb_offset,
240233
heap_size,
241-
peb_guest_dispatch_function_ptr_offset,
242234
peb_input_data_offset,
243235
peb_output_data_offset,
244236
peb_init_data_offset,
@@ -344,13 +336,6 @@ impl SandboxMemoryLayout {
344336
.next_multiple_of(hyperlight_common::vmem::PAGE_SIZE) as u64
345337
}
346338

347-
/// Get the offset in guest memory to where the guest dispatch function
348-
/// pointer is written
349-
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
350-
pub(super) fn get_dispatch_function_pointer_offset(&self) -> usize {
351-
self.peb_guest_dispatch_function_ptr_offset
352-
}
353-
354339
/// Get the offset in guest memory to the heap size
355340
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
356341
fn get_heap_size_offset(&self) -> usize {

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ use tracing::{Span, instrument};
2424

2525
use super::layout::SandboxMemoryLayout;
2626
use super::memory_region::MemoryRegion;
27-
use super::ptr::{GuestPtr, RawPtr};
28-
use super::ptr_offset::Offset;
27+
use super::ptr::RawPtr;
2928
use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, HostSharedMemory, SharedMemory};
3029
use crate::hypervisor::regs::CommonSpecialRegisters;
31-
use crate::sandbox::snapshot::Snapshot;
30+
use crate::sandbox::snapshot::{NextAction, Snapshot};
3231
use crate::{Result, new_error};
3332

3433
/// A struct that is responsible for laying out and managing the memory
@@ -44,7 +43,7 @@ pub(crate) struct SandboxMemoryManager<S> {
4443
/// Pointer to where to load memory from
4544
pub(crate) load_addr: RawPtr,
4645
/// Offset for the execution entrypoint from `load_addr`
47-
pub(crate) entrypoint_offset: Option<Offset>,
46+
pub(crate) entrypoint: NextAction,
4847
/// How many memory regions were mapped after sandbox creation
4948
pub(crate) mapped_rgns: u64,
5049
/// Buffer for accumulating guest abort messages
@@ -152,14 +151,14 @@ where
152151
shared_mem: S,
153152
scratch_mem: S,
154153
load_addr: RawPtr,
155-
entrypoint_offset: Option<Offset>,
154+
entrypoint: NextAction,
156155
) -> Self {
157156
Self {
158157
layout,
159158
shared_mem,
160159
scratch_mem,
161160
load_addr,
162-
entrypoint_offset,
161+
entrypoint,
163162
mapped_rgns: 0,
164163
abort_buffer: Vec::new(),
165164
}
@@ -176,12 +175,6 @@ where
176175
&mut self.shared_mem
177176
}
178177

179-
/// Get `SharedMemory` in `self` as a mutable reference
180-
#[cfg(any(gdb, test))]
181-
pub(crate) fn get_scratch_mem_mut(&mut self) -> &mut S {
182-
&mut self.scratch_mem
183-
}
184-
185178
/// Create a snapshot with the given mapped regions
186179
pub(crate) fn snapshot(
187180
&mut self,
@@ -190,6 +183,7 @@ where
190183
root_pt_gpa: u64,
191184
rsp_gva: u64,
192185
sregs: CommonSpecialRegisters,
186+
entrypoint: NextAction,
193187
) -> Result<Snapshot> {
194188
Snapshot::new(
195189
&mut self.shared_mem,
@@ -201,6 +195,7 @@ where
201195
root_pt_gpa,
202196
rsp_gva,
203197
sregs,
198+
entrypoint,
204199
)
205200
}
206201
}
@@ -212,14 +207,13 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
212207
shared_mem.copy_from_slice(s.memory(), 0)?;
213208
let scratch_mem = ExclusiveSharedMemory::new(s.layout().get_scratch_size())?;
214209
let load_addr: RawPtr = RawPtr::try_from(layout.get_guest_code_address())?;
215-
let entrypoint_gva = s.preinitialise();
216-
let entrypoint_offset = entrypoint_gva.map(|x| (x - u64::from(&load_addr)).into());
210+
let entrypoint = s.entrypoint();
217211
Ok(Self::new(
218212
layout,
219213
shared_mem,
220214
scratch_mem,
221215
load_addr,
222-
entrypoint_offset,
216+
entrypoint,
223217
))
224218
}
225219

@@ -257,7 +251,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
257251
scratch_mem: hscratch,
258252
layout: self.layout,
259253
load_addr: self.load_addr.clone(),
260-
entrypoint_offset: self.entrypoint_offset,
254+
entrypoint: self.entrypoint,
261255
mapped_rgns: self.mapped_rgns,
262256
abort_buffer: self.abort_buffer,
263257
};
@@ -266,7 +260,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
266260
scratch_mem: gscratch,
267261
layout: self.layout,
268262
load_addr: self.load_addr.clone(),
269-
entrypoint_offset: self.entrypoint_offset,
263+
entrypoint: self.entrypoint,
270264
mapped_rgns: self.mapped_rgns,
271265
abort_buffer: Vec::new(), // Guest doesn't need abort buffer
272266
};
@@ -278,20 +272,6 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
278272
}
279273

280274
impl SandboxMemoryManager<HostSharedMemory> {
281-
/// Get the address of the dispatch function in memory
282-
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
283-
pub(crate) fn get_pointer_to_dispatch_function(&self) -> Result<u64> {
284-
let guest_dispatch_function_ptr = self
285-
.shared_mem
286-
.read::<u64>(self.layout.get_dispatch_function_pointer_offset())?;
287-
288-
// This pointer is written by the guest library but is accessible to
289-
// the guest engine so we should bounds check it before we return it.
290-
291-
let guest_ptr = GuestPtr::try_from(RawPtr::from(guest_dispatch_function_ptr))?;
292-
guest_ptr.absolute()
293-
}
294-
295275
/// Reads a host function call from memory
296276
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
297277
pub(crate) fn get_host_function_call(&mut self) -> Result<FunctionCall> {

src/hyperlight_host/src/mem/shared_mem.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,10 @@ impl ExclusiveSharedMemory {
334334
#[cfg(target_os = "linux")]
335335
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
336336
pub fn new(min_size_bytes: usize) -> Result<Self> {
337-
use libc::{MAP_ANONYMOUS, MAP_PRIVATE, MAP_FAILED, PROT_READ, PROT_WRITE, c_int, mmap, off_t, size_t};
337+
use libc::{
338+
MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_READ, PROT_WRITE, c_int, mmap, off_t,
339+
size_t,
340+
};
338341
#[cfg(not(miri))]
339342
use libc::{MAP_NORESERVE, PROT_NONE, mprotect};
340343

0 commit comments

Comments
 (0)