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
62 changes: 62 additions & 0 deletions src/hyperlight_host/examples/map-file-cow-test/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright 2025 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Test that map_file_cow works end-to-end: UninitializedSandbox::new →
// map_file_cow → evolve → guest function call. Exercises the cross-process
// 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:
// HyperlightVmError(MapRegion(MapMemory(SurrogateProcess(
// "MapViewOfFileNuma2 failed: ... Access is denied."))))
//
// Run:
// cargo run --release --example map-file-cow-test

#![allow(clippy::disallowed_macros)]
use std::path::Path;

use hyperlight_host::sandbox::SandboxConfiguration;
use hyperlight_host::{MultiUseSandbox, UninitializedSandbox};

fn main() -> 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");

usbox.map_file_cow(Path::new(&test_file), 0xC000_0000, Some("test"))?;
eprintln!("[test] map_file_cow OK");

let mut mu: MultiUseSandbox = usbox.evolve()?;
eprintln!("[test] evolve OK");

let result: String = mu.call("Echo", "map_file_cow works!".to_string())?;
eprintln!("[test] guest returned: {result}");

let _ = std::fs::remove_file(&test_file);
Ok(())
}
33 changes: 32 additions & 1 deletion src/hyperlight_host/src/sandbox/file_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,13 @@ pub(crate) fn prepare_file_cow(
use std::os::windows::io::AsRawHandle;

use windows::Win32::Foundation::HANDLE;
use windows::Win32::Security::{
PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR,
};
use windows::Win32::System::Memory::{
CreateFileMappingW, FILE_MAP_READ, MapViewOfFile, PAGE_READONLY,
};
use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION1;

let file = std::fs::File::options().read(true).open(file_path)?;
let file_size = file.metadata()?.len();
Expand All @@ -313,12 +317,39 @@ pub(crate) fn prepare_file_cow(

let file_handle = HANDLE(file.as_raw_handle());

// Build a security descriptor with a NULL DACL (unrestricted
// access) so the surrogate process can map the section via
// MapViewOfFileNuma2. File-backed sections created with the
// default DACL fail with ERROR_ACCESS_DENIED when mapped
// cross-process on modern Windows.
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Security/struct.SECURITY_DESCRIPTOR.html
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/SystemServices/constant.SECURITY_DESCRIPTOR_REVISION1.html
let mut sd = SECURITY_DESCRIPTOR::default();
let psd = PSECURITY_DESCRIPTOR(std::ptr::addr_of_mut!(sd).cast());
Comment thread
jprendes marked this conversation as resolved.
unsafe {
windows::Win32::Security::InitializeSecurityDescriptor(
psd,
SECURITY_DESCRIPTOR_REVISION1,
)
.map_err(|e| {
HyperlightError::Error(format!("InitializeSecurityDescriptor failed: {e}"))
})?;
windows::Win32::Security::SetSecurityDescriptorDacl(psd, true, None, false).map_err(
|e| HyperlightError::Error(format!("SetSecurityDescriptorDacl failed: {e}")),
)?;
}
let sa = SECURITY_ATTRIBUTES {
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: psd.0,
bInheritHandle: false.into(),
};

// Create a read-only file mapping object backed by the actual file.
// Pass 0,0 for size to use the file's actual size — Windows will
// NOT extend a read-only file, so requesting page-aligned size
// would fail for files smaller than one page.
let mapping_handle =
unsafe { CreateFileMappingW(file_handle, None, PAGE_READONLY, 0, 0, None) }
unsafe { CreateFileMappingW(file_handle, Some(&sa), PAGE_READONLY, 0, 0, None) }
.map_err(|e| HyperlightError::Error(format!("CreateFileMappingW failed: {e}")))?;

// Map a read-only view into the host process.
Expand Down
Loading