#![allow(clippy::cast_possible_wrap)] use std::{ collections::{HashMap, HashSet}, env, fs, sync::Mutex, }; use libafl::{inputs::UsesInput, state::HasMetadata}; use libc::{ c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE, }; use meminterval::{Interval, IntervalTree}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use crate::{ emu::{Emulator, MemAccessInfo, SyscallHookResult}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, hooks::QemuHooks, GuestAddr, }; // TODO at some point, merge parts with libafl_frida pub const HIGH_SHADOW_ADDR: *mut c_void = 0x02008fff7000 as *mut c_void; pub const LOW_SHADOW_ADDR: *mut c_void = 0x00007fff8000 as *mut c_void; pub const GAP_SHADOW_ADDR: *mut c_void = 0x00008fff7000 as *mut c_void; pub const HIGH_SHADOW_SIZE: usize = 0xdfff0000fff; pub const LOW_SHADOW_SIZE: usize = 0xfffefff; pub const GAP_SHADOW_SIZE: usize = 0x1ffffffffff; pub const SHADOW_OFFSET: isize = 0x7fff8000; pub const QASAN_FAKESYS_NR: i32 = 0xa2a4; pub const SHADOW_PAGE_SIZE: usize = 4096; pub const SHADOW_PAGE_MASK: GuestAddr = !(SHADOW_PAGE_SIZE as GuestAddr - 1); #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy)] #[repr(u64)] pub enum QasanAction { CheckLoad, CheckStore, Poison, UserPoison, UnPoison, IsPoison, Alloc, Dealloc, Enable, Disable, SwapState, } #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy)] #[repr(i8)] pub enum PoisonKind { Valid = 0, Partial1 = 1, Partial2 = 2, Partial3 = 3, Partial4 = 4, Partial5 = 5, Partial6 = 6, Partial7 = 7, ArrayCookie = -84, // 0xac StackRz = -16, // 0xf0 StackLeftRz = -15, // 0xf1 StackMidRz = -14, // 0xf2 StackRightRz = -13, // 0xf3 StacKFreed = -11, // 0xf5 StackOOScope = -8, // 0xf8 GlobalRz = -7, // 0xf9 HeapRz = -23, // 0xe9 User = -9, // 0xf7 HeapLeftRz = -6, // 0xfa HeapRightRz = -5, // 0xfb HeapFreed = -3, // 0xfd } pub enum AsanError { Read(GuestAddr, usize), Write(GuestAddr, usize), BadFree(GuestAddr, Option>), MemLeak(Interval), } pub type AsanErrorCallback = Box; pub struct AsanGiovese { pub alloc_tree: Mutex>, pub saved_tree: IntervalTree, pub error_callback: Option, pub dirty_shadow: Mutex>, pub saved_shadow: HashMap>, pub snapshot_shadow: bool, } impl core::fmt::Debug for AsanGiovese { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("AsanGiovese") .field("alloc_tree", &self.alloc_tree) .field("dirty_shadow", &self.dirty_shadow) .finish() } } impl AsanGiovese { pub unsafe fn map_shadow() { assert!( libc::mmap( HIGH_SHADOW_ADDR, HIGH_SHADOW_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 ) != MAP_FAILED ); assert!( libc::mmap( LOW_SHADOW_ADDR, LOW_SHADOW_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 ) != MAP_FAILED ); assert!( libc::mmap( GAP_SHADOW_ADDR, GAP_SHADOW_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, -1, 0 ) != MAP_FAILED ); } #[inline] #[must_use] pub fn is_invalid_access_1(emu: &Emulator, addr: GuestAddr) -> bool { unsafe { let h = emu.g2h::<*const c_void>(addr) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); let k = *shadow_addr as isize; k != 0 && (h & 7).wrapping_add(1) > k } } #[inline] #[must_use] pub fn is_invalid_access_2(emu: &Emulator, addr: GuestAddr) -> bool { unsafe { let h = emu.g2h::<*const c_void>(addr) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); let k = *shadow_addr as isize; k != 0 && (h & 7).wrapping_add(2) > k } } #[inline] #[must_use] pub fn is_invalid_access_4(emu: &Emulator, addr: GuestAddr) -> bool { unsafe { let h = emu.g2h::<*const c_void>(addr) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); let k = *shadow_addr as isize; k != 0 && (h & 7).wrapping_add(4) > k } } #[inline] #[must_use] pub fn is_invalid_access_8(emu: &Emulator, addr: GuestAddr) -> bool { unsafe { let h = emu.g2h::<*const c_void>(addr) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); *shadow_addr != 0 } } #[inline] #[must_use] #[allow(clippy::cast_sign_loss)] pub fn is_invalid_access(emu: &Emulator, addr: GuestAddr, n: usize) -> bool { unsafe { if n == 0 { return false; } let n = n as isize; let mut start = addr; let end = start.wrapping_add(n as GuestAddr); let last_8 = end & !7; if start & 0x7 != 0 { let next_8 = (start & !7).wrapping_add(8); let first_size = next_8.wrapping_sub(start) as isize; if n <= first_size { let h = emu.g2h::<*const c_void>(start) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); let k = *shadow_addr as isize; return k != 0 && (h & 7).wrapping_add(n) > k; } let h = emu.g2h::<*const c_void>(start) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); let k = *shadow_addr as isize; if k != 0 && (h & 7).wrapping_add(first_size) > k { return true; } start = next_8; } while start < last_8 { let h = emu.g2h::<*const c_void>(start) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); if *shadow_addr != 0 { return true; } start = (start).wrapping_add(8); } if last_8 != end { let h = emu.g2h::<*const c_void>(start) as isize; let last_size = end.wrapping_sub(last_8) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); let k = *shadow_addr as isize; return k != 0 && (h & 7).wrapping_add(last_size) > k; } false } } #[inline] #[allow(clippy::cast_sign_loss)] pub fn poison(&mut self, emu: &Emulator, addr: GuestAddr, n: usize, poison_byte: i8) -> bool { unsafe { if n == 0 { return false; } if self.snapshot_shadow { let mut page = addr & SHADOW_PAGE_MASK; let mut set = self.dirty_shadow.lock().unwrap(); while page < addr + n as GuestAddr { set.insert(page); page += SHADOW_PAGE_SIZE as GuestAddr; } } let n = n as isize; let mut start = addr; let end = start.wrapping_add(n as GuestAddr); let last_8 = end & !7; if start & 0x7 != 0 { let next_8 = (start & !7).wrapping_add(8); let first_size = next_8.wrapping_sub(start) as isize; if n < first_size { return false; } let h = emu.g2h::<*const c_void>(start) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); *shadow_addr = (8isize).wrapping_sub(first_size) as i8; start = next_8; } while start < last_8 { let h = emu.g2h::<*const c_void>(start) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); *shadow_addr = poison_byte; start = (start).wrapping_add(8); } true } } #[inline] #[allow(clippy::must_use_candidate)] #[allow(clippy::cast_sign_loss)] pub fn unpoison(emu: &Emulator, addr: GuestAddr, n: usize) -> bool { unsafe { let n = n as isize; let mut start = addr; let end = start.wrapping_add(n as GuestAddr); while start < end { let h = emu.g2h::<*const c_void>(start) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); *shadow_addr = 0; start = (start).wrapping_add(8); } true } } #[inline] fn unpoison_page(emu: &Emulator, page: GuestAddr) { unsafe { let h = emu.g2h::<*const c_void>(page) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); shadow_addr.write_bytes(0, SHADOW_PAGE_SIZE); } } #[inline] #[allow(clippy::mut_from_ref)] fn get_shadow_page(emu: &Emulator, page: GuestAddr) -> &mut [i8] { unsafe { let h = emu.g2h::<*const c_void>(page) as isize; let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET); std::slice::from_raw_parts_mut(shadow_addr, SHADOW_PAGE_SIZE) } } #[must_use] pub fn new(snapshot_shadow: bool) -> Self { Self { alloc_tree: Mutex::new(IntervalTree::new()), saved_tree: IntervalTree::new(), error_callback: None, dirty_shadow: Mutex::new(HashSet::default()), saved_shadow: HashMap::default(), snapshot_shadow, } } #[must_use] pub fn with_error_callback(snapshot_shadow: bool, error_callback: AsanErrorCallback) -> Self { Self { alloc_tree: Mutex::new(IntervalTree::new()), saved_tree: IntervalTree::new(), error_callback: Some(error_callback), dirty_shadow: Mutex::new(HashSet::default()), saved_shadow: HashMap::default(), snapshot_shadow, } } pub fn report_and_crash(&mut self, emu: &Emulator, error: AsanError) { if let Some(cb) = self.error_callback.as_mut() { (cb)(emu, error); } else { std::process::abort(); } } pub fn alloc_insert(&mut self, start: GuestAddr, end: GuestAddr) { self.alloc_tree.lock().unwrap().insert(start..end, ()); } pub fn alloc_remove(&mut self, start: GuestAddr, end: GuestAddr) { let mut tree = self.alloc_tree.lock().unwrap(); let mut found = vec![]; for entry in tree.query(start..end) { found.push(*entry.interval); } for interval in found { tree.delete(interval); } } #[must_use] pub fn alloc_search(&mut self, query: GuestAddr) -> Option> { self.alloc_tree .lock() .unwrap() .query(query..=query) .next() .map(|entry| *entry.interval) } pub fn snapshot(&mut self, emu: &Emulator) { if self.snapshot_shadow { let set = self.dirty_shadow.lock().unwrap(); for &page in set.iter() { let data = Self::get_shadow_page(emu, page).to_vec(); self.saved_shadow.insert(page, data); } let tree = self.alloc_tree.lock().unwrap(); self.saved_tree = tree.clone(); } } pub fn rollback(&mut self, emu: &Emulator, detect_leaks: bool) { let mut leaks = vec![]; { let mut tree = self.alloc_tree.lock().unwrap(); if detect_leaks { for entry in tree.query(0..GuestAddr::MAX) { leaks.push(*entry.interval); } } if self.snapshot_shadow { tree.clear(); } } if self.snapshot_shadow { let mut set = self.dirty_shadow.lock().unwrap(); for &page in set.iter() { let original = self.saved_shadow.get(&page); if let Some(data) = original { let cur = Self::get_shadow_page(emu, page); cur.copy_from_slice(data); } else { Self::unpoison_page(emu, page); } } set.clear(); } for interval in leaks { self.report_and_crash(emu, AsanError::MemLeak(interval)); } } } static mut ASAN_INITED: bool = false; pub fn init_with_asan(args: &mut Vec, env: &mut [(String, String)]) -> Emulator { assert!(!args.is_empty()); let current = env::current_exe().unwrap(); let asan_lib = fs::canonicalize(current) .unwrap() .parent() .unwrap() .join("libqasan.so"); let asan_lib = asan_lib .to_str() .expect("The path to the asan lib is invalid") .to_string(); let add_asan = |e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..]; let mut added = false; for (k, v) in env.iter_mut() { if k == "QEMU_SET_ENV" { let mut new_v = vec![]; for e in v.split(',') { if e.starts_with("LD_PRELOAD=") { added = true; new_v.push(add_asan(e)); } else { new_v.push(e.to_string()); } } *v = new_v.join(","); } } for i in 0..args.len() { if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") { added = true; args[i + 1] = add_asan(&args[i + 1]); } } if !added { args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib); args.insert(1, "-E".into()); } unsafe { AsanGiovese::map_shadow(); ASAN_INITED = true; } Emulator::new(args, env) } pub enum QemuAsanOptions { None, Snapshot, DetectLeaks, SnapshotDetectLeaks, } pub type QemuAsanChildHelper = QemuAsanHelper; #[derive(Debug)] pub struct QemuAsanHelper { enabled: bool, detect_leaks: bool, empty: bool, rt: AsanGiovese, filter: QemuInstrumentationFilter, } impl QemuAsanHelper { #[must_use] pub fn new(filter: QemuInstrumentationFilter, options: QemuAsanOptions) -> Self { assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)"); let (snapshot, detect_leaks) = match options { QemuAsanOptions::None => (false, false), QemuAsanOptions::Snapshot => (true, false), QemuAsanOptions::DetectLeaks => (false, true), QemuAsanOptions::SnapshotDetectLeaks => (true, true), }; Self { enabled: true, detect_leaks, empty: true, rt: AsanGiovese::new(snapshot), filter, } } #[must_use] pub fn with_error_callback( filter: QemuInstrumentationFilter, error_callback: AsanErrorCallback, options: QemuAsanOptions, ) -> Self { assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)"); let (snapshot, detect_leaks) = match options { QemuAsanOptions::None => (false, false), QemuAsanOptions::Snapshot => (true, false), QemuAsanOptions::DetectLeaks => (false, true), QemuAsanOptions::SnapshotDetectLeaks => (true, true), }; Self { enabled: true, detect_leaks, empty: true, rt: AsanGiovese::with_error_callback(snapshot, error_callback), filter, } } #[must_use] pub fn must_instrument(&self, addr: u64) -> bool { self.filter.allowed(addr) } #[must_use] pub fn enabled(&self) -> bool { self.enabled } pub fn set_enabled(&mut self, enabled: bool) { self.enabled = enabled; } pub fn alloc(&mut self, _emulator: &Emulator, start: GuestAddr, end: GuestAddr) { self.rt.alloc_insert(start, end); } pub fn dealloc(&mut self, emulator: &Emulator, addr: GuestAddr) { let chunk = self.rt.alloc_search(addr); if let Some(ck) = chunk { if ck.start != addr { // Free not the start of the chunk self.rt .report_and_crash(emulator, AsanError::BadFree(addr, Some(ck))); } } else { // Free of wild ptr self.rt .report_and_crash(emulator, AsanError::BadFree(addr, None)); } } #[allow(clippy::unused_self)] #[must_use] pub fn is_poisoned(&self, emulator: &Emulator, addr: GuestAddr, size: usize) -> bool { AsanGiovese::is_invalid_access(emulator, addr, size) } pub fn read_1(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_1(emulator, addr) { self.rt.report_and_crash(emulator, AsanError::Read(addr, 1)); } } pub fn read_2(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_2(emulator, addr) { self.rt.report_and_crash(emulator, AsanError::Read(addr, 2)); } } pub fn read_4(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_4(emulator, addr) { self.rt.report_and_crash(emulator, AsanError::Read(addr, 4)); } } pub fn read_8(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_8(emulator, addr) { self.rt.report_and_crash(emulator, AsanError::Read(addr, 8)); } } pub fn read_n(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) { if self.enabled() && AsanGiovese::is_invalid_access(emulator, addr, size) { self.rt .report_and_crash(emulator, AsanError::Read(addr, size)); } } pub fn write_1(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_1(emulator, addr) { self.rt .report_and_crash(emulator, AsanError::Write(addr, 1)); } } pub fn write_2(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_2(emulator, addr) { self.rt .report_and_crash(emulator, AsanError::Write(addr, 2)); } } pub fn write_4(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_4(emulator, addr) { self.rt .report_and_crash(emulator, AsanError::Write(addr, 4)); } } pub fn write_8(&mut self, emulator: &Emulator, addr: GuestAddr) { if self.enabled() && AsanGiovese::is_invalid_access_8(emulator, addr) { self.rt .report_and_crash(emulator, AsanError::Write(addr, 8)); } } pub fn write_n(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) { if self.enabled() && AsanGiovese::is_invalid_access(emulator, addr, size) { self.rt .report_and_crash(emulator, AsanError::Write(addr, size)); } } pub fn poison( &mut self, emulator: &Emulator, addr: GuestAddr, size: usize, poison: PoisonKind, ) { self.rt.poison(emulator, addr, size, poison.into()); } #[allow(clippy::unused_self)] pub fn unpoison(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) { AsanGiovese::unpoison(emulator, addr, size); } pub fn reset(&mut self, emulator: &Emulator) { self.rt.rollback(emulator, self.detect_leaks); } } impl Default for QemuAsanHelper { fn default() -> Self { Self::new(QemuInstrumentationFilter::None, QemuAsanOptions::Snapshot) } } impl QemuHelper for QemuAsanHelper where S: UsesInput + HasMetadata, { const HOOKS_DO_SIDE_EFFECTS: bool = false; fn init_hooks(&self, hooks: &QemuHooks<'_, QT, S>) where QT: QemuHelperTuple, { hooks.syscalls(qasan_fake_syscall::); } fn first_exec(&self, hooks: &QemuHooks<'_, QT, S>) where QT: QemuHelperTuple, { hooks.reads( Some(gen_readwrite_asan::), Some(trace_read1_asan::), Some(trace_read2_asan::), Some(trace_read4_asan::), Some(trace_read8_asan::), Some(trace_read_n_asan::), ); hooks.writes( Some(gen_readwrite_asan::), Some(trace_write1_asan::), Some(trace_write2_asan::), Some(trace_write4_asan::), Some(trace_write8_asan::), Some(trace_write_n_asan::), ); } fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) { if self.empty { self.rt.snapshot(emulator); self.empty = false; } } fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) { self.reset(emulator); } } pub fn gen_readwrite_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, pc: GuestAddr, _info: MemAccessInfo, ) -> Option where S: UsesInput, QT: QemuHelperTuple, { let h = hooks.match_helper_mut::().unwrap(); if h.must_instrument(pc.into()) { Some(pc.into()) } else { None } } pub fn trace_read1_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.read_1(&emulator, addr); } pub fn trace_read2_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.read_2(&emulator, addr); } pub fn trace_read4_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.read_4(&emulator, addr); } pub fn trace_read8_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.read_8(&emulator, addr); } pub fn trace_read_n_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, size: usize, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.read_n(&emulator, addr, size); } pub fn trace_write1_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.write_1(&emulator, addr); } pub fn trace_write2_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.write_2(&emulator, addr); } pub fn trace_write4_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.write_4(&emulator, addr); } pub fn trace_write8_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.write_8(&emulator, addr); } pub fn trace_write_n_asan( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, _id: u64, addr: GuestAddr, size: usize, ) where S: UsesInput, QT: QemuHelperTuple, { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); h.read_n(&emulator, addr, size); } #[allow(clippy::too_many_arguments)] pub fn qasan_fake_syscall( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, sys_num: i32, a0: u64, a1: u64, a2: u64, a3: u64, _a4: u64, _a5: u64, _a6: u64, _a7: u64, ) -> SyscallHookResult where S: UsesInput, QT: QemuHelperTuple, { if sys_num == QASAN_FAKESYS_NR { let emulator = hooks.emulator().clone(); let h = hooks.match_helper_mut::().unwrap(); let mut r = 0; match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { h.read_n(&emulator, a1 as GuestAddr, a2 as usize); } QasanAction::CheckStore => { h.write_n(&emulator, a1 as GuestAddr, a2 as usize); } QasanAction::Poison => { h.poison( &emulator, a1 as GuestAddr, a2 as usize, PoisonKind::try_from(a3 as i8).unwrap(), ); } QasanAction::UserPoison => { h.poison(&emulator, a1 as GuestAddr, a2 as usize, PoisonKind::User); } QasanAction::UnPoison => { h.unpoison(&emulator, a1 as GuestAddr, a2 as usize); } QasanAction::IsPoison => { if h.is_poisoned(&emulator, a1 as GuestAddr, a2 as usize) { r = 1; } } QasanAction::Alloc => { h.alloc(&emulator, a1 as GuestAddr, a2 as GuestAddr); } QasanAction::Dealloc => { h.dealloc(&emulator, a1 as GuestAddr); } QasanAction::Enable => { h.set_enabled(true); } QasanAction::Disable => { h.set_enabled(false); } QasanAction::SwapState => { h.set_enabled(!h.enabled()); } } SyscallHookResult::new(Some(r)) } else { SyscallHookResult::new(None) } }