Skip to content

Commit c96bb27

Browse files
committed
When allocating a SurrogateProcess use MapViewOfFile2 instead of VirtualAllocEx
Signed-off-by: Simon Davies <simongdavies@users.noreply.github.com>
1 parent 917496b commit c96bb27

File tree

1 file changed

+70
-26
lines changed

1 file changed

+70
-26
lines changed

src/hyperlight_host/src/hypervisor/surrogate_process_manager.rs

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ use windows::Win32::System::JobObjects::{
3333
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
3434
};
3535
use windows::Win32::System::Memory::{
36-
VirtualAllocEx, VirtualProtectEx, MEM_COMMIT, MEM_RESERVE, PAGE_NOACCESS,
37-
PAGE_PROTECTION_FLAGS, PAGE_READWRITE,
36+
MapViewOfFileNuma2, VirtualProtectEx, PAGE_NOACCESS, PAGE_PROTECTION_FLAGS, PAGE_READWRITE,
3837
};
3938
use windows::Win32::System::Threading::{
4039
CreateProcessA, CREATE_SUSPENDED, PROCESS_INFORMATION, STARTUPINFOA,
@@ -79,7 +78,7 @@ const NUMBER_OF_SURROGATE_PROCESSES: usize = 512;
7978
/// There is, however, another API (WHvMapGpaRange2) that has a second
8079
/// parameter which is a handle to a process. This process merely has to exist,
8180
/// the memory being mapped from the host to the virtual machine is
82-
/// allocated/freed in this process using VirtualAllocEx/VirtualFreeEx.
81+
/// allocated/freed in this process using CreateFileMapping/MapViewOfFile.
8382
/// Memory for the HyperVisor partition is copied to and from the host process
8483
/// from/into the surrogate process in Sandbox before and after the VCPU is run.
8584
///
@@ -145,35 +144,54 @@ impl SurrogateProcessManager {
145144
&self,
146145
raw_size: usize,
147146
raw_source_address: *const c_void,
147+
mmap_file_handle: HandleWrapper,
148148
) -> Result<SurrogateProcess> {
149-
let process_handle: HANDLE = self.process_receiver.recv()?.into();
149+
let surrogate_process_handle: HANDLE = self.process_receiver.recv()?.into();
150+
let mapping_file_handle: HANDLE = mmap_file_handle.into();
150151

151-
// allocate memory
152+
// Allocate the memory by creating a view over the memory mapped file
153+
154+
// Use MapViewOfFile2 to map memoy into the surrogate process, the MapViewOfFile2 API is implemented in as an inline function in a windows header file
155+
// (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
156+
// MapViewOfFile2 which does not exist in the rust crate (see https://github.com/microsoft/windows-rs/issues/2595)
157+
const NUMA_NO_PREFERRED_NODE: u32 = 0xFFFFFFFF;
152158
let allocated_address = unsafe {
153-
VirtualAllocEx(
154-
process_handle,
159+
MapViewOfFileNuma2(
160+
mapping_file_handle,
161+
surrogate_process_handle,
162+
0,
155163
Some(raw_source_address),
156164
raw_size,
157-
MEM_COMMIT | MEM_RESERVE,
158-
PAGE_READWRITE,
165+
0,
166+
PAGE_READWRITE.0,
167+
NUMA_NO_PREFERRED_NODE,
159168
)
160169
};
161-
if allocated_address.is_null() {
170+
171+
if allocated_address.Value.is_null() {
172+
log_then_return!(
173+
"MapViewOfFileNuma2 failed for mem address {:?}",
174+
raw_source_address
175+
);
176+
}
177+
178+
if allocated_address.Value as *const c_void != raw_source_address {
162179
log_then_return!(
163-
"VirtualAllocEx failed for mem address {:?}",
180+
"Address Mismatch Allocated: {:?} Requested: {:?}",
181+
allocated_address.Value,
164182
raw_source_address
165183
);
166184
}
167185

168-
// set up guard page
186+
// set up guard pages
169187

170188
let mut unused_out_old_prot_flags = PAGE_PROTECTION_FLAGS(0);
171189

172190
// the first page of the raw_size is the guard page
173191
let first_guard_page_start = raw_source_address;
174192
if let Err(e) = unsafe {
175193
VirtualProtectEx(
176-
process_handle,
194+
surrogate_process_handle,
177195
first_guard_page_start,
178196
PAGE_SIZE_USIZE,
179197
PAGE_NOACCESS,
@@ -187,7 +205,7 @@ impl SurrogateProcessManager {
187205
let last_guard_page_start = unsafe { raw_source_address.add(raw_size - PAGE_SIZE_USIZE) };
188206
if let Err(e) = unsafe {
189207
VirtualProtectEx(
190-
process_handle,
208+
surrogate_process_handle,
191209
last_guard_page_start,
192210
PAGE_SIZE_USIZE,
193211
PAGE_NOACCESS,
@@ -197,7 +215,10 @@ impl SurrogateProcessManager {
197215
log_then_return!(WindowsAPIError(e.clone()));
198216
}
199217

200-
Ok(SurrogateProcess::new(allocated_address, process_handle))
218+
Ok(SurrogateProcess::new(
219+
allocated_address.Value,
220+
surrogate_process_handle,
221+
))
201222
}
202223

203224
/// Returns a surrogate process to the pool of surrogate processes.
@@ -391,15 +412,17 @@ mod tests {
391412
use std::thread;
392413
use std::time::{Duration, Instant};
393414

415+
use hyperlight_common::mem::PAGE_SIZE_USIZE;
394416
use rand::{thread_rng, Rng};
395417
use serial_test::serial;
396-
use windows::Win32::Foundation::BOOL;
418+
use windows::Win32::Foundation::{CloseHandle, BOOL, HANDLE, INVALID_HANDLE_VALUE};
397419
use windows::Win32::System::Diagnostics::ToolHelp::{
398420
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
399421
};
400422
use windows::Win32::System::JobObjects::IsProcessInJob;
401423
use windows::Win32::System::Memory::{
402-
VirtualAlloc, VirtualFree, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE,
424+
CreateFileMappingA, MapViewOfFile, UnmapViewOfFile, FILE_MAP_ALL_ACCESS, PAGE_READWRITE,
425+
SEC_COMMIT,
403426
};
404427

405428
use super::*;
@@ -423,13 +446,29 @@ mod tests {
423446
// surrogate process, make sure we actually got one,
424447
// then put it back
425448
for p in 0..NUMBER_OF_SURROGATE_PROCESSES {
426-
let allocated_address = unsafe {
427-
VirtualAlloc(None, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
449+
let dwmaximumsizehigh = 0;
450+
let dwmaximumsizelow = (size & 0xFFFFFFFF) as u32;
451+
let handle = unsafe {
452+
CreateFileMappingA(
453+
INVALID_HANDLE_VALUE, // Causes the page file to be used as the backing store
454+
None,
455+
PAGE_READWRITE | SEC_COMMIT,
456+
dwmaximumsizehigh,
457+
dwmaximumsizelow,
458+
PCSTR::null(),
459+
)
460+
.unwrap()
428461
};
462+
463+
let addr = unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0) };
464+
429465
let timer = Instant::now();
430466
let surrogate_process = {
431-
let res = surrogate_process_manager
432-
.get_surrogate_process(size, allocated_address)?;
467+
let res = surrogate_process_manager.get_surrogate_process(
468+
size,
469+
addr.Value,
470+
HandleWrapper::from(handle),
471+
)?;
433472
let elapsed = timer.elapsed();
434473
// Print out the time it took to get the process if its greater than 150ms (this is just to allow us to see that threads are blocking on the process queue)
435474
if (elapsed.as_millis() as u64) > 150 {
@@ -452,10 +491,11 @@ mod tests {
452491
// dropping the surrogate process, as we do in the line
453492
// below, will return it to the surrogate process manager
454493
drop(surrogate_process);
455-
unsafe {
456-
let res = VirtualFree(allocated_address, 0, MEM_RELEASE);
457-
assert!(res.is_ok())
458-
}
494+
let res = unsafe { UnmapViewOfFile(addr) };
495+
assert!(res.is_ok(), "Failed to UnmapViewOfFile: {:?}", res.err());
496+
497+
let res = unsafe { CloseHandle(handle) };
498+
assert!(res.is_ok(), "Failed to CloseHandle: {:?}", res.err());
459499
}
460500
Ok(())
461501
});
@@ -514,7 +554,11 @@ mod tests {
514554
let mem = ExclusiveSharedMemory::new(SIZE).unwrap();
515555

516556
let process = mgr
517-
.get_surrogate_process(mem.raw_mem_size(), mem.raw_ptr() as *mut c_void)
557+
.get_surrogate_process(
558+
mem.raw_mem_size(),
559+
mem.raw_ptr() as *mut c_void,
560+
mem.get_mmap_file_handle(),
561+
)
518562
.unwrap();
519563

520564
let buffer = vec![0u8; SIZE];

0 commit comments

Comments
 (0)