Skip to content

Commit 89ce6eb

Browse files
authored
Additional changes to validate page table setup and multiple snadbox sizes (#1119)
Signed-off-by: Simon Davies <[email protected]>
1 parent eefad6f commit 89ce6eb

File tree

4 files changed

+239
-3
lines changed

4 files changed

+239
-3
lines changed

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, Ret
3030
use hyperlight_common::flatbuffer_wrappers::util::estimate_flatbuffer_capacity;
3131
use hyperlight_host::GuestBinary;
3232
use hyperlight_host::sandbox::{MultiUseSandbox, SandboxConfiguration, UninitializedSandbox};
33+
use hyperlight_testing::sandbox_sizes::{LARGE_HEAP_SIZE, MEDIUM_HEAP_SIZE, SMALL_HEAP_SIZE};
3334
use hyperlight_testing::{c_simple_guest_as_string, simple_guest_as_string};
3435

3536
/// Sandbox heap size configurations for benchmarking.
@@ -54,17 +55,17 @@ impl SandboxSize {
5455
Self::Default => None,
5556
Self::Small => {
5657
let mut cfg = SandboxConfiguration::default();
57-
cfg.set_heap_size(8 * 1024 * 1024); // 8 MB
58+
cfg.set_heap_size(SMALL_HEAP_SIZE);
5859
Some(cfg)
5960
}
6061
Self::Medium => {
6162
let mut cfg = SandboxConfiguration::default();
62-
cfg.set_heap_size(64 * 1024 * 1024); // 64 MB
63+
cfg.set_heap_size(MEDIUM_HEAP_SIZE);
6364
Some(cfg)
6465
}
6566
Self::Large => {
6667
let mut cfg = SandboxConfiguration::default();
67-
cfg.set_heap_size(256 * 1024 * 1024); // 256 MB
68+
cfg.set_heap_size(LARGE_HEAP_SIZE);
6869
Some(cfg)
6970
}
7071
}

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,202 @@ impl SandboxMemoryManager<HostSharedMemory> {
559559
}
560560
}
561561
}
562+
563+
#[cfg(test)]
564+
#[cfg(feature = "init-paging")]
565+
mod tests {
566+
use hyperlight_testing::sandbox_sizes::{LARGE_HEAP_SIZE, MEDIUM_HEAP_SIZE, SMALL_HEAP_SIZE};
567+
use hyperlight_testing::simple_guest_as_string;
568+
569+
use super::*;
570+
use crate::GuestBinary;
571+
use crate::mem::shared_mem::GuestSharedMemory;
572+
use crate::sandbox::SandboxConfiguration;
573+
use crate::sandbox::uninitialized::UninitializedSandbox;
574+
575+
/// Helper to create a sandbox with page tables set up and return the manager
576+
fn create_sandbox_with_page_tables(
577+
config: Option<SandboxConfiguration>,
578+
) -> Result<SandboxMemoryManager<GuestSharedMemory>> {
579+
let path = simple_guest_as_string().expect("failed to get simple guest path");
580+
let sandbox = UninitializedSandbox::new(GuestBinary::FilePath(path), config)
581+
.expect("failed to create sandbox");
582+
583+
let mem_size = sandbox.mgr.layout.get_memory_size()?;
584+
585+
// Build the shared memory to get GuestSharedMemory
586+
let (_host_mem, guest_mem) = sandbox.mgr.shared_mem.build();
587+
let mut mgr = SandboxMemoryManager {
588+
shared_mem: guest_mem,
589+
layout: sandbox.mgr.layout,
590+
load_addr: sandbox.mgr.load_addr,
591+
entrypoint_offset: sandbox.mgr.entrypoint_offset,
592+
mapped_rgns: sandbox.mgr.mapped_rgns,
593+
stack_cookie: sandbox.mgr.stack_cookie,
594+
abort_buffer: sandbox.mgr.abort_buffer,
595+
};
596+
597+
// Get regions and set up page tables
598+
let mut regions = mgr.layout.get_memory_regions(&mgr.shared_mem)?;
599+
let mem_size_u64 = u64::try_from(mem_size)?;
600+
// set_up_shared_memory builds the page tables in shared memory
601+
mgr.set_up_shared_memory(mem_size_u64, &mut regions)?;
602+
603+
Ok(mgr)
604+
}
605+
606+
/// Verify a range of pages all have the same expected flags
607+
fn verify_page_range(
608+
excl_mem: &mut ExclusiveSharedMemory,
609+
start_addr: usize,
610+
end_addr: usize,
611+
expected_flags: u64,
612+
region_name: &str,
613+
) -> Result<()> {
614+
let mut addr = start_addr;
615+
616+
while addr < end_addr {
617+
let p = addr >> 21;
618+
let i = (addr >> 12) & 0x1ff;
619+
let pte_idx = p * 512 + i;
620+
let offset = SandboxMemoryLayout::PT_OFFSET + (pte_idx * 8);
621+
622+
let pte_val = excl_mem.read_u64(offset)?;
623+
let expected_pte = (addr as u64) | expected_flags;
624+
625+
if pte_val != expected_pte {
626+
return Err(new_error!(
627+
"{} region: addr 0x{:x}: expected PTE 0x{:x}, got 0x{:x}",
628+
region_name,
629+
addr,
630+
expected_pte,
631+
pte_val
632+
));
633+
}
634+
635+
addr += 0x1000;
636+
}
637+
638+
Ok(())
639+
}
640+
641+
/// Get expected flags for a memory region type
642+
fn get_expected_flags(region: &MemoryRegion) -> u64 {
643+
match region.region_type {
644+
MemoryRegionType::Code => PAGE_PRESENT | PAGE_RW | PAGE_USER,
645+
MemoryRegionType::Stack => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX,
646+
#[cfg(feature = "executable_heap")]
647+
MemoryRegionType::Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER,
648+
#[cfg(not(feature = "executable_heap"))]
649+
MemoryRegionType::Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX,
650+
MemoryRegionType::GuardPage => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX,
651+
MemoryRegionType::InputData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
652+
MemoryRegionType::OutputData => PAGE_PRESENT | PAGE_RW | PAGE_NX,
653+
MemoryRegionType::Peb => PAGE_PRESENT | PAGE_RW | PAGE_NX,
654+
MemoryRegionType::HostFunctionDefinitions => PAGE_PRESENT | PAGE_NX,
655+
MemoryRegionType::PageTables => PAGE_PRESENT | PAGE_RW | PAGE_NX,
656+
MemoryRegionType::InitData => region.flags.translate_flags(),
657+
}
658+
}
659+
660+
/// Verify the complete paging structure for a sandbox configuration
661+
fn verify_paging_structure(name: &str, config: Option<SandboxConfiguration>) -> Result<()> {
662+
let mut mgr = create_sandbox_with_page_tables(config)?;
663+
664+
let regions = mgr.layout.get_memory_regions(&mgr.shared_mem)?;
665+
666+
mgr.shared_mem.with_exclusivity(|excl_mem| {
667+
// Verify PML4 entry (single entry pointing to PDPT)
668+
let pml4_val = excl_mem.read_u64(SandboxMemoryLayout::PML4_OFFSET)?;
669+
let expected_pml4 =
670+
SandboxMemoryLayout::PDPT_GUEST_ADDRESS as u64 | PAGE_PRESENT | PAGE_RW;
671+
if pml4_val != expected_pml4 {
672+
return Err(new_error!(
673+
"{}: PML4[0] incorrect: expected 0x{:x}, got 0x{:x}",
674+
name,
675+
expected_pml4,
676+
pml4_val
677+
));
678+
}
679+
680+
// Verify PDPT entry (single entry pointing to PD)
681+
let pdpt_val = excl_mem.read_u64(SandboxMemoryLayout::PDPT_OFFSET)?;
682+
let expected_pdpt =
683+
SandboxMemoryLayout::PD_GUEST_ADDRESS as u64 | PAGE_PRESENT | PAGE_RW;
684+
if pdpt_val != expected_pdpt {
685+
return Err(new_error!(
686+
"{}: PDPT[0] incorrect: expected 0x{:x}, got 0x{:x}",
687+
name,
688+
expected_pdpt,
689+
pdpt_val
690+
));
691+
}
692+
693+
// Verify all 512 PD entries (each pointing to a PT)
694+
for i in 0..512 {
695+
let offset = SandboxMemoryLayout::PD_OFFSET + (i * 8);
696+
let pd_val = excl_mem.read_u64(offset)?;
697+
let expected_pt_addr =
698+
SandboxMemoryLayout::PT_GUEST_ADDRESS as u64 + (i as u64 * 4096);
699+
let expected_pd = expected_pt_addr | PAGE_PRESENT | PAGE_RW;
700+
if pd_val != expected_pd {
701+
return Err(new_error!(
702+
"{}: PD[{}] incorrect: expected 0x{:x}, got 0x{:x}",
703+
name,
704+
i,
705+
expected_pd,
706+
pd_val
707+
));
708+
}
709+
}
710+
711+
// Verify PTEs for each memory region
712+
for region in &regions {
713+
let start = region.guest_region.start;
714+
let end = region.guest_region.end;
715+
let expected_flags = get_expected_flags(region);
716+
717+
verify_page_range(
718+
excl_mem,
719+
start,
720+
end,
721+
expected_flags,
722+
&format!("{} {:?}", name, region.region_type),
723+
)?;
724+
}
725+
726+
Ok(())
727+
})??;
728+
729+
Ok(())
730+
}
731+
732+
/// Test the complete paging structure (PML4, PDPT, PD, and all PTEs) for
733+
/// sandboxes of different sizes: default, small (8MB), medium (64MB), and large (256MB)
734+
#[test]
735+
fn test_page_table_contents() {
736+
let test_cases: [(&str, Option<SandboxConfiguration>); 4] = [
737+
("default", None),
738+
("small (8MB heap)", {
739+
let mut cfg = SandboxConfiguration::default();
740+
cfg.set_heap_size(SMALL_HEAP_SIZE);
741+
Some(cfg)
742+
}),
743+
("medium (64MB heap)", {
744+
let mut cfg = SandboxConfiguration::default();
745+
cfg.set_heap_size(MEDIUM_HEAP_SIZE);
746+
Some(cfg)
747+
}),
748+
("large (256MB heap)", {
749+
let mut cfg = SandboxConfiguration::default();
750+
cfg.set_heap_size(LARGE_HEAP_SIZE);
751+
Some(cfg)
752+
}),
753+
];
754+
755+
for (name, config) in test_cases {
756+
verify_paging_structure(name, config)
757+
.unwrap_or_else(|e| panic!("Page table verification failed for {}: {}", name, e));
758+
}
759+
}
760+
}

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ mod tests {
787787
use std::thread;
788788

789789
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
790+
use hyperlight_testing::sandbox_sizes::{LARGE_HEAP_SIZE, MEDIUM_HEAP_SIZE, SMALL_HEAP_SIZE};
790791
use hyperlight_testing::simple_guest_as_string;
791792

792793
#[cfg(target_os = "linux")]
@@ -1296,4 +1297,27 @@ mod tests {
12961297
};
12971298
assert_ne!(sandbox3.id, sandbox_id);
12981299
}
1300+
1301+
/// Test that sandboxes can be created and evolved with different heap sizes
1302+
#[test]
1303+
fn test_sandbox_creation_various_sizes() {
1304+
let test_cases: [(&str, u64); 3] = [
1305+
("small (8MB heap)", SMALL_HEAP_SIZE),
1306+
("medium (64MB heap)", MEDIUM_HEAP_SIZE),
1307+
("large (256MB heap)", LARGE_HEAP_SIZE),
1308+
];
1309+
1310+
for (name, heap_size) in test_cases {
1311+
let mut cfg = SandboxConfiguration::default();
1312+
cfg.set_heap_size(heap_size);
1313+
1314+
let path = simple_guest_as_string().unwrap();
1315+
let sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), Some(cfg))
1316+
.unwrap_or_else(|e| panic!("Failed to create {} sandbox: {}", name, e))
1317+
.evolve()
1318+
.unwrap_or_else(|e| panic!("Failed to evolve {} sandbox: {}", name, e));
1319+
1320+
drop(sbox);
1321+
}
1322+
}
12991323
}

src/hyperlight_testing/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,15 @@ pub fn simple_guest_for_fuzzing_as_string() -> Result<String> {
130130

131131
simple_guest_as_string()
132132
}
133+
134+
/// Standard sandbox heap sizes for benchmarking and testing.
135+
/// These constants define common heap sizes used across benchmarks and tests
136+
/// to ensure consistency.
137+
pub mod sandbox_sizes {
138+
/// Small heap size: 8 MB
139+
pub const SMALL_HEAP_SIZE: u64 = 8 * 1024 * 1024;
140+
/// Medium heap size: 64 MB
141+
pub const MEDIUM_HEAP_SIZE: u64 = 64 * 1024 * 1024;
142+
/// Large heap size: 256 MB
143+
pub const LARGE_HEAP_SIZE: u64 = 256 * 1024 * 1024;
144+
}

0 commit comments

Comments
 (0)