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
45 changes: 32 additions & 13 deletions src/hyperlight_host/examples/map-file-cow-test/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(())
}
17 changes: 16 additions & 1 deletion src/hyperlight_host/src/hypervisor/surrogate_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading