diff --git a/src/hyperlight_host/examples/map-file-cow-test/main.rs b/src/hyperlight_host/examples/map-file-cow-test/main.rs index 446420e0d..5d4bcdf86 100644 --- a/src/hyperlight_host/examples/map-file-cow-test/main.rs +++ b/src/hyperlight_host/examples/map-file-cow-test/main.rs @@ -18,9 +18,12 @@ limitations under the License. // section mapping via MapViewOfFileNuma2 on Windows (the surrogate process // must be able to map the file-backed section). // -// Before the NULL DACL fix, this fails on Windows with: +// Covers both a page-aligned file and an intentionally unaligned file. +// Before fix: the unaligned case fails on Windows with // HyperlightVmError(MapRegion(MapMemory(SurrogateProcess( // "MapViewOfFileNuma2 failed: ... Access is denied.")))) +// because the file-backed section has max_size == file_size (< the +// page-aligned host_size the surrogate requests). // // Run: // cargo run --release --example map-file-cow-test @@ -31,32 +34,48 @@ use std::path::Path; use hyperlight_host::sandbox::SandboxConfiguration; use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; -fn main() -> hyperlight_host::Result<()> { +fn run_once(test_file: &Path, label: &str) -> hyperlight_host::Result<()> { let mut config = SandboxConfiguration::default(); config.set_heap_size(4 * 1024 * 1024); config.set_scratch_size(64 * 1024 * 1024); - // Create a test file to map (simulating an initrd). - let test_file = std::env::temp_dir().join("hl_map_file_cow_test.bin"); - std::fs::write(&test_file, vec![0xABu8; 8192]).unwrap(); - let mut usbox = UninitializedSandbox::new( hyperlight_host::GuestBinary::FilePath( hyperlight_testing::simple_guest_as_string().unwrap(), ), Some(config), )?; - eprintln!("[test] UninitializedSandbox::new OK"); + eprintln!("[{label}] UninitializedSandbox::new OK"); - usbox.map_file_cow(Path::new(&test_file), 0xC000_0000, Some("test"))?; - eprintln!("[test] map_file_cow OK"); + usbox.map_file_cow(test_file, 0xC000_0000, Some(label))?; + eprintln!( + "[{label}] map_file_cow OK ({} bytes)", + std::fs::metadata(test_file)?.len() + ); let mut mu: MultiUseSandbox = usbox.evolve()?; - eprintln!("[test] evolve OK"); + eprintln!("[{label}] evolve OK"); + + let result: String = mu.call("Echo", format!("{label}: map_file_cow works!"))?; + eprintln!("[{label}] guest returned: {result}"); + Ok(()) +} + +fn main() -> hyperlight_host::Result<()> { + let aligned = std::env::temp_dir().join("hl_map_file_cow_aligned.bin"); + let unaligned = std::env::temp_dir().join("hl_map_file_cow_unaligned.bin"); + + // 2 full pages. + std::fs::write(&aligned, vec![0xABu8; 8192]).unwrap(); + // Deliberately unaligned: not a multiple of 4 KiB. Must succeed + // (Windows: requires the surrogate to map "to end of section" rather + // than the caller's page-aligned host_size). + std::fs::write(&unaligned, vec![0xCDu8; 8193]).unwrap(); - let result: String = mu.call("Echo", "map_file_cow works!".to_string())?; - eprintln!("[test] guest returned: {result}"); + run_once(&aligned, "aligned")?; + run_once(&unaligned, "unaligned")?; - let _ = std::fs::remove_file(&test_file); + let _ = std::fs::remove_file(&aligned); + let _ = std::fs::remove_file(&unaligned); Ok(()) } diff --git a/src/hyperlight_host/src/hypervisor/surrogate_process.rs b/src/hyperlight_host/src/hypervisor/surrogate_process.rs index 6c439dbf6..481aa44e8 100644 --- a/src/hyperlight_host/src/hypervisor/surrogate_process.rs +++ b/src/hyperlight_host/src/hypervisor/surrogate_process.rs @@ -103,13 +103,28 @@ impl SurrogateProcess { // Use MapViewOfFile2 to map memory into the surrogate process, the MapViewOfFile2 API is implemented in as an inline function in a windows header file // (see https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile2#remarks) so we use the same API it uses in the header file here instead of // MapViewOfFile2 which does not exist in the rust crate (see https://github.com/microsoft/windows-rs/issues/2595) + // + // For ReadOnlyFile mappings backed by a file-sized section + // (max_size == file_size, potentially not page-aligned), + // passing the caller's page-aligned `host_size` would exceed + // the section size and fail with ERROR_ACCESS_DENIED. Pass 0 + // instead to request "map to end of section": Windows returns + // a page-aligned view and zero-fills the tail of the final + // page, matching POSIX mmap semantics. For SandboxMemory + // sections the section size is already page-aligned and set + // by the caller, so pass host_size explicitly (guard-page + // bookkeeping below depends on knowing the exact extent). + let bytes_to_map = match mapping { + SurrogateMapping::SandboxMemory => host_size, + SurrogateMapping::ReadOnlyFile => 0, + }; let surrogate_base = unsafe { MapViewOfFileNuma2( handle.into(), self.process_handle.into(), 0, None, - host_size, + bytes_to_map, 0, page_protection.0, NUMA_NO_PREFERRED_NODE,