From 4e3e31df4eef6bea59501a79491f5312e5dcb1ee Mon Sep 17 00:00:00 2001 From: Evan Richter Date: Fri, 28 Jan 2022 02:42:23 -0600 Subject: [PATCH] [libafl_qemu] GuestAddr type (#501) Guest addresses now represented by correct sized integers. Previously u64 was used to represent guest addresses. This is great for 64-bit targets, but clunky for other architectures. This introduces a GuestAddr type alias that is defined based on the selected emulation architecture. Note: This changes only the user-facing Rust interface. Before traversing the FFI boundary, all GuestAddrs are sized back to u64. Another Note: Guest addresses _from_ the FFI boundary are completely trusted. Values that are too large are truncated to fit into a GuestAddr using the `as GuestAddr` cast. This may not be ideal, as errors could be masked. If desired and the performance is ok, a non-breaking update could change all `as` casts to `.try_into().unwrap()` so that critical failures in FFI are always checked. --- libafl_qemu/src/asan.rs | 86 +++++++++++++----------- libafl_qemu/src/emu.rs | 130 ++++++++++++++++++++++-------------- libafl_qemu/src/executor.rs | 87 ++++++++++++++---------- libafl_qemu/src/lib.rs | 7 ++ libafl_qemu/src/snapshot.rs | 46 ++++++------- 5 files changed, 206 insertions(+), 150 deletions(-) diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index 8e91798d5c..a592360f5a 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -6,7 +6,7 @@ use crate::{ emu::{Emulator, SyscallHookResult}, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, - Regs, + GuestAddr, Regs, }; // TODO at some point, merge parts with libafl_frida @@ -231,16 +231,16 @@ impl QemuAsanHelper { #[allow(clippy::unused_self)] #[must_use] - pub fn is_poisoned(&self, emulator: &Emulator, addr: u64, size: usize) -> bool { + pub fn is_poisoned(&self, emulator: &Emulator, addr: GuestAddr, size: usize) -> bool { unsafe { asan_giovese_loadN(emulator.g2h(addr), size) != 0 } } - pub fn read_1(&mut self, emulator: &Emulator, addr: u64) { + pub fn read_1(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_load1(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, - addr, + addr.into(), 1, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -250,12 +250,12 @@ impl QemuAsanHelper { } } - pub fn read_2(&mut self, emulator: &Emulator, addr: u64) { + pub fn read_2(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_load2(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, - addr, + addr.into(), 2, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -265,12 +265,12 @@ impl QemuAsanHelper { } } - pub fn read_4(&mut self, emulator: &Emulator, addr: u64) { + pub fn read_4(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_load4(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, - addr, + addr.into(), 4, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -280,12 +280,12 @@ impl QemuAsanHelper { } } - pub fn read_8(&mut self, emulator: &Emulator, addr: u64) { + pub fn read_8(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_load8(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, - addr, + addr.into(), 8, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -295,12 +295,12 @@ impl QemuAsanHelper { } } - pub fn read_n(&mut self, emulator: &Emulator, addr: u64, size: usize) { + pub fn read_n(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) { unsafe { if self.enabled() && asan_giovese_loadN(emulator.g2h(addr), size) != 0 { asan_giovese_report_and_crash( 0, - addr, + addr.into(), size, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -310,12 +310,12 @@ impl QemuAsanHelper { } } - pub fn write_1(&mut self, emulator: &Emulator, addr: u64) { + pub fn write_1(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_store1(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, - addr, + addr.into(), 1, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -325,12 +325,12 @@ impl QemuAsanHelper { } } - pub fn write_2(&mut self, emulator: &Emulator, addr: u64) { + pub fn write_2(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_store2(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, - addr, + addr.into(), 2, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -340,12 +340,12 @@ impl QemuAsanHelper { } } - pub fn write_4(&mut self, emulator: &Emulator, addr: u64) { + pub fn write_4(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_store4(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, - addr, + addr.into(), 4, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -355,12 +355,12 @@ impl QemuAsanHelper { } } - pub fn write_8(&mut self, emulator: &Emulator, addr: u64) { + pub fn write_8(&mut self, emulator: &Emulator, addr: GuestAddr) { unsafe { if self.enabled() && asan_giovese_store8(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, - addr, + addr.into(), 8, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -370,12 +370,12 @@ impl QemuAsanHelper { } } - pub fn write_n(&mut self, emulator: &Emulator, addr: u64, size: usize) { + pub fn write_n(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) { unsafe { if self.enabled() && asan_giovese_storeN(emulator.g2h(addr), size) != 0 { asan_giovese_report_and_crash( 1, - addr, + addr.into(), size, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, @@ -386,12 +386,18 @@ impl QemuAsanHelper { } #[allow(clippy::unused_self)] - pub fn poison(&mut self, emulator: &Emulator, addr: u64, size: usize, poison: PoisonKind) { + pub fn poison( + &mut self, + emulator: &Emulator, + addr: GuestAddr, + size: usize, + poison: PoisonKind, + ) { unsafe { asan_giovese_poison_region(emulator.g2h(addr), size, poison.into()) }; } #[allow(clippy::unused_self)] - pub fn unpoison(&mut self, emulator: &Emulator, addr: u64, size: usize) { + pub fn unpoison(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) { unsafe { asan_giovese_unpoison_region(emulator.g2h(addr), size) }; } @@ -465,7 +471,7 @@ pub fn trace_read1_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -479,7 +485,7 @@ pub fn trace_read2_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -493,7 +499,7 @@ pub fn trace_read4_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -507,7 +513,7 @@ pub fn trace_read8_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -521,7 +527,7 @@ pub fn trace_read_n_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, size: usize, ) where I: Input, @@ -536,7 +542,7 @@ pub fn trace_write1_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -550,7 +556,7 @@ pub fn trace_write2_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -564,7 +570,7 @@ pub fn trace_write4_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -578,7 +584,7 @@ pub fn trace_write8_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -592,7 +598,7 @@ pub fn trace_write_n_asan( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, size: usize, ) where I: Input, @@ -626,27 +632,27 @@ where let mut r = 0; match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { - h.read_n(emulator, a1, a2 as usize); + h.read_n(emulator, a1 as GuestAddr, a2 as usize); } QasanAction::CheckStore => { - h.write_n(emulator, a1, a2 as usize); + h.write_n(emulator, a1 as GuestAddr, a2 as usize); } QasanAction::Poison => { h.poison( emulator, - a1, + a1 as GuestAddr, a2 as usize, PoisonKind::try_from(a3 as u8).unwrap(), ); } QasanAction::UserPoison => { - h.poison(emulator, a1, a2 as usize, PoisonKind::User); + h.poison(emulator, a1 as GuestAddr, a2 as usize, PoisonKind::User); } QasanAction::UnPoison => { - h.unpoison(emulator, a1, a2 as usize); + h.unpoison(emulator, a1 as GuestAddr, a2 as usize); } QasanAction::IsPoison => { - if h.is_poisoned(emulator, a1, a2 as usize) { + if h.is_poisoned(emulator, a1 as GuestAddr, a2 as usize) { r = 1; } } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 79ab2a1812..a43fc4ba22 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -12,6 +12,16 @@ use num_traits::Num; use std::{slice::from_raw_parts, str::from_utf8_unchecked}; use strum_macros::EnumIter; +#[cfg(not(any(feature = "x86_64", feature = "aarch64")))] +/// `GuestAddr` is u32 for 32-bit targets +pub type GuestAddr = u32; + +#[cfg(any(feature = "x86_64", feature = "aarch64"))] +/// `GuestAddr` is u64 for 64-bit targets +pub type GuestAddr = u64; + +pub type GuestUsize = GuestAddr; + #[cfg(feature = "python")] use pyo3::{prelude::*, PyIterProtocol}; @@ -120,9 +130,9 @@ impl SyscallHookResult { #[repr(C)] #[cfg_attr(feature = "python", pyclass(unsendable))] pub struct MapInfo { - start: u64, - end: u64, - offset: u64, + start: GuestAddr, + end: GuestAddr, + offset: GuestAddr, path: *const u8, flags: i32, is_priv: i32, @@ -131,17 +141,17 @@ pub struct MapInfo { #[cfg_attr(feature = "python", pymethods)] impl MapInfo { #[must_use] - pub fn start(&self) -> u64 { + pub fn start(&self) -> GuestAddr { self.start } #[must_use] - pub fn end(&self) -> u64 { + pub fn end(&self) -> GuestAddr { self.end } #[must_use] - pub fn offset(&self) -> u64 { + pub fn offset(&self) -> GuestAddr { self.offset } @@ -349,7 +359,7 @@ impl Emulator { /// This will write to a translated guest address (using `g2h`). /// It just adds `guest_base` and writes to that location, without checking the bounds. /// This may only be safely used for valid guest addresses! - pub unsafe fn write_mem(&self, addr: u64, buf: &[u8]) { + pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) { let host_addr = self.g2h(addr); copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len()); } @@ -360,7 +370,7 @@ impl Emulator { /// This will read from a translated guest address (using `g2h`). /// It just adds `guest_base` and writes to that location, without checking the bounds. /// This may only be safely used for valid guest addresses! - pub unsafe fn read_mem(&self, addr: u64, buf: &mut [u8]) { + pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) { let host_addr = self.g2h(addr); copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len()); } @@ -399,27 +409,27 @@ impl Emulator { } } - pub fn set_breakpoint(&self, addr: u64) { + pub fn set_breakpoint(&self, addr: GuestAddr) { unsafe { - libafl_qemu_set_breakpoint(addr); + libafl_qemu_set_breakpoint(addr.into()); } } - pub fn remove_breakpoint(&self, addr: u64) { + pub fn remove_breakpoint(&self, addr: GuestAddr) { unsafe { - libafl_qemu_remove_breakpoint(addr); + libafl_qemu_remove_breakpoint(addr.into()); } } - pub fn set_hook(&self, addr: u64, callback: extern "C" fn(u64), val: u64) { + pub fn set_hook(&self, addr: GuestAddr, callback: extern "C" fn(u64), val: u64) { unsafe { - libafl_qemu_set_hook(addr, callback, val); + libafl_qemu_set_hook(addr.into(), callback, val); } } - pub fn remove_hook(&self, addr: u64) { + pub fn remove_hook(&self, addr: GuestAddr) { unsafe { - libafl_qemu_remove_hook(addr); + libafl_qemu_remove_hook(addr.into()); } } @@ -433,13 +443,13 @@ impl Emulator { } #[must_use] - pub fn g2h(&self, addr: u64) -> *mut T { - unsafe { transmute(addr + guest_base as u64) } + pub fn g2h(&self, addr: GuestAddr) -> *mut T { + unsafe { transmute(addr as usize + guest_base) } } #[must_use] - pub fn h2g(&self, addr: *const T) -> u64 { - unsafe { (addr as usize - guest_base) as u64 } + pub fn h2g(&self, addr: *const T) -> GuestAddr { + unsafe { (addr as usize - guest_base) as GuestAddr } } #[must_use] @@ -448,21 +458,27 @@ impl Emulator { } #[must_use] - pub fn load_addr(&self) -> u64 { - unsafe { libafl_load_addr() } + pub fn load_addr(&self) -> GuestAddr { + unsafe { libafl_load_addr() as GuestAddr } } #[must_use] - pub fn get_brk(&self) -> u64 { - unsafe { libafl_get_brk() } + pub fn get_brk(&self) -> GuestAddr { + unsafe { libafl_get_brk() as GuestAddr } } - pub fn set_brk(&self, brk: u64) { - unsafe { libafl_set_brk(brk) }; + pub fn set_brk(&self, brk: GuestAddr) { + unsafe { libafl_set_brk(brk.into()) }; } - fn mmap(&self, addr: u64, size: usize, perms: MmapPerms, flags: c_int) -> Result { - let res = unsafe { target_mmap(addr, size as u64, perms.into(), flags, -1, 0) }; + fn mmap( + &self, + addr: GuestAddr, + size: usize, + perms: MmapPerms, + flags: c_int, + ) -> Result { + let res = unsafe { target_mmap(addr.into(), size as u64, perms.into(), flags, -1, 0) }; if res == 0 { Err(()) } else { @@ -470,12 +486,23 @@ impl Emulator { } } - pub fn map_private(&self, addr: u64, size: usize, perms: MmapPerms) -> Result { + pub fn map_private( + &self, + addr: GuestAddr, + size: usize, + perms: MmapPerms, + ) -> Result { self.mmap(addr, size, perms, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) .map_err(|_| format!("Failed to map {}", addr)) + .map(|addr| addr as GuestAddr) } - pub fn map_fixed(&self, addr: u64, size: usize, perms: MmapPerms) -> Result { + pub fn map_fixed( + &self, + addr: GuestAddr, + size: usize, + perms: MmapPerms, + ) -> Result { self.mmap( addr, size, @@ -483,10 +510,11 @@ impl Emulator { libc::MAP_FIXED | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, ) .map_err(|_| format!("Failed to map {}", addr)) + .map(|addr| addr as GuestAddr) } - pub fn mprotect(&self, addr: u64, size: usize, perms: MmapPerms) -> Result<(), String> { - let res = unsafe { target_mprotect(addr, size as u64, perms.into()) }; + pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> { + let res = unsafe { target_mprotect(addr.into(), size as u64, perms.into()) }; if res == 0 { Ok(()) } else { @@ -494,8 +522,8 @@ impl Emulator { } } - pub fn unmap(&self, addr: u64, size: usize) -> Result<(), String> { - if unsafe { target_munmap(addr, size as u64) } == 0 { + pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> { + if unsafe { target_munmap(addr.into(), size as u64) } == 0 { Ok(()) } else { Err(format!("Failed to unmap {}", addr)) @@ -652,14 +680,14 @@ impl Emulator { #[cfg(feature = "python")] pub mod pybind { - use super::{MmapPerms, SyscallHookResult}; + use super::{GuestAddr, GuestUsize, MmapPerms, SyscallHookResult}; use core::mem::transmute; use pyo3::exceptions::PyValueError; use pyo3::{prelude::*, types::PyInt}; use std::convert::TryFrom; static mut PY_SYSCALL_HOOK: Option = None; - static mut PY_GENERIC_HOOKS: Vec<(u64, PyObject)> = vec![]; + static mut PY_GENERIC_HOOKS: Vec<(GuestAddr, PyObject)> = vec![]; extern "C" fn py_syscall_hook_wrapper( sys_num: i32, @@ -719,13 +747,13 @@ pub mod pybind { } } - fn write_mem(&self, addr: u64, buf: &[u8]) { + fn write_mem(&self, addr: GuestAddr, buf: &[u8]) { unsafe { self.emu.write_mem(addr, buf); } } - fn read_mem(&self, addr: u64, size: usize) -> Vec { + fn read_mem(&self, addr: GuestAddr, size: usize) -> Vec { let mut buf = vec![0; size]; unsafe { self.emu.read_mem(addr, &mut buf); @@ -737,19 +765,19 @@ pub mod pybind { self.emu.num_regs() } - fn write_reg(&self, reg: i32, val: u64) -> PyResult<()> { + fn write_reg(&self, reg: i32, val: GuestUsize) -> PyResult<()> { self.emu.write_reg(reg, val).map_err(PyValueError::new_err) } - fn read_reg(&self, reg: i32) -> PyResult { + fn read_reg(&self, reg: i32) -> PyResult { self.emu.read_reg(reg).map_err(PyValueError::new_err) } - fn set_breakpoint(&self, addr: u64) { + fn set_breakpoint(&self, addr: GuestAddr) { self.emu.set_breakpoint(addr); } - fn remove_breakpoint(&self, addr: u64) { + fn remove_breakpoint(&self, addr: GuestAddr) { self.emu.remove_breakpoint(addr); } @@ -759,11 +787,11 @@ pub mod pybind { } } - fn g2h(&self, addr: u64) -> u64 { + fn g2h(&self, addr: GuestAddr) -> u64 { self.emu.g2h::<*const u8>(addr) as u64 } - fn h2g(&self, addr: u64) -> u64 { + fn h2g(&self, addr: u64) -> GuestAddr { self.emu.h2g(unsafe { transmute::<_, *const u8>(addr) }) } @@ -771,11 +799,11 @@ pub mod pybind { self.emu.binary_path().to_owned() } - fn load_addr(&self) -> u64 { + fn load_addr(&self) -> GuestAddr { self.emu.load_addr() } - fn map_private(&self, addr: u64, size: usize, perms: i32) -> PyResult { + fn map_private(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult { if let Ok(p) = MmapPerms::try_from(perms) { self.emu .map_private(addr, size, p) @@ -785,7 +813,7 @@ pub mod pybind { } } - fn map_fixed(&self, addr: u64, size: usize, perms: i32) -> PyResult { + fn map_fixed(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult { if let Ok(p) = MmapPerms::try_from(perms) { self.emu .map_fixed(addr, size, p) @@ -795,7 +823,7 @@ pub mod pybind { } } - fn mprotect(&self, addr: u64, size: usize, perms: i32) -> PyResult<()> { + fn mprotect(&self, addr: GuestAddr, size: usize, perms: i32) -> PyResult<()> { if let Ok(p) = MmapPerms::try_from(perms) { self.emu .mprotect(addr, size, p) @@ -805,7 +833,7 @@ pub mod pybind { } } - fn unmap(&self, addr: u64, size: usize) -> PyResult<()> { + fn unmap(&self, addr: GuestAddr, size: usize) -> PyResult<()> { self.emu.unmap(addr, size).map_err(PyValueError::new_err) } @@ -816,7 +844,7 @@ pub mod pybind { self.emu.set_pre_syscall_hook(py_syscall_hook_wrapper); } - fn set_hook(&self, addr: u64, hook: PyObject) { + fn set_hook(&self, addr: GuestAddr, hook: PyObject) { unsafe { let idx = PY_GENERIC_HOOKS.len(); PY_GENERIC_HOOKS.push((addr, hook)); @@ -824,7 +852,7 @@ pub mod pybind { } } - fn remove_hook(&self, addr: u64) { + fn remove_hook(&self, addr: GuestAddr) { unsafe { PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr); } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 00850b7482..7e603765b0 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -23,6 +23,7 @@ pub use crate::emu::SyscallHookResult; use crate::{ emu::{Emulator, SKIP_EXEC_HOOK}, helper::QemuHelperTuple, + GuestAddr, }; static mut QEMU_HELPERS_PTR: *const c_void = ptr::null(); @@ -120,6 +121,12 @@ where } } +// function signature for Read or Write hook functions with known length (1, 2, 4, 8) +type FixedLenHook = fn(&Emulator, &mut QT, &mut S, u64, GuestAddr); + +// function signature for Read or Write hook functions with runtime length n +type DynamicLenHook = fn(&Emulator, &mut QT, &mut S, u64, GuestAddr, usize); + static mut READ1_HOOKS: Vec<*const c_void> = vec![]; extern "C" fn read1_hooks_wrapper(id: u64, addr: u64) where @@ -130,8 +137,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &READ1_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -145,8 +152,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &READ2_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -160,8 +167,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &READ4_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -175,8 +182,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &READ8_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -190,8 +197,15 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &READ_N_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr, size as usize); + let func: DynamicLenHook = unsafe { transmute(*hook) }; + (func)( + &emulator, + helpers, + state, + id, + addr as GuestAddr, + size as usize, + ); } } @@ -205,8 +219,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE1_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -220,8 +234,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE2_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -235,8 +249,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE4_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -250,8 +264,8 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE8_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr); + let func: FixedLenHook = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr as GuestAddr); } } @@ -265,8 +279,15 @@ where let state = inprocess_get_state::().unwrap(); let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE_N_HOOKS } { - let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; - (func)(&emulator, helpers, state, id, addr, size as usize); + let func: DynamicLenHook = unsafe { transmute(*hook) }; + (func)( + &emulator, + helpers, + state, + id, + addr as GuestAddr, + size as usize, + ); } } @@ -564,7 +585,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_read1_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read1_execution(&self, hook: FixedLenHook) { unsafe { READ1_HOOKS.push(hook as *const _); } @@ -573,7 +594,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_read2_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read2_execution(&self, hook: FixedLenHook) { unsafe { READ2_HOOKS.push(hook as *const _); } @@ -582,7 +603,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_read4_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read4_execution(&self, hook: FixedLenHook) { unsafe { READ4_HOOKS.push(hook as *const _); } @@ -591,7 +612,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_read8_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read8_execution(&self, hook: FixedLenHook) { unsafe { READ8_HOOKS.push(hook as *const _); } @@ -600,10 +621,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_read_n_execution( - &self, - hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64, size: usize), - ) { + pub fn hook_read_n_execution(&self, hook: DynamicLenHook) { unsafe { READ_N_HOOKS.push(hook as *const _); } @@ -624,7 +642,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_write1_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write1_execution(&self, hook: FixedLenHook) { unsafe { WRITE1_HOOKS.push(hook as *const _); } @@ -633,7 +651,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_write2_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write2_execution(&self, hook: FixedLenHook) { unsafe { WRITE2_HOOKS.push(hook as *const _); } @@ -642,7 +660,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_write4_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write4_execution(&self, hook: FixedLenHook) { unsafe { WRITE4_HOOKS.push(hook as *const _); } @@ -651,7 +669,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_write8_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write8_execution(&self, hook: FixedLenHook) { unsafe { WRITE8_HOOKS.push(hook as *const _); } @@ -660,10 +678,7 @@ where } #[allow(clippy::unused_self)] - pub fn hook_write_n_execution( - &self, - hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64, size: usize), - ) { + pub fn hook_write_n_execution(&self, hook: DynamicLenHook) { unsafe { WRITE_N_HOOKS.push(hook as *const _); } diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 499c82d3fd..ad7814edbe 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -1,3 +1,10 @@ +// This lint triggers too often on the current GuestAddr type when emulating 64-bit targets because +// u64::from(GuestAddr) is a no-op, but the .into() call is needed when GuestAddr is u32. +#![cfg_attr( + any(feature = "x86_64", feature = "aarch64"), + allow(clippy::useless_conversion) +)] + use std::env; #[cfg(cpu_target = "aarch64")] diff --git a/libafl_qemu/src/snapshot.rs b/libafl_qemu/src/snapshot.rs index 761a92d1ae..4de98a8c27 100644 --- a/libafl_qemu/src/snapshot.rs +++ b/libafl_qemu/src/snapshot.rs @@ -5,14 +5,14 @@ use crate::{ emu::Emulator, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple}, - SYS_mmap, SYS_mremap, + GuestAddr, SYS_mmap, SYS_mremap, }; pub const SNAPSHOT_PAGE_SIZE: usize = 4096; #[derive(Debug)] pub struct SnapshotPageInfo { - pub addr: u64, + pub addr: GuestAddr, pub dirty: bool, pub data: [u8; SNAPSHOT_PAGE_SIZE], } @@ -20,12 +20,12 @@ pub struct SnapshotPageInfo { #[derive(Debug)] // TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html pub struct QemuSnapshotHelper { - pub access_cache: [u64; 4], + pub access_cache: [GuestAddr; 4], pub access_cache_idx: usize, - pub pages: HashMap, - pub dirty: Vec, - pub brk: u64, - pub new_maps: Vec<(u64, usize)>, + pub pages: HashMap, + pub dirty: Vec, + pub brk: GuestAddr, + pub new_maps: Vec<(GuestAddr, usize)>, pub empty: bool, } @@ -33,7 +33,7 @@ impl QemuSnapshotHelper { #[must_use] pub fn new() -> Self { Self { - access_cache: [u64::MAX; 4], + access_cache: [GuestAddr::MAX; 4], access_cache_idx: 0, pages: HashMap::default(), dirty: vec![], @@ -60,13 +60,13 @@ impl QemuSnapshotHelper { }; unsafe { emulator.read_mem(addr, &mut info.data) }; self.pages.insert(addr, info); - addr += SNAPSHOT_PAGE_SIZE as u64; + addr += SNAPSHOT_PAGE_SIZE as GuestAddr; } } self.empty = false; } - pub fn page_access(&mut self, page: u64) { + pub fn page_access(&mut self, page: GuestAddr) { if self.access_cache[0] == page || self.access_cache[1] == page || self.access_cache[2] == page @@ -85,18 +85,18 @@ impl QemuSnapshotHelper { self.dirty.push(page); } - pub fn access(&mut self, addr: u64, size: usize) { + pub fn access(&mut self, addr: GuestAddr, size: usize) { debug_assert!(size > 0); - let page = addr & (SNAPSHOT_PAGE_SIZE as u64 - 1); + let page = addr & (SNAPSHOT_PAGE_SIZE as GuestAddr - 1); self.page_access(page); - let second_page = (addr + size as u64 - 1) & (SNAPSHOT_PAGE_SIZE as u64 - 1); + let second_page = (addr + size as GuestAddr - 1) & (SNAPSHOT_PAGE_SIZE as GuestAddr - 1); if page != second_page { self.page_access(second_page); } } pub fn reset(&mut self, emulator: &Emulator) { - self.access_cache = [u64::MAX; 4]; + self.access_cache = [GuestAddr::MAX; 4]; self.access_cache_idx = 0; while let Some(page) = self.dirty.pop() { if let Some(info) = self.pages.get_mut(&page) { @@ -108,7 +108,7 @@ impl QemuSnapshotHelper { self.reset_maps(emulator); } - pub fn add_mapped(&mut self, start: u64, size: usize) { + pub fn add_mapped(&mut self, start: GuestAddr, size: usize) { self.new_maps.push((start, size)); } @@ -160,7 +160,7 @@ pub fn trace_write1_snapshot( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -176,7 +176,7 @@ pub fn trace_write2_snapshot( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -192,7 +192,7 @@ pub fn trace_write4_snapshot( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -208,7 +208,7 @@ pub fn trace_write8_snapshot( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, ) where I: Input, QT: QemuHelperTuple, @@ -224,7 +224,7 @@ pub fn trace_write_n_snapshot( helpers: &mut QT, _state: &mut S, _id: u64, - addr: u64, + addr: GuestAddr, size: usize, ) where I: Input, @@ -256,7 +256,7 @@ where I: Input, QT: QemuHelperTuple, { - if result == u64::MAX + if result as GuestAddr == GuestAddr::MAX /* -1 */ { return result; @@ -265,12 +265,12 @@ where let h = helpers .match_first_type_mut::() .unwrap(); - h.add_mapped(result, a1 as usize); + h.add_mapped(result as GuestAddr, a1 as usize); } else if i64::from(sys_num) == SYS_mremap { let h = helpers .match_first_type_mut::() .unwrap(); - h.add_mapped(a0, a2 as usize); + h.add_mapped(a0 as GuestAddr, a2 as usize); } result }