Skip to content

Commit 17cddf3

Browse files
danbugsandreiltd
andauthored
fix(whp): use NULL DACL for map_file_cow file mapping sections (#1386)
* 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> * fix: remove conflict markers Signed-off-by: Tomasz Andrzejak <andreiltd@gmail.com> * fix: run cargo fmt Signed-off-by: Tomasz Andrzejak <andreiltd@gmail.com> * fix: use descriptor types for proper alignment Signed-off-by: Tomasz Andrzejak <andreiltd@gmail.com> --------- Signed-off-by: danbugs <danilochiarlone@gmail.com> Signed-off-by: Tomasz Andrzejak <andreiltd@gmail.com> Co-authored-by: Tomasz Andrzejak <andreiltd@gmail.com>
1 parent 108fd89 commit 17cddf3

2 files changed

Lines changed: 94 additions & 1 deletion

File tree

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

src/hyperlight_host/src/sandbox/file_mapping.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,13 @@ 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::{
299+
PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR,
300+
};
298301
use windows::Win32::System::Memory::{
299302
CreateFileMappingW, FILE_MAP_READ, MapViewOfFile, PAGE_READONLY,
300303
};
304+
use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION1;
301305

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

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

320+
// Build a security descriptor with a NULL DACL (unrestricted
321+
// access) so the surrogate process can map the section via
322+
// MapViewOfFileNuma2. File-backed sections created with the
323+
// default DACL fail with ERROR_ACCESS_DENIED when mapped
324+
// cross-process on modern Windows.
325+
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Security/struct.SECURITY_DESCRIPTOR.html
326+
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/SystemServices/constant.SECURITY_DESCRIPTOR_REVISION1.html
327+
let mut sd = SECURITY_DESCRIPTOR::default();
328+
let psd = PSECURITY_DESCRIPTOR(std::ptr::addr_of_mut!(sd).cast());
329+
unsafe {
330+
windows::Win32::Security::InitializeSecurityDescriptor(
331+
psd,
332+
SECURITY_DESCRIPTOR_REVISION1,
333+
)
334+
.map_err(|e| {
335+
HyperlightError::Error(format!("InitializeSecurityDescriptor failed: {e}"))
336+
})?;
337+
windows::Win32::Security::SetSecurityDescriptorDacl(psd, true, None, false).map_err(
338+
|e| HyperlightError::Error(format!("SetSecurityDescriptorDacl failed: {e}")),
339+
)?;
340+
}
341+
let sa = SECURITY_ATTRIBUTES {
342+
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
343+
lpSecurityDescriptor: psd.0,
344+
bInheritHandle: false.into(),
345+
};
346+
316347
// Create a read-only file mapping object backed by the actual file.
317348
// Pass 0,0 for size to use the file's actual size — Windows will
318349
// NOT extend a read-only file, so requesting page-aligned size
319350
// would fail for files smaller than one page.
320351
let mapping_handle =
321-
unsafe { CreateFileMappingW(file_handle, None, PAGE_READONLY, 0, 0, None) }
352+
unsafe { CreateFileMappingW(file_handle, Some(&sa), PAGE_READONLY, 0, 0, None) }
322353
.map_err(|e| HyperlightError::Error(format!("CreateFileMappingW failed: {e}")))?;
323354

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

0 commit comments

Comments
 (0)