Skip to content

Commit 60d9a17

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

File tree

1 file changed

+72
-26
lines changed

1 file changed

+72
-26
lines changed

src/hyperlight_host/src/hypervisor/surrogate_process_manager.rs

Lines changed: 72 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,56 @@ 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() {
162172
log_then_return!(
163-
"VirtualAllocEx failed for mem address {:?}",
173+
"MapViewOfFileNuma2 failed for mem address {:?}",
164174
raw_source_address
165175
);
166176
}
167177

168-
// set up guard page
178+
if allocated_address.Value as *const c_void != raw_source_address {
179+
log_then_return!(
180+
"Address Mismatch Allocated: {:?} Requested: {:?}",
181+
allocated_address.Value,
182+
raw_source_address
183+
);
184+
}
185+
186+
// set up guard pages
187+
188+
// If the following calls to VirtualProtectEx are changed make sure to update the calls to VirtualProtect in shared_mem.rs
169189

170190
let mut unused_out_old_prot_flags = PAGE_PROTECTION_FLAGS(0);
171191

172192
// the first page of the raw_size is the guard page
173193
let first_guard_page_start = raw_source_address;
174194
if let Err(e) = unsafe {
175195
VirtualProtectEx(
176-
process_handle,
196+
surrogate_process_handle,
177197
first_guard_page_start,
178198
PAGE_SIZE_USIZE,
179199
PAGE_NOACCESS,
@@ -187,7 +207,7 @@ impl SurrogateProcessManager {
187207
let last_guard_page_start = unsafe { raw_source_address.add(raw_size - PAGE_SIZE_USIZE) };
188208
if let Err(e) = unsafe {
189209
VirtualProtectEx(
190-
process_handle,
210+
surrogate_process_handle,
191211
last_guard_page_start,
192212
PAGE_SIZE_USIZE,
193213
PAGE_NOACCESS,
@@ -197,7 +217,10 @@ impl SurrogateProcessManager {
197217
log_then_return!(WindowsAPIError(e.clone()));
198218
}
199219

200-
Ok(SurrogateProcess::new(allocated_address, process_handle))
220+
Ok(SurrogateProcess::new(
221+
allocated_address.Value,
222+
surrogate_process_handle,
223+
))
201224
}
202225

203226
/// Returns a surrogate process to the pool of surrogate processes.
@@ -391,15 +414,17 @@ mod tests {
391414
use std::thread;
392415
use std::time::{Duration, Instant};
393416

417+
use hyperlight_common::mem::PAGE_SIZE_USIZE;
394418
use rand::{thread_rng, Rng};
395419
use serial_test::serial;
396-
use windows::Win32::Foundation::BOOL;
420+
use windows::Win32::Foundation::{CloseHandle, BOOL, HANDLE, INVALID_HANDLE_VALUE};
397421
use windows::Win32::System::Diagnostics::ToolHelp::{
398422
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
399423
};
400424
use windows::Win32::System::JobObjects::IsProcessInJob;
401425
use windows::Win32::System::Memory::{
402-
VirtualAlloc, VirtualFree, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE,
426+
CreateFileMappingA, MapViewOfFile, UnmapViewOfFile, FILE_MAP_ALL_ACCESS, PAGE_READWRITE,
427+
SEC_COMMIT,
403428
};
404429

405430
use super::*;
@@ -423,13 +448,29 @@ mod tests {
423448
// surrogate process, make sure we actually got one,
424449
// then put it back
425450
for p in 0..NUMBER_OF_SURROGATE_PROCESSES {
426-
let allocated_address = unsafe {
427-
VirtualAlloc(None, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
451+
let dwmaximumsizehigh = 0;
452+
let dwmaximumsizelow = (size & 0xFFFFFFFF) as u32;
453+
let handle = unsafe {
454+
CreateFileMappingA(
455+
INVALID_HANDLE_VALUE, // Causes the page file to be used as the backing store
456+
None,
457+
PAGE_READWRITE | SEC_COMMIT,
458+
dwmaximumsizehigh,
459+
dwmaximumsizelow,
460+
PCSTR::null(),
461+
)
462+
.unwrap()
428463
};
464+
465+
let addr = unsafe { MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0) };
466+
429467
let timer = Instant::now();
430468
let surrogate_process = {
431-
let res = surrogate_process_manager
432-
.get_surrogate_process(size, allocated_address)?;
469+
let res = surrogate_process_manager.get_surrogate_process(
470+
size,
471+
addr.Value,
472+
HandleWrapper::from(handle),
473+
)?;
433474
let elapsed = timer.elapsed();
434475
// 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)
435476
if (elapsed.as_millis() as u64) > 150 {
@@ -454,10 +495,11 @@ mod tests {
454495
// dropping the surrogate process, as we do in the line
455496
// below, will return it to the surrogate process manager
456497
drop(surrogate_process);
457-
unsafe {
458-
let res = VirtualFree(allocated_address, 0, MEM_RELEASE);
459-
assert!(res.is_ok())
460-
}
498+
let res = unsafe { UnmapViewOfFile(addr) };
499+
assert!(res.is_ok(), "Failed to UnmapViewOfFile: {:?}", res.err());
500+
501+
let res = unsafe { CloseHandle(handle) };
502+
assert!(res.is_ok(), "Failed to CloseHandle: {:?}", res.err());
461503
}
462504
Ok(())
463505
});
@@ -516,7 +558,11 @@ mod tests {
516558
let mem = ExclusiveSharedMemory::new(SIZE).unwrap();
517559

518560
let process = mgr
519-
.get_surrogate_process(mem.raw_mem_size(), mem.raw_ptr() as *mut c_void)
561+
.get_surrogate_process(
562+
mem.raw_mem_size(),
563+
mem.raw_ptr() as *mut c_void,
564+
HandleWrapper::from(mem.get_mmap_file_handle()),
565+
)
520566
.unwrap();
521567

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

0 commit comments

Comments
 (0)