Skip to content

Commit 6a5458a

Browse files
committed
fixup! Snapshot only contains pages dirties since last snapshot was taken
1 parent 75e5750 commit 6a5458a

File tree

3 files changed

+42
-54
lines changed

3 files changed

+42
-54
lines changed

src/hyperlight_host/src/mem/shared_mem.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ impl ExclusiveSharedMemory {
680680
/// This function is unsafe because it does not mark the pages as dirty.
681681
/// Only use this if you are certain that the pages do not need to be marked as dirty.
682682
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
683-
pub unsafe fn restore_copy_from_slice(&mut self, src: &[u8], offset: usize) -> Result<()> {
683+
pub unsafe fn copy_from_slice_no_dirty(&mut self, src: &[u8], offset: usize) -> Result<()> {
684684
let data = self.as_mut_slice();
685685
bounds_check!(offset, src.len(), data.len());
686686
data[offset..offset + src.len()].copy_from_slice(src);
@@ -692,7 +692,7 @@ impl ExclusiveSharedMemory {
692692
/// This function is unsafe because it does not mark the pages as dirty.
693693
/// Only use this if you are certain that the pages do not need to be marked as dirty.
694694
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
695-
pub(crate) unsafe fn restore_zero_fill(&mut self, offset: usize, len: usize) -> Result<()> {
695+
pub(crate) unsafe fn zero_fill_no_dirty(&mut self, offset: usize, len: usize) -> Result<()> {
696696
bounds_check!(offset, len, self.mem_size());
697697
let data = self.as_mut_slice();
698698
data[offset..offset + len].fill(0);

src/hyperlight_host/src/mem/shared_mem_snapshot.rs

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -85,66 +85,30 @@ impl SharedMemorySnapshot {
8585
}
8686

8787
let mut pages_to_restore = HashSet::new();
88+
89+
// Unconditionally restore all current dirty pages
8890
for page_num in bit_index_iterator(current_dirty_pages) {
8991
pages_to_restore.insert(page_num);
9092
}
9193

9294
if let Some(most_recent) = most_recent_snapshot {
93-
// Check if we're restoring to the same snapshot state the sandbox was most recently in
9495
if Arc::ptr_eq(self, most_recent) {
95-
// No need to collect additional pages - just use current dirty pages
96-
}
97-
// Check if we're "rolling back" to a previous state, i.e. `self` is older than `most_recent` (`self` is ancestor of `most_recent`)
98-
// We need to restore all pages that exist in `most_recent` and its ancestors up to `self` (exclusive)
99-
else if self.is_ancestor_of(most_recent) {
100-
let mut current_snapshot = Some(most_recent);
101-
102-
while let Some(snapshot) = current_snapshot {
103-
if Arc::ptr_eq(snapshot, self) {
104-
break;
105-
}
106-
107-
for page_num in snapshot.data.keys() {
108-
pages_to_restore.insert(*page_num);
109-
}
110-
111-
current_snapshot = snapshot.parent.as_ref();
112-
}
113-
// Check if we're "fast forwarding" to a "newer" state, i.e. `self` is newer than `most_recent` (`most_recent` is ancestor of `self`)
114-
// We need to restore all pages that exist in `self` and its ancestors up to `most_recent` (exclusive)
96+
// Restoring to same snapshot sandbox was most recently in: only restore current dirty pages
97+
} else if self.is_ancestor_of(most_recent) {
98+
// Rolling back: collect pages from most_recent back to self (exclusive)
99+
Self::collect_pages_from_chain(most_recent, Some(self), &mut pages_to_restore);
115100
} else if most_recent.is_ancestor_of(self) {
116-
let mut current_snapshot = Some(self);
117-
118-
while let Some(snapshot) = current_snapshot {
119-
if Arc::ptr_eq(snapshot, most_recent) {
120-
break;
121-
}
122-
123-
for page_num in snapshot.data.keys() {
124-
pages_to_restore.insert(*page_num);
125-
}
126-
127-
current_snapshot = snapshot.parent.as_ref();
128-
}
101+
// Fast-forwarding: collect pages from self back to most_recent (exclusive)
102+
Self::collect_pages_from_chain(self, Some(most_recent), &mut pages_to_restore);
129103
} else {
130-
// Neither is ancestor of the other - they're on different branches
131-
// This is not supported for now
104+
// Different branches not supported for now
132105
return Err(crate::new_error!(
133106
"Cannot restore between snapshots on different branches"
134107
));
135108
}
136109
} else {
137-
// No previous snapshots exist, meaning we're restoring a fresh sandbox to this snapshot.
138-
// We need to restore pages that exist in this snapshot and all its ancestors.
139-
let mut current_snapshot = Some(self);
140-
141-
while let Some(snapshot) = current_snapshot {
142-
for page_num in snapshot.data.keys() {
143-
pages_to_restore.insert(*page_num);
144-
}
145-
146-
current_snapshot = snapshot.parent.as_ref();
147-
}
110+
// Sandbox has no previous snapshots: restore all pages from this snapshot and its ancestors
111+
Self::collect_pages_from_chain(self, None, &mut pages_to_restore);
148112
}
149113

150114
// Restore all collected pages
@@ -156,11 +120,11 @@ impl SharedMemorySnapshot {
156120
if let Some(page_data) = self.find_page_in_snapshots(page_num) {
157121
// Restore from snapshot
158122
// # Safety: We don't want to dirty the pages we restore
159-
unsafe { e.restore_copy_from_slice(page_data, offset)? };
123+
unsafe { e.copy_from_slice_no_dirty(page_data, offset)? };
160124
} else {
161125
// Zero the page (return to initial state)
162126
// # Safety: We don't want to dirty the pages we restore
163-
unsafe { e.restore_zero_fill(offset, PAGE_SIZE_USIZE)? };
127+
unsafe { e.zero_fill_no_dirty(offset, PAGE_SIZE_USIZE)? };
164128
}
165129
}
166130
Ok(())
@@ -170,7 +134,7 @@ impl SharedMemorySnapshot {
170134
}
171135

172136
/// Check if this snapshot is an ancestor of the other snapshot
173-
/// (i.e., the other snapshot was taken after this one in the same chain)
137+
/// (i.e. the other snapshot was taken after this one in the same chain)
174138
fn is_ancestor_of(self: &Arc<Self>, other: &Arc<SharedMemorySnapshot>) -> bool {
175139
let mut current = other.parent.as_ref();
176140

@@ -204,6 +168,30 @@ impl SharedMemorySnapshot {
204168
pub(super) fn sandbox_size(&self) -> usize {
205169
self.sandbox_size
206170
}
171+
172+
/// Collect all page numbers from snapshots in a chain, starting from `start_snapshot`
173+
/// and stopping when `stop_snapshot` is reached (exclusive) or when the chain ends.
174+
fn collect_pages_from_chain(
175+
start_snapshot: &Arc<SharedMemorySnapshot>,
176+
stop_snapshot: Option<&Arc<SharedMemorySnapshot>>,
177+
pages_to_restore: &mut HashSet<usize>,
178+
) {
179+
let mut current_snapshot = Some(start_snapshot);
180+
181+
while let Some(snapshot) = current_snapshot {
182+
if let Some(stop) = stop_snapshot {
183+
if Arc::ptr_eq(snapshot, stop) {
184+
break;
185+
}
186+
}
187+
188+
for page_num in snapshot.data.keys() {
189+
pages_to_restore.insert(*page_num);
190+
}
191+
192+
current_snapshot = snapshot.parent.as_ref();
193+
}
194+
}
207195
}
208196

209197
#[cfg(test)]

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl MultiUseSandbox {
103103
.with_exclusivity(|e| e.get_and_clear_dirty_pages())??;
104104
let vm_dirty_pages = self.vm.get_and_clear_dirty_pages()?;
105105

106-
let dirty_pages_bitmap = bitmap_union(&vm_dirty_pages, &host_dirty_pages);
106+
let dirty_pages_bitmap = bitmap_union(&vm_dirty_pages, &host_dirty_pages)?;
107107

108108
let snapshot = self
109109
.mem_mgr
@@ -123,7 +123,7 @@ impl MultiUseSandbox {
123123
.with_exclusivity(|e| e.get_and_clear_dirty_pages())??;
124124
let vm_dirty_pages = self.vm.get_and_clear_dirty_pages()?;
125125

126-
let dirty_pages_bitmap = bitmap_union(&vm_dirty_pages, &host_dirty_pages);
126+
let dirty_pages_bitmap = bitmap_union(&vm_dirty_pages, &host_dirty_pages)?;
127127

128128
let rgns_to_unmap = self
129129
.mem_mgr

0 commit comments

Comments
 (0)