Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/hyperlight_common/src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct HyperlightPEB {
pub code_ptr: u64,
pub input_stack: GuestMemoryRegion,
pub output_stack: GuestMemoryRegion,
pub init_data: GuestMemoryRegion,
pub guest_heap: GuestMemoryRegion,
pub guest_stack: GuestStack,
pub host_function_definitions: GuestMemoryRegion,
Expand Down
23 changes: 23 additions & 0 deletions src/hyperlight_guest/src/guest_handle/host_comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,29 @@ use crate::error::{HyperlightGuestError, Result};
use crate::exit::out32;

impl GuestHandle {
/// Get user memory region as bytes.
pub fn read_n_bytes_from_user_memory(&self, num: u64) -> Result<Vec<u8>> {
let peb_ptr = self.peb().unwrap();
let user_memory_region_ptr = unsafe { (*peb_ptr).init_data.ptr as *mut u8 };
let user_memory_region_size = unsafe { (*peb_ptr).init_data.size };

if num > user_memory_region_size {
return Err(HyperlightGuestError::new(
ErrorCode::GuestError,
format!(
"Requested {} bytes from user memory, but only {} bytes are available",
num, user_memory_region_size
),
));
} else {
let user_memory_region_slice =
unsafe { core::slice::from_raw_parts(user_memory_region_ptr, num as usize) };
let user_memory_region_bytes = user_memory_region_slice.to_vec();

Ok(user_memory_region_bytes)
}
}

/// Get a return value from a host function call.
/// This usually requires a host function to be called first using
/// `call_host_function_internal`.
Expand Down
5 changes: 5 additions & 0 deletions src/hyperlight_guest_bin/src/host_comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ pub fn get_host_function_details() -> HostFunctionDetails {
handle.get_host_function_details()
}

pub fn read_n_bytes_from_user_memory(num: u64) -> Result<Vec<u8>> {
let handle = unsafe { GUEST_HANDLE };
handle.read_n_bytes_from_user_memory(num)
}

/// Print a message using the host's print function.
///
/// This function requires memory to be setup to be used. In particular, the
Expand Down
94 changes: 89 additions & 5 deletions src/hyperlight_host/src/mem/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ use rand::{RngCore, rng};
use tracing::{Span, instrument};

use super::memory_region::MemoryRegionType::{
Code, GuardPage, Heap, HostFunctionDefinitions, InputData, OutputData, PageTables, Peb, Stack,
Code, GuardPage, Heap, HostFunctionDefinitions, InitData, InputData, OutputData, PageTables,
Peb, Stack,
};
use super::memory_region::{
DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryRegionFlags, MemoryRegionVecBuilder,
};
use super::memory_region::{MemoryRegion, MemoryRegionFlags, MemoryRegionVecBuilder};
use super::mgr::AMOUNT_OF_MEMORY_PER_PT;
use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, SharedMemory};
use crate::error::HyperlightError::{GuestOffsetIsInvalid, MemoryRequestTooBig};
use crate::sandbox::SandboxConfiguration;
use crate::{Result, new_error};

// +-------------------------------------------+
// | Init Data | (GuestBlob size)
// +-------------------------------------------+
// | Guest (User) Stack |
// +-------------------------------------------+
Expand All @@ -56,6 +61,8 @@ use crate::{Result, new_error};
// | PML4 |
// +-------------------------------------------+ 0x0_000

/// - `InitData` - some extra data that can be loaded onto the sandbox during
/// initialization.
///
/// - `HostDefinitions` - the length of this is the `HostFunctionDefinitionSize`
/// field from `SandboxConfiguration`
Expand All @@ -82,6 +89,7 @@ pub(crate) struct SandboxMemoryLayout {
pub(super) stack_size: usize,
/// The heap size of this sandbox.
pub(super) heap_size: usize,
init_data_size: usize,

/// The following fields are offsets to the actual PEB struct fields.
/// They are used when writing the PEB struct itself
Expand All @@ -92,6 +100,7 @@ pub(crate) struct SandboxMemoryLayout {
pub(super) peb_host_function_definitions_offset: usize,
peb_input_data_offset: usize,
peb_output_data_offset: usize,
peb_init_data_offset: usize,
peb_heap_data_offset: usize,
peb_guest_stack_data_offset: usize,

Expand All @@ -103,6 +112,7 @@ pub(crate) struct SandboxMemoryLayout {
guest_heap_buffer_offset: usize,
guard_page_offset: usize,
guest_user_stack_buffer_offset: usize, // the lowest address of the user stack
init_data_offset: usize,

// other
pub(crate) peb_address: usize,
Expand All @@ -111,6 +121,7 @@ pub(crate) struct SandboxMemoryLayout {
total_page_table_size: usize,
// The offset in the sandbox memory where the code starts
guest_code_offset: usize,
pub(crate) init_data_permissions: Option<MemoryRegionFlags>,
}

impl Debug for SandboxMemoryLayout {
Expand All @@ -122,6 +133,10 @@ impl Debug for SandboxMemoryLayout {
)
.field("Stack Size", &format_args!("{:#x}", self.stack_size))
.field("Heap Size", &format_args!("{:#x}", self.heap_size))
.field(
"Init Data Size",
&format_args!("{:#x}", self.init_data_size),
)
.field("PEB Address", &format_args!("{:#x}", self.peb_address))
.field("PEB Offset", &format_args!("{:#x}", self.peb_offset))
.field("Code Size", &format_args!("{:#x}", self.code_size))
Expand Down Expand Up @@ -149,6 +164,10 @@ impl Debug for SandboxMemoryLayout {
"Output Data Offset",
&format_args!("{:#x}", self.peb_output_data_offset),
)
.field(
"Init Data Offset",
&format_args!("{:#x}", self.peb_init_data_offset),
)
.field(
"Guest Heap Offset",
&format_args!("{:#x}", self.peb_heap_data_offset),
Expand Down Expand Up @@ -181,6 +200,10 @@ impl Debug for SandboxMemoryLayout {
"Guest User Stack Buffer Offset",
&format_args!("{:#x}", self.guest_user_stack_buffer_offset),
)
.field(
"Init Data Offset",
&format_args!("{:#x}", self.init_data_offset),
)
.field(
"Page Table Size",
&format_args!("{:#x}", self.total_page_table_size),
Expand Down Expand Up @@ -231,6 +254,8 @@ impl SandboxMemoryLayout {
code_size: usize,
stack_size: usize,
heap_size: usize,
init_data_size: usize,
init_data_permissions: Option<MemoryRegionFlags>,
) -> Result<Self> {
let total_page_table_size =
Self::get_total_page_table_size(cfg, code_size, stack_size, heap_size);
Expand All @@ -244,6 +269,7 @@ impl SandboxMemoryLayout {
let peb_code_pointer_offset = peb_offset + offset_of!(HyperlightPEB, code_ptr);
let peb_input_data_offset = peb_offset + offset_of!(HyperlightPEB, input_stack);
let peb_output_data_offset = peb_offset + offset_of!(HyperlightPEB, output_stack);
let peb_init_data_offset = peb_offset + offset_of!(HyperlightPEB, init_data);
let peb_heap_data_offset = peb_offset + offset_of!(HyperlightPEB, guest_heap);
let peb_guest_stack_data_offset = peb_offset + offset_of!(HyperlightPEB, guest_stack);
let peb_host_function_definitions_offset =
Expand Down Expand Up @@ -275,6 +301,7 @@ impl SandboxMemoryLayout {
let guest_user_stack_buffer_offset = guard_page_offset + PAGE_SIZE_USIZE;
// round up stack size to page size. This is needed for MemoryRegion
let stack_size_rounded = round_up_to(stack_size, PAGE_SIZE_USIZE);
let init_data_offset = guest_user_stack_buffer_offset + stack_size_rounded;

Ok(Self {
peb_offset,
Expand All @@ -286,6 +313,7 @@ impl SandboxMemoryLayout {
peb_host_function_definitions_offset,
peb_input_data_offset,
peb_output_data_offset,
peb_init_data_offset,
peb_heap_data_offset,
peb_guest_stack_data_offset,
sandbox_memory_config: cfg,
Expand All @@ -299,6 +327,9 @@ impl SandboxMemoryLayout {
guard_page_offset,
total_page_table_size,
guest_code_offset,
init_data_offset,
init_data_size,
init_data_permissions,
})
}

Expand All @@ -325,6 +356,13 @@ impl SandboxMemoryLayout {
self.peb_host_function_definitions_offset + size_of::<u64>()
}

/// Get the offset in guest memory to the init data size
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
pub(super) fn get_init_data_size_offset(&self) -> usize {
// The init data size is the first field in the `GuestMemoryRegion` struct
self.peb_init_data_offset
}

/// Get the offset in guest memory to the minimum guest stack address.
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
fn get_min_guest_stack_address_offset(&self) -> usize {
Expand Down Expand Up @@ -361,6 +399,14 @@ impl SandboxMemoryLayout {
self.get_output_data_size_offset() + size_of::<u64>()
}

/// Get the offset in guest memory to the init data pointer.
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
pub(super) fn get_init_data_pointer_offset(&self) -> usize {
// The init data pointer is immediately after the init data size field,
// which is a `u64`.
self.get_init_data_size_offset() + size_of::<u64>()
}

/// Get the offset in guest memory to the start of output data.
///
/// This function exists to accommodate the macro that generates C API
Expand Down Expand Up @@ -444,7 +490,7 @@ impl SandboxMemoryLayout {
/// layout.
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
fn get_unaligned_memory_size(&self) -> usize {
self.get_top_of_user_stack_offset() + self.stack_size
self.init_data_offset + self.init_data_size
}

/// get the code offset
Expand Down Expand Up @@ -682,12 +728,31 @@ impl SandboxMemoryLayout {
}

// stack
let final_offset = builder.push_page_aligned(
let init_data_offset = builder.push_page_aligned(
self.get_guest_stack_size(),
MemoryRegionFlags::READ | MemoryRegionFlags::WRITE,
Stack,
);

let expected_init_data_offset = TryInto::<usize>::try_into(self.init_data_offset)?;

if init_data_offset != expected_init_data_offset {
return Err(new_error!(
"Init Data offset does not match expected Init Data offset expected: {}, actual: {}",
expected_init_data_offset,
init_data_offset
));
}

let final_offset = if self.init_data_size > 0 {
let mem_flags = self
.init_data_permissions
.unwrap_or(DEFAULT_GUEST_BLOB_MEM_FLAGS);
builder.push_page_aligned(self.init_data_size, mem_flags, InitData)
} else {
init_data_offset
};

let expected_final_offset = TryInto::<usize>::try_into(self.get_memory_size()?)?;

if final_offset != expected_final_offset {
Expand All @@ -701,6 +766,16 @@ impl SandboxMemoryLayout {
Ok(builder.build())
}

#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
pub(crate) fn write_init_data(
&self,
shared_mem: &mut ExclusiveSharedMemory,
bytes: &[u8],
) -> Result<()> {
shared_mem.copy_from_slice(bytes, self.init_data_offset)?;
Ok(())
}

/// Write the finished memory layout to `shared_mem` and return
/// `Ok` if successful.
///
Expand Down Expand Up @@ -767,6 +842,14 @@ impl SandboxMemoryLayout {
let addr = get_address!(output_data_buffer_offset);
shared_mem.write_u64(self.get_output_data_pointer_offset(), addr)?;

// Set up init data pointer
shared_mem.write_u64(
self.get_init_data_size_offset(),
(self.get_unaligned_memory_size() - self.init_data_offset).try_into()?,
)?;
let addr = get_address!(init_data_offset);
shared_mem.write_u64(self.get_init_data_pointer_offset(), addr)?;

// Set up heap buffer pointer
let addr = get_address!(guest_heap_buffer_offset);
shared_mem.write_u64(self.get_heap_size_offset(), self.heap_size.try_into()?)?;
Expand Down Expand Up @@ -869,7 +952,8 @@ mod tests {
#[test]
fn test_get_memory_size() {
let sbox_cfg = SandboxConfiguration::default();
let sbox_mem_layout = SandboxMemoryLayout::new(sbox_cfg, 4096, 2048, 4096).unwrap();
let sbox_mem_layout =
SandboxMemoryLayout::new(sbox_cfg, 4096, 2048, 4096, 0, None).unwrap();
assert_eq!(
sbox_mem_layout.get_memory_size().unwrap(),
get_expected_memory_size(&sbox_mem_layout)
Expand Down
30 changes: 30 additions & 0 deletions src/hyperlight_host/src/mem/memory_region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ use mshv_bindings::{hv_x64_memory_intercept_message, mshv_user_mem_region};
#[cfg(target_os = "windows")]
use windows::Win32::System::Hypervisor::{self, WHV_MEMORY_ACCESS_TYPE};

use super::mgr::{PAGE_NX, PAGE_PRESENT, PAGE_RW, PAGE_USER};

pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionFlags::READ;

bitflags! {
/// flags representing memory permission for a memory region
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Expand All @@ -60,6 +64,30 @@ bitflags! {
}
}

impl MemoryRegionFlags {
pub(crate) fn translate_flags(&self) -> u64 {
let mut page_flags = 0;

page_flags |= PAGE_PRESENT; // Mark page as present

if self.contains(MemoryRegionFlags::WRITE) {
page_flags |= PAGE_RW; // Allow read/write
}

if self.contains(MemoryRegionFlags::STACK_GUARD) {
page_flags |= PAGE_RW; // The guard page is marked RW so that if it gets written to we can detect it in the host
}

if self.contains(MemoryRegionFlags::EXECUTE) {
page_flags |= PAGE_USER; // Allow user access
} else {
page_flags |= PAGE_NX; // Mark as non-executable if EXECUTE is not set
}

page_flags
}
}

impl std::fmt::Display for MemoryRegionFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_empty() {
Expand Down Expand Up @@ -129,6 +157,8 @@ pub enum MemoryRegionType {
PageTables,
/// The region contains the guest's code
Code,
/// The region contains the guest's init data
InitData,
/// The region contains the PEB
Peb,
/// The region contains the Host Function Definitions
Expand Down
Loading
Loading