Skip to content

Commit 7054dd5

Browse files
committed
fixup! Avoid copying memory on snapshot restore, when possible
Address review comments Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent 2ee6b29 commit 7054dd5

File tree

5 files changed

+45
-13
lines changed

5 files changed

+45
-13
lines changed

src/hyperlight_host/src/hypervisor/gdb/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ pub enum DebugMemoryAccessError {
9696
#[error("Failed to translate guest address {0:#x}")]
9797
TranslateGuestAddress(u64),
9898
#[error("Failed to write to read-only region")]
99-
WriteToReadOnly(),
99+
WriteToReadOnly,
100100
}
101101

102102
impl DebugMemoryAccess {
@@ -161,7 +161,7 @@ impl DebugMemoryAccess {
161161
.scratch_mem
162162
.copy_from_slice(data, resolved.offset)
163163
.map_err(|e| DebugMemoryAccessError::CopyFailed(Box::new(e))),
164-
_ => Err(DebugMemoryAccessError::WriteToReadOnly()),
164+
_ => Err(DebugMemoryAccessError::WriteToReadOnly),
165165
}
166166
}
167167
}

src/hyperlight_host/src/mem/layout.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ impl<'a> BaseGpaRegion<&'a [u8], &'a [u8]> {
132132
}
133133
impl<'a> ResolvedGpa<&'a [u8], &'a [u8]> {
134134
pub(crate) fn as_ref<'b>(&'b self) -> &'a [u8] {
135+
let base = self.base.as_ref();
136+
if self.offset > base.len() {
137+
return &[];
138+
}
135139
&self.base.as_ref()[self.offset..]
136140
}
137141
}
@@ -158,7 +162,7 @@ mod coherence_hack {
158162
#[cfg(any(gdb, feature = "mem_profile"))]
159163
impl<T: coherence_hack::SharedMemoryAsRefMarker> ReadableSharedMemory for T {
160164
fn copy_to_slice(&self, slice: &mut [u8], offset: usize) -> Result<()> {
161-
let ss: &[u8] = &self.as_ref()[offset..];
165+
let ss: &[u8] = self.as_ref();
162166
let end = offset + slice.len();
163167
if end > ss.len() {
164168
return Err(new_error!(
@@ -184,6 +188,14 @@ impl<Sn: ReadableSharedMemory, Sc: ReadableSharedMemory> ResolvedGpa<Sn, Sc> {
184188
#[allow(clippy::useless_conversion)]
185189
let host_region_end: usize = r.host_region.end.into();
186190
let len = host_region_end - host_region_base;
191+
// Safety: it's a documented invariant of MemoryRegion
192+
// that the memory must remain alive as long as the
193+
// sandbox is alive, and the way this code is used,
194+
// the lifetimes of the snapshot and scratch memories
195+
// ensure that the sandbox is still alive. This could
196+
// perhaps be cleaned up/improved/made harder to
197+
// misuse significantly, but it would require a much
198+
// larger rework.
187199
let ss = std::slice::from_raw_parts(host_region_base as *const u8, len);
188200
let end = self.offset + slice.len();
189201
if end > ss.len() {
@@ -230,7 +242,7 @@ pub(crate) struct SandboxMemoryLayout {
230242
// The size of the scratch region in physical memory; note that
231243
// this will appear under the top of physical memory.
232244
scratch_size: usize,
233-
// The size of the scratch region in physical memory; note that
245+
// The size of the snapshot region in physical memory; note that
234246
// this will appear somewhere near the base of physical memory.
235247
snapshot_size: usize,
236248
}
@@ -653,10 +665,10 @@ impl SandboxMemoryLayout {
653665
Ok(())
654666
}
655667

656-
/// Write the finished memory layout to `shared_mem` and return
657-
/// `Ok` if successful.
668+
/// Write the finished memory layout to `mem` and return `Ok` if
669+
/// successful.
658670
///
659-
/// Note: `shared_mem` may have been modified, even if `Err` was returned
671+
/// Note: `mem` may have been modified, even if `Err` was returned
660672
/// from this function.
661673
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
662674
pub(crate) fn write_peb(&self, mem: &mut [u8]) -> Result<()> {

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,13 @@ impl SandboxMemoryManager<HostSharedMemory> {
428428
Option<GuestSharedMemory>,
429429
)> {
430430
let gsnapshot = if *snapshot.memory() == self.shared_mem {
431+
// If the snapshot memory is already the correct memory,
432+
// which is readonly, don't bother with restoring it,
433+
// since its contents must be the same. Note that in the
434+
// #[cfg(unshared_snapshot_mem)] case, this condition will
435+
// never be true, since even immediately after a restore,
436+
// self.shared_mem is a (writable) copy, not the original
437+
// shared_mem.
431438
None
432439
} else {
433440
let new_snapshot_mem = snapshot.memory().to_mgr_snapshot_mem()?;

src/hyperlight_host/src/mem/shared_mem.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,16 @@ mod tests {
19981998
pub struct ReadonlySharedMemory {
19991999
region: Arc<HostMapping>,
20002000
}
2001+
// Safety: HostMapping is only non-Send/Sync (causing
2002+
// ReadonlySharedMemory to not be automatically Send/Sync) because raw
2003+
// pointers are not ("as a lint", as the Rust docs say). We don't want
2004+
// to mark HostMapping Send/Sync immediately, because that could
2005+
// socially imply that it's "safe" to use unsafe accesses from
2006+
// multiple threads at once in more cases, including ones that don't
2007+
// actually ensure immutability/synchronisation. Since
2008+
// ReadonlySharedMemory can only be accessed by reading, and reading
2009+
// concurrently from multiple threads is not racy,
2010+
// ReadonlySharedMemory can be Send and Sync.
20012011
unsafe impl Send for ReadonlySharedMemory {}
20022012
unsafe impl Sync for ReadonlySharedMemory {}
20032013

@@ -2032,11 +2042,11 @@ impl ReadonlySharedMemory {
20322042
guest_base: u64,
20332043
region_type: MemoryRegionType,
20342044
) -> MemoryRegion {
2045+
#[allow(clippy::panic)]
2046+
// This will not ever actually panic: the only place this is
2047+
// called is HyperlightVm::update_snapshot_mapping, which
2048+
// always calls it with the Snapshot region type.
20352049
if region_type != MemoryRegionType::Snapshot {
2036-
#[allow(clippy::panic)]
2037-
// This will not ever actually panic: the only place this
2038-
// is called is HyperlightVm::update_snapshot_mapping,
2039-
// which always calls it with the Snapshot region type.
20402050
panic!("ReadonlySharedMemory::mapping_at should only be used for Snapshot regions");
20412051
}
20422052
mapping_at(

src/hyperlight_host/src/sandbox/snapshot.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,10 @@ unsafe fn guest_page<'a>(
287287
let resolved = layout
288288
.resolve_gpa(gpa, regions)?
289289
.with_memories(snap, scratch);
290-
Some(&resolved.as_ref()[0..PAGE_SIZE])
290+
if resolved.as_ref().len() < PAGE_SIZE {
291+
return None;
292+
}
293+
Some(&resolved.as_ref()[..PAGE_SIZE])
291294
}
292295

293296
fn map_specials(pt_buf: &GuestPageTableBuffer, scratch_size: usize) {
@@ -666,7 +669,7 @@ mod tests {
666669
// Restore snapshot B
667670
mgr.restore_snapshot(&snapshot_b).unwrap();
668671
mgr.shared_mem
669-
.with_contents(|contents| assert_eq!(&contents[0..pattern_a.len()], &pattern_b[..]))
672+
.with_contents(|contents| assert_eq!(&contents[0..pattern_b.len()], &pattern_b[..]))
670673
.unwrap();
671674
}
672675
}

0 commit comments

Comments
 (0)