Fix snapshot reset function when brk shrunk below the snapshotted value (#2812)

* added change_brk function for correctly handling SYS_brk

* we need to update h.brk with the new brk_val

* map back pages if brk shrunk below the snapshotted value

* fmt and clippy

* use GuestAddr instead of u64

---------

Co-authored-by: Romain Malmain <romain.malmain@pm.me>
This commit is contained in:
cube0x8 2025-01-09 01:30:39 +02:00 committed by GitHub
parent f7745155c9
commit 1a9e25145d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -393,6 +393,20 @@ impl SnapshotModule {
log::debug!("Start restore"); log::debug!("Start restore");
let new_brk = qemu.get_brk();
if new_brk < self.brk {
// The heap has shrunk below the snapshotted brk value. We need to remap those pages in the target.
// The next for loop will restore their content if needed.
let aligned_new_brk = (new_brk + ((SNAPSHOT_PAGE_SIZE - 1) as GuestAddr))
& (!(SNAPSHOT_PAGE_SIZE - 1) as GuestAddr);
log::debug!("New brk ({:#x?}) < snapshotted brk ({:#x?})! Mapping back in the target {:#x?} - {:#x?}", new_brk, self.brk, aligned_new_brk, aligned_new_brk + (self.brk - aligned_new_brk));
drop(qemu.map_fixed(
aligned_new_brk,
(self.brk - aligned_new_brk) as usize,
MmapPerms::ReadWrite,
));
}
for acc in &mut self.accesses { for acc in &mut self.accesses {
unsafe { &mut (*acc.get()) }.dirty.retain(|page| { unsafe { &mut (*acc.get()) }.dirty.retain(|page| {
if let Some(info) = self.pages.get_mut(page) { if let Some(info) = self.pages.get_mut(page) {
@ -524,7 +538,12 @@ impl SnapshotModule {
} }
} }
pub fn change_mapped(&mut self, start: GuestAddr, mut size: usize, perms: Option<MmapPerms>) { pub fn change_mapped_perms(
&mut self,
start: GuestAddr,
mut size: usize,
perms: Option<MmapPerms>,
) {
if size % SNAPSHOT_PAGE_SIZE != 0 { if size % SNAPSHOT_PAGE_SIZE != 0 {
size = size + (SNAPSHOT_PAGE_SIZE - size % SNAPSHOT_PAGE_SIZE); size = size + (SNAPSHOT_PAGE_SIZE - size % SNAPSHOT_PAGE_SIZE);
} }
@ -851,18 +870,8 @@ where
h.access(a0, a1 as usize); h.access(a0, a1 as usize);
} }
SYS_brk => { SYS_brk => {
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap(); // We don't handle brk here. It is handled in the reset function only when it's needed.
if h.brk != result && result != 0 && result > h.initial_brk { log::debug!("New brk ({:#x?}) received.", result);
/* brk has changed, and it doesn't shrink below initial_brk. We change mapping from the snapshotted initial brk address to the new target_brk
* If no brk mapping has been made until now, change_mapped won't change anything and just create a new mapping.
* It is safe to assume RW perms here
*/
h.change_mapped(
h.initial_brk,
(result - h.initial_brk) as usize,
Some(MmapPerms::ReadWrite),
);
}
} }
// mmap syscalls // mmap syscalls
sys_const => { sys_const => {
@ -898,7 +907,7 @@ where
} else if sys_const == SYS_mprotect { } else if sys_const == SYS_mprotect {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) { if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap(); let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
h.change_mapped(a0, a1 as usize, Some(prot)); h.change_mapped_perms(a0, a1 as usize, Some(prot));
} }
} else if sys_const == SYS_munmap { } else if sys_const == SYS_munmap {
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap(); let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();