From fa1a5acc05d3764e2b8f2146d6a00a5abc79177a Mon Sep 17 00:00:00 2001 From: danbugs Date: Fri, 17 Apr 2026 07:22:23 +0000 Subject: [PATCH 1/4] fix(whp): use NULL DACL for map_file_cow file mapping sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../examples/map-file-cow-test/main.rs | 64 +++++++++++++++++++ .../src/sandbox/file_mapping.rs | 27 +++++++- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/hyperlight_host/examples/map-file-cow-test/main.rs diff --git a/src/hyperlight_host/examples/map-file-cow-test/main.rs b/src/hyperlight_host/examples/map-file-cow-test/main.rs new file mode 100644 index 000000000..e4aaaacdb --- /dev/null +++ b/src/hyperlight_host/examples/map-file-cow-test/main.rs @@ -0,0 +1,64 @@ +<<<<<<< Updated upstream +======= +/* +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. +*/ +>>>>>>> Stashed changes +// 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 hyperlight_host::sandbox::SandboxConfiguration; +use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; +use std::path::Path; + +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(()) +} diff --git a/src/hyperlight_host/src/sandbox/file_mapping.rs b/src/hyperlight_host/src/sandbox/file_mapping.rs index 1ebe1f262..e4a77a146 100644 --- a/src/hyperlight_host/src/sandbox/file_mapping.rs +++ b/src/hyperlight_host/src/sandbox/file_mapping.rs @@ -295,6 +295,7 @@ 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}; use windows::Win32::System::Memory::{ CreateFileMappingW, FILE_MAP_READ, MapViewOfFile, PAGE_READONLY, }; @@ -313,12 +314,36 @@ 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. + let mut sd_bytes = [0u8; 40]; // SECURITY_DESCRIPTOR_MIN_LENGTH + unsafe { + let psd = PSECURITY_DESCRIPTOR(sd_bytes.as_mut_ptr() as *mut _); + windows::Win32::Security::InitializeSecurityDescriptor( + psd, 1, // SECURITY_DESCRIPTOR_REVISION + ) + .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::() as u32, + lpSecurityDescriptor: sd_bytes.as_mut_ptr() as *mut _, + 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. From 75b36b8192efd42485f3b03d265033f2782541a1 Mon Sep 17 00:00:00 2001 From: Tomasz Andrzejak Date: Fri, 17 Apr 2026 09:33:34 +0200 Subject: [PATCH 2/4] fix: remove conflict markers Signed-off-by: Tomasz Andrzejak --- src/hyperlight_host/examples/map-file-cow-test/main.rs | 3 --- 1 file changed, 3 deletions(-) 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 e4aaaacdb..1d11d29f0 100644 --- a/src/hyperlight_host/examples/map-file-cow-test/main.rs +++ b/src/hyperlight_host/examples/map-file-cow-test/main.rs @@ -1,5 +1,3 @@ -<<<<<<< Updated upstream -======= /* Copyright 2025 The Hyperlight Authors. @@ -15,7 +13,6 @@ 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. */ ->>>>>>> Stashed changes // 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 From ca6a2628225bfa5fa2edaf39e857b37eed674ff0 Mon Sep 17 00:00:00 2001 From: Tomasz Andrzejak Date: Fri, 17 Apr 2026 10:16:27 +0200 Subject: [PATCH 3/4] fix: run cargo fmt Signed-off-by: Tomasz Andrzejak --- src/hyperlight_host/examples/map-file-cow-test/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 1d11d29f0..446420e0d 100644 --- a/src/hyperlight_host/examples/map-file-cow-test/main.rs +++ b/src/hyperlight_host/examples/map-file-cow-test/main.rs @@ -26,9 +26,10 @@ limitations under the License. // 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}; -use std::path::Path; fn main() -> hyperlight_host::Result<()> { let mut config = SandboxConfiguration::default(); From c24e4f3575d8f5cab2c390a3baafe7690d5d756c Mon Sep 17 00:00:00 2001 From: Tomasz Andrzejak Date: Fri, 17 Apr 2026 10:59:18 +0200 Subject: [PATCH 4/4] fix: use descriptor types for proper alignment Signed-off-by: Tomasz Andrzejak --- src/hyperlight_host/src/sandbox/file_mapping.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/hyperlight_host/src/sandbox/file_mapping.rs b/src/hyperlight_host/src/sandbox/file_mapping.rs index e4a77a146..0139fcdba 100644 --- a/src/hyperlight_host/src/sandbox/file_mapping.rs +++ b/src/hyperlight_host/src/sandbox/file_mapping.rs @@ -295,10 +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}; + 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(); @@ -319,11 +322,14 @@ pub(crate) fn prepare_file_cow( // MapViewOfFileNuma2. File-backed sections created with the // default DACL fail with ERROR_ACCESS_DENIED when mapped // cross-process on modern Windows. - let mut sd_bytes = [0u8; 40]; // SECURITY_DESCRIPTOR_MIN_LENGTH + // 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()); unsafe { - let psd = PSECURITY_DESCRIPTOR(sd_bytes.as_mut_ptr() as *mut _); windows::Win32::Security::InitializeSecurityDescriptor( - psd, 1, // SECURITY_DESCRIPTOR_REVISION + psd, + SECURITY_DESCRIPTOR_REVISION1, ) .map_err(|e| { HyperlightError::Error(format!("InitializeSecurityDescriptor failed: {e}")) @@ -334,7 +340,7 @@ pub(crate) fn prepare_file_cow( } let sa = SECURITY_ATTRIBUTES { nLength: std::mem::size_of::() as u32, - lpSecurityDescriptor: sd_bytes.as_mut_ptr() as *mut _, + lpSecurityDescriptor: psd.0, bInheritHandle: false.into(), };