Improve libafl_qemu snapshots (#484)

* mprotect

* expose EnumIter

* thread safe mem snapshot

* update qemu hash

* clippy

* child helpers

* fixes

* fix build

* fix dep
This commit is contained in:
Andrea Fioraldi 2022-02-09 09:40:59 +01:00 committed by GitHub
parent 6bfbdd6318
commit 63d89463a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 272 additions and 78 deletions

View File

@ -60,7 +60,7 @@ num_enum = { version = "0.5.4", default-features = false }
typed-builder = "0.9.1" # Implement the builder pattern at compiletime typed-builder = "0.9.1" # Implement the builder pattern at compiletime
ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown
intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] } intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] }
backtrace = {version = "0.3.62", optional = true} # Used to get the stacktrace in StacktraceObserver backtrace = {version = "0.3", optional = true} # Used to get the stacktrace in StacktraceObserver
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] }
miniz_oxide = { version = "0.5", optional = true} miniz_oxide = { version = "0.5", optional = true}

View File

@ -36,7 +36,7 @@ capstone = "0.10.0"
color-backtrace ={ version = "0.5", features = [ "resolve-modules" ] } color-backtrace ={ version = "0.5", features = [ "resolve-modules" ] }
termcolor = "1.1.2" termcolor = "1.1.2"
serde = "1.0" serde = "1.0"
backtrace = { version = "0.3.58", default-features = false, features = ["std", "serde"] } backtrace = { version = "0.3", default-features = false, features = ["std", "serde"] }
num-traits = "0.2.14" num-traits = "0.2.14"
ahash = "0.7" ahash = "0.7"
paste = "1.0" paste = "1.0"

View File

@ -34,7 +34,9 @@ goblin = "0.4.2"
libc = "0.2" libc = "0.2"
strum = "0.21" strum = "0.21"
strum_macros = "0.21" strum_macros = "0.21"
syscall-numbers = "2.0.0" syscall-numbers = "2.0"
bio = "0.39"
thread_local = "1.1.3"
#pyo3 = { version = "0.15", features = ["extension-module"], optional = true } #pyo3 = { version = "0.15", features = ["extension-module"], optional = true }
pyo3 = { version = "0.15", optional = true } pyo3 = { version = "0.15", optional = true }

View File

@ -3,7 +3,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "fa2b9c4a25f548f15b3d1b1afcfdb75cc7165f9a"; const QEMU_REVISION: &str = "152fdbe024493f31e60060714caee3b90fdf3d9e";
fn build_dep_check(tools: &[&str]) { fn build_dep_check(tools: &[&str]) {
for tool in tools { for tool in tools {
@ -221,7 +221,6 @@ fn main() {
"--disable-vvfat", "--disable-vvfat",
"--disable-xen", "--disable-xen",
"--disable-xen-pci-passthrough", "--disable-xen-pci-passthrough",
"--disable-xfsctl",
]) ])
.status() .status()
.expect("Configure failed"); .expect("Configure failed");
@ -248,7 +247,7 @@ fn main() {
for dir in &[ for dir in &[
build_dir.join("libcommon.fa.p"), build_dir.join("libcommon.fa.p"),
build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)),
build_dir.join("libcommon-user.fa.p"), //build_dir.join("libcommon-user.fa.p"),
//build_dir.join("libqemuutil.a.p"), //build_dir.join("libqemuutil.a.p"),
//build_dir.join("libqom.fa.p"), //build_dir.join("libqom.fa.p"),
//build_dir.join("libhwcore.fa.p"), //build_dir.join("libhwcore.fa.p"),

View File

@ -1,5 +1,5 @@
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use strum_macros::EnumIter; pub use strum_macros::EnumIter;
#[cfg(feature = "python")] #[cfg(feature = "python")]
use pyo3::prelude::*; use pyo3::prelude::*;

View File

@ -1,5 +1,5 @@
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use strum_macros::EnumIter; pub use strum_macros::EnumIter;
#[cfg(feature = "python")] #[cfg(feature = "python")]
use pyo3::prelude::*; use pyo3::prelude::*;

View File

@ -162,8 +162,9 @@ pub fn init_with_asan(args: &mut Vec<String>, env: &mut [(String, String)]) -> E
Emulator::new(args, env) Emulator::new(args, env)
} }
pub type QemuAsanChildHelper = QemuAsanHelper;
#[derive(Debug)] #[derive(Debug)]
// TODO intrumentation filter
pub struct QemuAsanHelper { pub struct QemuAsanHelper {
enabled: bool, enabled: bool,
filter: QemuInstrumentationFilter, filter: QemuInstrumentationFilter,
@ -418,6 +419,8 @@ where
I: Input, I: Input,
S: HasMetadata, S: HasMetadata,
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,

View File

@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
emu::Emulator, emu::Emulator,
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
}; };
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
@ -78,6 +78,57 @@ where
} }
} }
#[derive(Debug)]
pub struct QemuCmpLogChildHelper {
filter: QemuInstrumentationFilter,
}
impl QemuCmpLogChildHelper {
#[must_use]
pub fn new() -> Self {
Self {
filter: QemuInstrumentationFilter::None,
}
}
#[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
Self { filter }
}
#[must_use]
pub fn must_instrument(&self, addr: u64) -> bool {
self.filter.allowed(addr)
}
}
impl Default for QemuCmpLogChildHelper {
fn default() -> Self {
Self::new()
}
}
impl<I, S> QemuHelper<I, S> for QemuCmpLogChildHelper
where
I: Input,
S: HasMetadata,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{
executor.hook_cmp_generation(gen_hashed_cmp_ids::<I, QT, S>);
executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
}
}
pub fn gen_unique_cmp_ids<I, QT, S>( pub fn gen_unique_cmp_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
@ -110,6 +161,26 @@ where
})) }))
} }
pub fn gen_hashed_cmp_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
pc: u64,
_size: usize,
) -> Option<u64>
where
S: HasMetadata,
I: Input,
QT: QemuHelperTuple<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuCmpLogChildHelper>() {
if !h.must_instrument(pc) {
return None;
}
}
Some(hash_me(pc))
}
pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8) { pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8) {
unsafe { unsafe {
__libafl_targets_cmplog_instructions(id as usize, 1, u64::from(v0), u64::from(v1)); __libafl_targets_cmplog_instructions(id as usize, 1, u64::from(v0), u64::from(v1));

View File

@ -1,13 +1,15 @@
use hashbrown::{hash_map::Entry, HashMap}; use hashbrown::{hash_map::Entry, HashMap};
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
pub use libafl_targets::{EDGES_MAP, EDGES_MAP_SIZE, MAX_EDGES_NUM}; pub use libafl_targets::{
edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{cell::UnsafeCell, cmp::max}; use std::{cell::UnsafeCell, cmp::max};
use crate::{ use crate::{
emu::Emulator, emu::Emulator,
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
}; };
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
@ -74,15 +76,58 @@ where
} }
} }
thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0)); pub type QemuEdgeCoverageWithBlocksHelper = QemuEdgeCoverageChildHelper;
fn hash_me(mut x: u64) -> u64 { #[derive(Debug)]
x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; pub struct QemuEdgeCoverageChildHelper {
x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; filter: QemuInstrumentationFilter,
x = (x.overflowing_shr(16).0 ^ x) ^ x;
x
} }
impl QemuEdgeCoverageChildHelper {
#[must_use]
pub fn new() -> Self {
Self {
filter: QemuInstrumentationFilter::None,
}
}
#[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
Self { filter }
}
#[must_use]
pub fn must_instrument(&self, addr: u64) -> bool {
self.filter.allowed(addr)
}
}
impl Default for QemuEdgeCoverageChildHelper {
fn default() -> Self {
Self::new()
}
}
impl<I, S> QemuHelper<I, S> for QemuEdgeCoverageChildHelper
where
I: Input,
S: HasMetadata,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
{
executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>);
executor.emulator().set_exec_edge_hook(trace_edge_hitcount);
}
}
thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0));
pub fn gen_unique_edge_ids<I, QT, S>( pub fn gen_unique_edge_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
@ -140,7 +185,7 @@ where
I: Input, I: Input,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageHelper>() { if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageChildHelper>() {
if !h.must_instrument(src) && !h.must_instrument(dest) { if !h.must_instrument(src) && !h.must_instrument(dest) {
return None; return None;
} }
@ -181,8 +226,9 @@ pub fn gen_hashed_block_ids<I, QT, S>(
pub extern "C" fn trace_block_transition_hitcount(id: u64) { pub extern "C" fn trace_block_transition_hitcount(id: u64) {
unsafe { unsafe {
PREV_LOC.with(|prev_loc| { PREV_LOC.with(|prev_loc| {
let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_SIZE - 1); let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_PTR_SIZE - 1);
EDGES_MAP[x] = EDGES_MAP[x].wrapping_add(1); let entry = EDGES_MAP_PTR.add(x);
*entry = (*entry).wrapping_add(1);
*prev_loc.get() = id.overflowing_shr(1).0; *prev_loc.get() = id.overflowing_shr(1).0;
}); });
} }
@ -191,8 +237,9 @@ pub extern "C" fn trace_block_transition_hitcount(id: u64) {
pub extern "C" fn trace_block_transition_single(id: u64) { pub extern "C" fn trace_block_transition_single(id: u64) {
unsafe { unsafe {
PREV_LOC.with(|prev_loc| { PREV_LOC.with(|prev_loc| {
let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_SIZE - 1); let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_PTR_SIZE - 1);
EDGES_MAP[x] = 1; let entry = EDGES_MAP_PTR.add(x);
*entry = 1;
*prev_loc.get() = id.overflowing_shr(1).0; *prev_loc.get() = id.overflowing_shr(1).0;
}); });
} }

View File

@ -27,7 +27,7 @@ use pyo3::{prelude::*, PyIterProtocol};
pub const SKIP_EXEC_HOOK: u64 = u64::MAX; pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq)]
#[repr(i32)] #[repr(i32)]
pub enum MmapPerms { pub enum MmapPerms {
None = 0, None = 0,
@ -201,7 +201,7 @@ extern "C" {
fn target_mmap(start: u64, len: u64, target_prot: i32, flags: i32, fd: i32, offset: u64) fn target_mmap(start: u64, len: u64, target_prot: i32, flags: i32, fd: i32, offset: u64)
-> u64; -> u64;
/// int target_mprotect(abi_ulong start, abi_ulong len, int prot); /// int target_mprotect(abi_ulong start, abi_ulong len, int prot)
fn target_mprotect(start: u64, len: u64, target_prot: i32) -> i32; fn target_mprotect(start: u64, len: u64, target_prot: i32) -> i32;
/// int target_munmap(abi_ulong start, abi_ulong len) /// int target_munmap(abi_ulong start, abi_ulong len)

View File

@ -11,6 +11,8 @@ pub trait QemuHelper<I, S>: 'static + Debug
where where
I: Input, I: Input,
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = true;
fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>)
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
@ -114,3 +116,11 @@ impl QemuInstrumentationFilter {
} }
} }
} }
#[must_use]
pub fn hash_me(mut x: u64) -> u64 {
x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0;
x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0;
x = (x.overflowing_shr(16).0 ^ x) ^ x;
x
}

View File

@ -1,5 +1,5 @@
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use strum_macros::EnumIter; pub use strum_macros::EnumIter;
#[cfg(feature = "python")] #[cfg(feature = "python")]
use pyo3::prelude::*; use pyo3::prelude::*;

View File

@ -1,11 +1,17 @@
use bio::data_structures::interval_tree::IntervalTree;
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
use std::collections::HashMap; use std::{
cell::UnsafeCell,
collections::{HashMap, HashSet},
sync::Mutex,
};
use thread_local::ThreadLocal;
use crate::{ use crate::{
emu::Emulator, emu::{Emulator, MmapPerms},
executor::QemuExecutor, executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple}, helper::{QemuHelper, QemuHelperTuple},
GuestAddr, SYS_mmap, SYS_mremap, GuestAddr, SYS_mmap, SYS_mprotect, SYS_mremap,
}; };
pub const SNAPSHOT_PAGE_SIZE: usize = 4096; pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
@ -13,19 +19,32 @@ pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
#[derive(Debug)] #[derive(Debug)]
pub struct SnapshotPageInfo { pub struct SnapshotPageInfo {
pub addr: GuestAddr, pub addr: GuestAddr,
pub dirty: bool, pub perms: MmapPerms,
pub data: [u8; SNAPSHOT_PAGE_SIZE], pub private: bool,
pub data: Option<Box<[u8; SNAPSHOT_PAGE_SIZE]>>,
}
#[derive(Default, Debug)]
pub struct SnapshotAccessInfo {
pub access_cache: [GuestAddr; 4],
pub access_cache_idx: usize,
pub dirty: HashSet<GuestAddr>,
}
impl SnapshotAccessInfo {
pub fn clear(&mut self) {
self.access_cache_idx = 0;
self.access_cache = [GuestAddr::MAX; 4];
self.dirty.clear();
}
} }
#[derive(Debug)] #[derive(Debug)]
// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html
pub struct QemuSnapshotHelper { pub struct QemuSnapshotHelper {
pub access_cache: [GuestAddr; 4], pub accesses: ThreadLocal<UnsafeCell<SnapshotAccessInfo>>,
pub access_cache_idx: usize, pub new_maps: Mutex<IntervalTree<GuestAddr, Option<MmapPerms>>>,
pub pages: HashMap<GuestAddr, SnapshotPageInfo>, pub pages: HashMap<GuestAddr, SnapshotPageInfo>,
pub dirty: Vec<GuestAddr>,
pub brk: GuestAddr, pub brk: GuestAddr,
pub new_maps: Vec<(GuestAddr, usize)>,
pub empty: bool, pub empty: bool,
} }
@ -33,32 +52,33 @@ impl QemuSnapshotHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
access_cache: [GuestAddr::MAX; 4], accesses: ThreadLocal::new(),
access_cache_idx: 0, new_maps: Mutex::new(IntervalTree::new()),
pages: HashMap::default(), pages: HashMap::default(),
dirty: vec![],
brk: 0, brk: 0,
new_maps: vec![],
empty: true, empty: true,
} }
} }
#[allow(clippy::uninit_assumed_init)]
pub fn snapshot(&mut self, emulator: &Emulator) { pub fn snapshot(&mut self, emulator: &Emulator) {
self.brk = emulator.get_brk(); self.brk = emulator.get_brk();
self.pages.clear(); self.pages.clear();
for map in emulator.mappings() { for map in emulator.mappings() {
// TODO track all the pages OR track mproctect
if !map.flags().is_w() {
continue;
}
let mut addr = map.start(); let mut addr = map.start();
while addr < map.end() { while addr < map.end() {
let mut info = SnapshotPageInfo { let mut info = SnapshotPageInfo {
addr, addr,
dirty: false, perms: map.flags(),
data: [0; SNAPSHOT_PAGE_SIZE], private: map.is_priv(),
data: None,
}; };
unsafe { emulator.read_mem(addr, &mut info.data) }; if map.flags().is_w() {
unsafe {
info.data = Some(Box::new(core::mem::MaybeUninit::uninit().assume_init()));
emulator.read_mem(addr, &mut info.data.as_mut().unwrap()[..]);
}
}
self.pages.insert(addr, info); self.pages.insert(addr, info);
addr += SNAPSHOT_PAGE_SIZE as GuestAddr; addr += SNAPSHOT_PAGE_SIZE as GuestAddr;
} }
@ -67,22 +87,20 @@ impl QemuSnapshotHelper {
} }
pub fn page_access(&mut self, page: GuestAddr) { pub fn page_access(&mut self, page: GuestAddr) {
if self.access_cache[0] == page unsafe {
|| self.access_cache[1] == page let acc = self.accesses.get_or_default().get();
|| self.access_cache[2] == page if (*acc).access_cache[0] == page
|| self.access_cache[3] == page || (*acc).access_cache[1] == page
{ || (*acc).access_cache[2] == page
return; || (*acc).access_cache[3] == page
} {
self.access_cache[self.access_cache_idx] = page;
self.access_cache_idx = (self.access_cache_idx + 1) & 3;
if let Some(info) = self.pages.get_mut(&page) {
if info.dirty {
return; return;
} }
info.dirty = true; let idx = (*acc).access_cache_idx;
(*acc).access_cache[idx] = page;
(*acc).access_cache_idx = (idx + 1) & 3;
(*acc).dirty.insert(page);
} }
self.dirty.push(page);
} }
pub fn access(&mut self, addr: GuestAddr, size: usize) { pub fn access(&mut self, addr: GuestAddr, size: usize) {
@ -96,27 +114,62 @@ impl QemuSnapshotHelper {
} }
pub fn reset(&mut self, emulator: &Emulator) { pub fn reset(&mut self, emulator: &Emulator) {
self.access_cache = [GuestAddr::MAX; 4]; self.reset_maps(emulator);
self.access_cache_idx = 0; for acc in self.accesses.iter_mut() {
while let Some(page) = self.dirty.pop() { for page in unsafe { &(*acc.get()).dirty } {
if let Some(info) = self.pages.get_mut(&page) { if let Some(info) = self.pages.get_mut(page) {
unsafe { emulator.write_mem(page, &info.data) }; // TODO avoid duplicated memcpy
info.dirty = false; if let Some(data) = info.data.as_ref() {
unsafe { emulator.write_mem(*page, &data[..]) };
}
}
} }
unsafe { (*acc.get()).clear() };
} }
emulator.set_brk(self.brk); emulator.set_brk(self.brk);
self.reset_maps(emulator);
} }
pub fn add_mapped(&mut self, start: GuestAddr, size: usize) { pub fn add_mapped(&mut self, start: GuestAddr, mut size: usize, perms: Option<MmapPerms>) {
self.new_maps.push((start, size)); if size % SNAPSHOT_PAGE_SIZE != 0 {
size = size + (SNAPSHOT_PAGE_SIZE - size % SNAPSHOT_PAGE_SIZE);
}
self.new_maps
.lock()
.unwrap()
.insert(start..start + (size as GuestAddr), perms);
} }
pub fn reset_maps(&mut self, emulator: &Emulator) { pub fn reset_maps(&mut self, emulator: &Emulator) {
for (addr, size) in &self.new_maps { let new_maps = self.new_maps.get_mut().unwrap();
drop(emulator.unmap(*addr, *size)); for r in new_maps.find(0..GuestAddr::MAX) {
let addr = r.interval().start;
let end = r.interval().end;
let perms = r.data();
let mut page = addr & (SNAPSHOT_PAGE_SIZE as GuestAddr - 1);
let mut prev = None;
while page < end {
if let Some(info) = self.pages.get(&page) {
if let Some((addr, size)) = prev {
drop(emulator.unmap(addr, size));
}
prev = None;
if let Some(p) = perms {
if info.perms != *p {
drop(emulator.mprotect(page, SNAPSHOT_PAGE_SIZE, info.perms));
}
}
} else if let Some((_, size)) = &mut prev {
*size += SNAPSHOT_PAGE_SIZE;
} else {
prev = Some((page, SNAPSHOT_PAGE_SIZE));
}
page += SNAPSHOT_PAGE_SIZE as GuestAddr;
}
if let Some((addr, size)) = prev {
drop(emulator.unmap(addr, size));
}
} }
self.new_maps.clear(); *new_maps = IntervalTree::new();
} }
} }
@ -262,15 +315,24 @@ where
return result; return result;
} }
if i64::from(sys_num) == SYS_mmap { if i64::from(sys_num) == SYS_mmap {
let h = helpers if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
.match_first_type_mut::<QemuSnapshotHelper>() let h = helpers
.unwrap(); .match_first_type_mut::<QemuSnapshotHelper>()
h.add_mapped(result as GuestAddr, a1 as usize); .unwrap();
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
}
} else if i64::from(sys_num) == SYS_mremap { } else if i64::from(sys_num) == SYS_mremap {
let h = helpers let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>() .match_first_type_mut::<QemuSnapshotHelper>()
.unwrap(); .unwrap();
h.add_mapped(a0 as GuestAddr, a2 as usize); h.add_mapped(result as GuestAddr, a2 as usize, None);
} else if i64::from(sys_num) == SYS_mprotect {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = helpers
.match_first_type_mut::<QemuSnapshotHelper>()
.unwrap();
h.add_mapped(a0 as GuestAddr, a2 as usize, Some(prot));
}
} }
result result
} }

View File

@ -1,5 +1,5 @@
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use strum_macros::EnumIter; pub use strum_macros::EnumIter;
#[cfg(feature = "python")] #[cfg(feature = "python")]
use pyo3::prelude::*; use pyo3::prelude::*;