Skip to content

Commit fa1a5ac

Browse files
committed
fix(whp): use NULL DACL for map_file_cow file mapping sections
File-backed sections created with the default DACL via CreateFileMappingW fail with ERROR_ACCESS_DENIED when mapped into a surrogate process via MapViewOfFileNuma2 on modern Windows. Create the section with a NULL DACL security descriptor which grants unrestricted access. The mapping remains file-backed and zero-copy (FILE_MAP_READ, no data copied). Also adds a map-file-cow-test example that exercises the full lifecycle (create → map_file_cow → evolve → call) and would fail before this change on Windows. Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent 108fd89 commit fa1a5ac

2 files changed

Lines changed: 90 additions & 1 deletion

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<<<<<<< Updated upstream
2+
=======
3+
/*
4+
Copyright 2025 The Hyperlight Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
>>>>>>> Stashed changes
19+
// Test that map_file_cow works end-to-end: UninitializedSandbox::new →
20+
// map_file_cow → evolve → guest function call. Exercises the cross-process
21+
// section mapping via MapViewOfFileNuma2 on Windows (the surrogate process
22+
// must be able to map the file-backed section).
23+
//
24+
// Before the NULL DACL fix, this fails on Windows with:
25+
// HyperlightVmError(MapRegion(MapMemory(SurrogateProcess(
26+
// "MapViewOfFileNuma2 failed: ... Access is denied."))))
27+
//
28+
// Run:
29+
// cargo run --release --example map-file-cow-test
30+
31+
#![allow(clippy::disallowed_macros)]
32+
use hyperlight_host::sandbox::SandboxConfiguration;
33+
use hyperlight_host::{MultiUseSandbox, UninitializedSandbox};
34+
use std::path::Path;
35+
36+
fn main() -> hyperlight_host::Result<()> {
37+
let mut config = SandboxConfiguration::default();
38+
config.set_heap_size(4 * 1024 * 1024);
39+
config.set_scratch_size(64 * 1024 * 1024);
40+
41+
// Create a test file to map (simulating an initrd).
42+
let test_file = std::env::temp_dir().join("hl_map_file_cow_test.bin");
43+
std::fs::write(&test_file, vec![0xABu8; 8192]).unwrap();
44+
45+
let mut usbox = UninitializedSandbox::new(
46+
hyperlight_host::GuestBinary::FilePath(
47+
hyperlight_testing::simple_guest_as_string().unwrap(),
48+
),
49+
Some(config),
50+
)?;
51+
eprintln!("[test] UninitializedSandbox::new OK");
52+
53+
usbox.map_file_cow(Path::new(&test_file), 0xC000_0000, Some("test"))?;
54+
eprintln!("[test] map_file_cow OK");
55+
56+
let mut mu: MultiUseSandbox = usbox.evolve()?;
57+
eprintln!("[test] evolve OK");
58+
59+
let result: String = mu.call("Echo", "map_file_cow works!".to_string())?;
60+
eprintln!("[test] guest returned: {result}");
61+
62+
let _ = std::fs::remove_file(&test_file);
63+
Ok(())
64+
}

src/hyperlight_host/src/sandbox/file_mapping.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ pub(crate) fn prepare_file_cow(
295295
use std::os::windows::io::AsRawHandle;
296296

297297
use windows::Win32::Foundation::HANDLE;
298+
use windows::Win32::Security::{PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES};
298299
use windows::Win32::System::Memory::{
299300
CreateFileMappingW, FILE_MAP_READ, MapViewOfFile, PAGE_READONLY,
300301
};
@@ -313,12 +314,36 @@ pub(crate) fn prepare_file_cow(
313314

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

317+
// Build a security descriptor with a NULL DACL (unrestricted
318+
// access) so the surrogate process can map the section via
319+
// MapViewOfFileNuma2. File-backed sections created with the
320+
// default DACL fail with ERROR_ACCESS_DENIED when mapped
321+
// cross-process on modern Windows.
322+
let mut sd_bytes = [0u8; 40]; // SECURITY_DESCRIPTOR_MIN_LENGTH
323+
unsafe {
324+
let psd = PSECURITY_DESCRIPTOR(sd_bytes.as_mut_ptr() as *mut _);
325+
windows::Win32::Security::InitializeSecurityDescriptor(
326+
psd, 1, // SECURITY_DESCRIPTOR_REVISION
327+
)
328+
.map_err(|e| {
329+
HyperlightError::Error(format!("InitializeSecurityDescriptor failed: {e}"))
330+
})?;
331+
windows::Win32::Security::SetSecurityDescriptorDacl(psd, true, None, false).map_err(
332+
|e| HyperlightError::Error(format!("SetSecurityDescriptorDacl failed: {e}")),
333+
)?;
334+
}
335+
let sa = SECURITY_ATTRIBUTES {
336+
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
337+
lpSecurityDescriptor: sd_bytes.as_mut_ptr() as *mut _,
338+
bInheritHandle: false.into(),
339+
};
340+
316341
// Create a read-only file mapping object backed by the actual file.
317342
// Pass 0,0 for size to use the file's actual size — Windows will
318343
// NOT extend a read-only file, so requesting page-aligned size
319344
// would fail for files smaller than one page.
320345
let mapping_handle =
321-
unsafe { CreateFileMappingW(file_handle, None, PAGE_READONLY, 0, 0, None) }
346+
unsafe { CreateFileMappingW(file_handle, Some(&sa), PAGE_READONLY, 0, 0, None) }
322347
.map_err(|e| HyperlightError::Error(format!("CreateFileMappingW failed: {e}")))?;
323348

324349
// Map a read-only view into the host process.

0 commit comments

Comments
 (0)