Skip to content

Commit 1f4c54a

Browse files
committed
Install DirtyPageTracker immediately after allocating shared memory. Uninstall DirtyPageTracker immediately before setting up VM and mapping memory into VM. Use dirty pages to create snapshots with new snapshotmanager.
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 5d71e2a commit 1f4c54a

File tree

7 files changed

+148
-173
lines changed

7 files changed

+148
-173
lines changed

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
2424
use hyperlight_common::flatbuffer_wrappers::guest_error::GuestError;
2525
use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
2626
use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
27+
use hyperlight_common::mem::PAGES_IN_BLOCK;
2728
use tracing::{Span, instrument};
2829

2930
use super::exe::ExeInfo;
@@ -33,8 +34,9 @@ use super::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegion, MemoryReg
3334
use super::ptr::{GuestPtr, RawPtr};
3435
use super::ptr_offset::Offset;
3536
use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, HostSharedMemory, SharedMemory};
36-
use super::shared_mem_snapshot::SharedMemorySnapshot;
37-
use crate::HyperlightError::NoMemorySnapshot;
37+
use super::shared_memory_snapshot_manager::SharedMemorySnapshotManager;
38+
use crate::mem::bitmap::{bitmap_union, new_page_bitmap};
39+
use crate::mem::dirty_page_tracking::DirtyPageTracker;
3840
use crate::sandbox::SandboxConfiguration;
3941
use crate::sandbox::uninitialized::GuestBlob;
4042
use crate::{Result, log_then_return, new_error};
@@ -75,9 +77,8 @@ pub(crate) struct SandboxMemoryManager<S> {
7577
pub(crate) entrypoint_offset: Offset,
7678
/// How many memory regions were mapped after sandbox creation
7779
pub(crate) mapped_rgns: u64,
78-
/// A vector of memory snapshots that can be used to save and restore the state of the memory
79-
/// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
80-
snapshots: Arc<Mutex<Vec<SharedMemorySnapshot>>>,
80+
/// Shared memory snapshots that can be used to save and restore the state of the memory
81+
snapshot_manager: Arc<Mutex<Option<SharedMemorySnapshotManager>>>,
8182
}
8283

8384
impl<S> SandboxMemoryManager<S>
@@ -98,7 +99,7 @@ where
9899
load_addr,
99100
entrypoint_offset,
100101
mapped_rgns: 0,
101-
snapshots: Arc::new(Mutex::new(Vec::new())),
102+
snapshot_manager: Arc::new(Mutex::new(None)),
102103
}
103104
}
104105

@@ -265,54 +266,100 @@ where
265266
}
266267
}
267268

268-
/// this function will create a memory snapshot and push it onto the stack of snapshots
269-
/// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
270-
pub(crate) fn push_state(&mut self) -> Result<()> {
271-
let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem, self.mapped_rgns)?;
272-
self.snapshots
269+
/// this function will create an initial snapshot and then create the SnapshotManager
270+
pub(crate) fn create_initial_snapshot(
271+
&mut self,
272+
vm_dirty_bitmap: &[u64],
273+
host_dirty_page_idx: &[usize],
274+
layout: &SandboxMemoryLayout,
275+
) -> Result<()> {
276+
let mut existing_snapshot_manager = self
277+
.snapshot_manager
273278
.try_lock()
274-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
275-
.push(snapshot);
279+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
280+
281+
if existing_snapshot_manager.is_some() {
282+
log_then_return!("Snapshot manager already initialized, not creating a new one");
283+
}
284+
285+
// covert vec of page indices to bitmap
286+
let mut res = new_page_bitmap(self.shared_mem.raw_mem_size(), false)?;
287+
for page_idx in host_dirty_page_idx {
288+
let block_idx = page_idx / PAGES_IN_BLOCK;
289+
let bit_idx = page_idx % PAGES_IN_BLOCK;
290+
res[block_idx] |= 1 << bit_idx;
291+
}
292+
293+
// merge the host dirty page map into the dirty bitmap
294+
let merged = bitmap_union(&res, vm_dirty_bitmap);
295+
296+
let snapshot_manager = SharedMemorySnapshotManager::new(
297+
&mut self.shared_mem,
298+
&merged,
299+
layout,
300+
self.mapped_rgns,
301+
)?;
302+
existing_snapshot_manager.replace(snapshot_manager);
276303
Ok(())
277304
}
278305

279306
/// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot
280307
/// off the stack
281308
/// It should be used when you want to restore the state of the memory to a previous state but still want to
282309
/// retain that state, for example after calling a function in the guest
283-
///
284-
/// Returns the number of memory regions mapped into the sandbox
285-
/// that need to be unmapped in order for the restore to be
286-
/// completed.
287-
pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result<u64> {
288-
let mut snapshots = self
289-
.snapshots
310+
pub(crate) fn restore_state_from_last_snapshot(&mut self, dirty_bitmap: &[u64]) -> Result<u64> {
311+
let mut snapshot_manager = self
312+
.snapshot_manager
290313
.try_lock()
291314
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
292-
let last = snapshots.last_mut();
293-
if last.is_none() {
294-
log_then_return!(NoMemorySnapshot);
315+
316+
match snapshot_manager.as_mut() {
317+
None => {
318+
log_then_return!("Snapshot manager not initialized");
319+
}
320+
Some(snapshot_manager) => {
321+
snapshot_manager.restore_from_snapshot(&mut self.shared_mem, dirty_bitmap)
322+
}
295323
}
296-
#[allow(clippy::unwrap_used)] // We know that last is not None because we checked it above
297-
let snapshot = last.unwrap();
298-
let old_rgns = self.mapped_rgns;
299-
self.mapped_rgns = snapshot.restore_from_snapshot(&mut self.shared_mem)?;
300-
Ok(old_rgns - self.mapped_rgns)
301324
}
302325

303326
/// this function pops the last snapshot off the stack and restores the memory to the previous state
304327
/// It should be used when you want to restore the state of the memory to a previous state and do not need to retain that state
305328
/// for example when devolving a sandbox to a previous state.
306-
pub(crate) fn pop_and_restore_state_from_snapshot(&mut self) -> Result<u64> {
307-
let last = self
308-
.snapshots
329+
pub(crate) fn pop_and_restore_state_from_snapshot(
330+
&mut self,
331+
dirty_bitmap: &[u64],
332+
) -> Result<u64> {
333+
let mut snapshot_manager = self
334+
.snapshot_manager
309335
.try_lock()
310-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
311-
.pop();
312-
if last.is_none() {
313-
log_then_return!(NoMemorySnapshot);
336+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
337+
338+
match snapshot_manager.as_mut() {
339+
None => {
340+
log_then_return!("Snapshot manager not initialized");
341+
}
342+
Some(snapshot_manager) => snapshot_manager
343+
.pop_and_restore_state_from_snapshot(&mut self.shared_mem, dirty_bitmap),
344+
}
345+
}
346+
347+
pub(crate) fn push_state(&mut self, dirty_bitmap: &[u64]) -> Result<()> {
348+
let mut snapshot_manager = self
349+
.snapshot_manager
350+
.try_lock()
351+
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
352+
353+
match snapshot_manager.as_mut() {
354+
None => {
355+
log_then_return!("Snapshot manager not initialized");
356+
}
357+
Some(snapshot_manager) => snapshot_manager.create_new_snapshot(
358+
&mut self.shared_mem,
359+
dirty_bitmap,
360+
self.mapped_rgns,
361+
),
314362
}
315-
self.restore_state_from_last_snapshot()
316363
}
317364

318365
/// Sets `addr` to the correct offset in the memory referenced by
@@ -347,7 +394,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
347394
cfg: SandboxConfiguration,
348395
exe_info: &mut ExeInfo,
349396
guest_blob: Option<&GuestBlob>,
350-
) -> Result<Self> {
397+
) -> Result<(Self, DirtyPageTracker)> {
351398
let guest_blob_size = guest_blob.map(|b| b.data.len()).unwrap_or(0);
352399
let guest_blob_mem_flags = guest_blob.map(|b| b.permissions);
353400

@@ -360,6 +407,7 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
360407
guest_blob_mem_flags,
361408
)?;
362409
let mut shared_mem = ExclusiveSharedMemory::new(layout.get_memory_size()?)?;
410+
let tracker = shared_mem.start_tracking_dirty_pages()?;
363411

364412
let load_addr: RawPtr = RawPtr::try_from(layout.get_guest_code_address())?;
365413

@@ -378,7 +426,10 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
378426
&mut shared_mem.as_mut_slice()[layout.get_guest_code_offset()..],
379427
)?;
380428

381-
Ok(Self::new(layout, shared_mem, load_addr, entrypoint_offset))
429+
Ok((
430+
Self::new(layout, shared_mem, load_addr, entrypoint_offset),
431+
tracker,
432+
))
382433
}
383434

384435
/// Writes host function details to memory
@@ -440,15 +491,15 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
440491
load_addr: self.load_addr.clone(),
441492
entrypoint_offset: self.entrypoint_offset,
442493
mapped_rgns: 0,
443-
snapshots: Arc::new(Mutex::new(Vec::new())),
494+
snapshot_manager: Arc::new(Mutex::new(None)),
444495
},
445496
SandboxMemoryManager {
446497
shared_mem: gshm,
447498
layout: self.layout,
448499
load_addr: self.load_addr.clone(),
449500
entrypoint_offset: self.entrypoint_offset,
450501
mapped_rgns: 0,
451-
snapshots: Arc::new(Mutex::new(Vec::new())),
502+
snapshot_manager: Arc::new(Mutex::new(None)),
452503
},
453504
)
454505
}

src/hyperlight_host/src/mem/mod.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ pub mod ptr_offset;
4343
/// A wrapper around unsafe functionality to create and initialize
4444
/// a memory region for a guest running in a sandbox.
4545
pub mod shared_mem;
46-
/// A wrapper around a `SharedMemory` and a snapshot in time
47-
/// of the memory therein
48-
pub mod shared_mem_snapshot;
4946
/// Utilities for writing shared memory tests
5047
#[cfg(test)]
5148
pub(crate) mod shared_mem_tests;

src/hyperlight_host/src/mem/shared_mem_snapshot.rs

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,9 @@ impl MultiUseSandbox {
270270
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
271271
pub(crate) fn restore_state(&mut self) -> Result<()> {
272272
let mem_mgr = self.mem_mgr.unwrap_mgr_mut();
273-
let rgns_to_unmap = mem_mgr.restore_state_from_last_snapshot()?;
274-
unsafe { self.vm.unmap_regions(rgns_to_unmap)? };
273+
let dirty_pages = self.vm.get_and_clear_dirty_pages()?;
274+
let rgns_to_umap = mem_mgr.restore_state_from_last_snapshot(&dirty_pages)?;
275+
unsafe { self.vm.unmap_regions(rgns_to_umap)? };
275276
Ok(())
276277
}
277278

@@ -354,10 +355,11 @@ impl DevolvableSandbox<MultiUseSandbox, MultiUseSandbox, Noop<MultiUseSandbox, M
354355
/// The devolve can be used to return the MultiUseSandbox to the state before the code was loaded. Thus avoiding initialisation overhead
355356
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
356357
fn devolve(mut self, _tsn: Noop<MultiUseSandbox, MultiUseSandbox>) -> Result<MultiUseSandbox> {
358+
let dirty_pages = self.vm.get_and_clear_dirty_pages()?;
357359
let rgns_to_unmap = self
358360
.mem_mgr
359361
.unwrap_mgr_mut()
360-
.pop_and_restore_state_from_snapshot()?;
362+
.pop_and_restore_state_from_snapshot(&dirty_pages)?;
361363
unsafe { self.vm.unmap_regions(rgns_to_unmap)? };
362364
Ok(self)
363365
}
@@ -389,7 +391,8 @@ where
389391
let mut ctx = self.new_call_context();
390392
transition_func.call(&mut ctx)?;
391393
let mut sbox = ctx.finish_no_reset();
392-
sbox.mem_mgr.unwrap_mgr_mut().push_state()?;
394+
let vm_dirty_pages = sbox.vm.get_and_clear_dirty_pages()?;
395+
sbox.mem_mgr.unwrap_mgr_mut().push_state(&vm_dirty_pages)?;
393396
Ok(sbox)
394397
}
395398
}

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ mod tests {
241241

242242
let new_mgr = || {
243243
let mut exe_info = simple_guest_exe_info().unwrap();
244-
let mut mgr = SandboxMemoryManager::load_guest_binary_into_memory(
244+
let (mut mgr, _) = SandboxMemoryManager::load_guest_binary_into_memory(
245245
sandbox_cfg,
246246
&mut exe_info,
247247
None,
@@ -356,7 +356,7 @@ mod tests {
356356
tracing::subscriber::with_default(subscriber.clone(), || {
357357
let new_mgr = || {
358358
let mut exe_info = simple_guest_exe_info().unwrap();
359-
let mut mgr = SandboxMemoryManager::load_guest_binary_into_memory(
359+
let (mut mgr, _) = SandboxMemoryManager::load_guest_binary_into_memory(
360360
sandbox_cfg,
361361
&mut exe_info,
362362
None,

0 commit comments

Comments
 (0)