Refactor QEMU hooks (#1690)

* Rewrite QEMU Asan

* fake sys

* New hooks

* edge cov helper

* opaque raw hook

* new hooks

* EMulator::get

* new asan

* fix fuzzers

* fix types

* fix

* fix

* fix

* merge fix

* fix
This commit is contained in:
Andrea Fioraldi 2023-11-23 18:57:15 +01:00 committed by GitHub
parent 43c9100f59
commit f1aee3c376
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3385 additions and 1781 deletions

View File

@ -329,7 +329,7 @@ fn fuzz(
};
let mut hooks = QemuHooks::new(
&emu,
emu.clone(),
tuple_list!(
QemuEdgeCoverageChildHelper::default(),
QemuCmpLogChildHelper::default(),

View File

@ -45,15 +45,15 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
// asan::{init_with_asan, QemuAsanHelper},
cmplog::{CmpLogObserver, QemuCmpLogHelper},
//asan::{init_with_asan, QemuAsanHelper},
edges::edges_map_mut_slice,
edges::QemuEdgeCoverageHelper,
edges::MAX_EDGES_NUM,
elf::EasyElf,
emu::Emulator,
filter_qemu_args,
hooks::QemuHooks,
Emulator,
GuestReg,
//snapshot::QemuSnapshotHelper,
MmapPerms,
@ -173,7 +173,7 @@ fn fuzz(
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env).unwrap();
//let emu = init_with_asan(&mut args, &mut env);
// let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap();
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
@ -342,11 +342,11 @@ fn fuzz(
};
let mut hooks = QemuHooks::new(
&emu,
emu.clone(),
tuple_list!(
QemuEdgeCoverageHelper::default(),
QemuCmpLogHelper::default(),
//QemuAsanHelper::default(),
// QemuAsanHelper::default(asan),
//QemuSnapshotHelper::new()
),
);

View File

@ -215,7 +215,10 @@ pub fn fuzz() -> Result<(), Error> {
ExitKind::Ok
};
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageChildHelper::default(),));
let mut hooks = QemuHooks::new(
emu.clone(),
tuple_list!(QemuEdgeCoverageChildHelper::default(),),
);
let mut executor = QemuForkExecutor::new(
&mut hooks,

View File

@ -236,7 +236,7 @@ pub fn fuzz() {
coverage.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
let mut hooks = QemuHooks::new(
&emu,
emu.clone(),
tuple_list!(QemuDrCovHelper::new(
QemuInstrumentationFilter::None,
rangemap,

View File

@ -103,11 +103,12 @@ impl<'a> Client<'a> {
let mut env = self.env()?;
log::debug!("ENV: {:#?}", env);
let emu = {
let (emu, mut asan) = {
if self.options.is_asan_core(core_id) {
init_with_asan(&mut args, &mut env)?
let (emu, asan) = init_with_asan(&mut args, &mut env)?;
(emu, Some(asan))
} else {
Emulator::new(&args, &env)?
(Emulator::new(&args, &env)?, None)
}
};
@ -136,11 +137,14 @@ impl<'a> Client<'a> {
let helpers = tuple_list!(
edge_coverage_helper,
QemuCmpLogHelper::default(),
QemuAsanHelper::default(),
QemuAsanHelper::default(asan.take().unwrap()),
);
instance.build().run(helpers, state)
} else if is_asan {
let helpers = tuple_list!(edge_coverage_helper, QemuAsanHelper::default(),);
let helpers = tuple_list!(
edge_coverage_helper,
QemuAsanHelper::default(asan.take().unwrap()),
);
instance.build().run(helpers, state)
} else if is_cmplog {
let helpers = tuple_list!(edge_coverage_helper, QemuCmpLogHelper::default(),);

View File

@ -59,7 +59,7 @@ impl<'a> Instance<'a> {
where
QT: QemuHelperTuple<ClientState>,
{
let mut hooks = QemuHooks::new(self.emu, helpers);
let mut hooks = QemuHooks::new(self.emu.clone(), helpers);
// Create an observation channel using the coverage map
let edges_observer = unsafe {

View File

@ -192,7 +192,7 @@ pub fn fuzz() {
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default()));
let mut hooks = QemuHooks::new(emu.clone(), tuple_list!(QemuEdgeCoverageHelper::default()));
// Create a QEMU in-process executor
let mut executor = QemuExecutor::new(

View File

@ -65,6 +65,7 @@ rangemap = "1.3"
log = "0.4.20"
addr2line = "0.21"
typed-arena = "2.0"
paste = "1"
enum-map = "2.7"
pyo3 = { version = "0.18", optional = true }

View File

@ -32,3 +32,4 @@ json = "0.12"
shell-words = "1.1"
pkg-config = "0.3.26"
cc = "1.0"
regex = "1"

View File

@ -77,6 +77,7 @@ const WRAPPER_HEADER: &str = r#"
#include "qemu/plugin-memory.h"
#include "libafl_extras/exit.h"
#include "libafl_extras/hook.h"
"#;
@ -114,6 +115,7 @@ pub fn generate(
.allowlist_type("libafl_exit_reason_kind")
.allowlist_type("libafl_exit_reason_sync_backdoor")
.allowlist_type("libafl_exit_reason_breakpoint")
.allowlist_type("Syx.*")
.allowlist_function("qemu_user_init")
.allowlist_function("target_mmap")
.allowlist_function("target_mprotect")
@ -128,12 +130,9 @@ pub fn generate(
.allowlist_function("qemu_plugin_hwaddr_phys_addr")
.allowlist_function("qemu_plugin_get_hwaddr")
.allowlist_function("qemu_target_page_size")
.allowlist_function("syx_snapshot_init")
.allowlist_function("syx_snapshot_new")
.allowlist_function("syx_snapshot_root_restore")
.allowlist_function("syx_snapshot_dirty_list_add")
.allowlist_function("syx_.*")
.allowlist_function("device_list_all")
.allowlist_function("libafl_get_exit_reason")
.allowlist_function("libafl_.*")
.blocklist_function("main_loop_wait") // bindgen issue #1313
.parse_callbacks(Box::new(bindgen::CargoCallbacks));

View File

@ -8,7 +8,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "8db5524416b52c999459f1fe3373846bdcb23ac1";
const QEMU_REVISION: &str = "6a63c7f7924d3db1dfff99e7b6cf460e8fb35785";
fn build_dep_check(tools: &[&str]) {
for tool in tools {

View File

@ -5,6 +5,7 @@ use std::{
process::Command,
};
use regex::Regex;
use which::which;
mod bindings;
@ -28,6 +29,13 @@ pub fn build_with_bindings(
.expect("Failed to generate the bindings");
bind.write_to_file(bindings_file)
.expect("Faield to write to the bindings file");
// """Fix""" the bindings here
let contents =
fs::read_to_string(bindings_file).expect("Should have been able to read the file");
let re = Regex::new("(Option<\\s*)unsafe( extern \"C\" fn\\(data: u64)").unwrap();
let replaced = re.replace_all(&contents, "$1$2");
fs::write(bindings_file, replaced.as_bytes()).expect("Unable to write file");
}
// For bindgen, the llvm version must be >= of the rust llvm version

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@ use crate::{
calls::FullBacktraceCollector,
emu::{EmuError, Emulator, MemAccessInfo, SyscallHookResult},
helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks,
hooks::{Hook, QemuHooks},
GuestAddr, Regs,
};
@ -61,6 +61,14 @@ pub enum QasanAction {
SwapState,
}
impl TryFrom<u32> for QasanAction {
type Error = num_enum::TryFromPrimitiveError<QasanAction>;
fn try_from(value: u32) -> Result<Self, Self::Error> {
QasanAction::try_from(value as u64)
}
}
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, PartialEq)]
#[repr(i8)]
pub enum PoisonKind {
@ -143,7 +151,7 @@ impl AllocTreeItem {
self.allocated = false;
}
}
use std::pin::Pin;
pub struct AsanGiovese {
pub alloc_tree: Mutex<IntervalTree<GuestAddr, AllocTreeItem>>,
pub saved_tree: IntervalTree<GuestAddr, AllocTreeItem>,
@ -163,7 +171,7 @@ impl core::fmt::Debug for AsanGiovese {
}
impl AsanGiovese {
pub unsafe fn map_shadow() {
unsafe fn map_shadow() {
assert!(
libc::mmap(
HIGH_SHADOW_ADDR,
@ -196,6 +204,80 @@ impl AsanGiovese {
);
}
#[must_use]
fn new(emu: &Emulator) -> Pin<Box<Self>> {
let res = 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: true, // By default, track the dirty shadow pages
};
let mut boxed = Box::pin(res);
emu.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall);
boxed
}
extern "C" fn fake_syscall(
mut self: Pin<&mut Self>,
sys_num: i32,
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
a3: GuestAddr,
_a4: GuestAddr,
_a5: GuestAddr,
_a6: GuestAddr,
_a7: GuestAddr,
) -> SyscallHookResult {
if sys_num == QASAN_FAKESYS_NR {
let mut r = 0;
let emulator = Emulator::get().unwrap();
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
QasanAction::Poison => {
self.poison(
&emulator,
a1,
a2 as usize,
PoisonKind::try_from(a3 as i8).unwrap().into(),
);
}
QasanAction::UserPoison => {
self.poison(&emulator, a1, a2 as usize, PoisonKind::User.into());
}
QasanAction::UnPoison => {
Self::unpoison(&emulator, a1, a2 as usize);
}
QasanAction::IsPoison => {
if Self::is_invalid_access(&emulator, a1, a2 as usize) {
r = 1;
}
}
QasanAction::Alloc => {
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
self.allocation(pc, a1, a2);
}
QasanAction::Dealloc => {
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
self.deallocation(&emulator, pc, a1);
}
_ => (),
}
SyscallHookResult::new(Some(r))
} else {
SyscallHookResult::new(None)
}
}
fn set_error_callback(&mut self, error_callback: AsanErrorCallback) {
self.error_callback = Some(error_callback);
}
fn set_snapshot_shadow(&mut self, snapshot_shadow: bool) {
self.snapshot_shadow = snapshot_shadow;
}
#[inline]
#[must_use]
pub fn is_invalid_access_1(emu: &Emulator, addr: GuestAddr) -> bool {
@ -357,7 +439,7 @@ impl AsanGiovese {
}
#[inline]
fn unpoison_page(emu: &Emulator, page: GuestAddr) {
pub 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);
@ -375,30 +457,6 @@ impl AsanGiovese {
}
}
#[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_or_crash(&mut self, emu: &Emulator, pc: GuestAddr, error: AsanError) {
if let Some(mut cb) = self.error_callback.take() {
(cb)(self, emu, pc, error);
@ -529,6 +587,15 @@ impl AsanGiovese {
}
}
pub fn allocation(&mut self, pc: GuestAddr, start: GuestAddr, end: GuestAddr) {
self.alloc_remove(start, end);
self.alloc_insert(pc, start, end);
}
pub fn deallocation(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
self.alloc_free(emulator, pc, addr);
}
pub fn snapshot(&mut self, emu: &Emulator) {
if self.snapshot_shadow {
let set = self.dirty_shadow.lock().unwrap();
@ -599,7 +666,7 @@ static mut ASAN_INITED: bool = false;
pub fn init_with_asan(
args: &mut Vec<String>,
env: &mut [(String, String)],
) -> Result<Emulator, EmuError> {
) -> Result<(Emulator, Pin<Box<AsanGiovese>>), EmuError> {
let current = env::current_exe().unwrap();
let asan_lib = fs::canonicalize(current)
.unwrap()
@ -644,7 +711,11 @@ pub fn init_with_asan(
AsanGiovese::map_shadow();
ASAN_INITED = true;
}
Emulator::new(args, env)
let emu = Emulator::new(args, env)?;
let rt = AsanGiovese::new(&emu);
Ok((emu, rt))
}
pub enum QemuAsanOptions {
@ -661,13 +732,26 @@ pub struct QemuAsanHelper {
enabled: bool,
detect_leaks: bool,
empty: bool,
rt: AsanGiovese,
rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter,
}
impl QemuAsanHelper {
#[must_use]
pub fn new(filter: QemuInstrumentationFilter, options: QemuAsanOptions) -> Self {
pub fn default(rt: Pin<Box<AsanGiovese>>) -> Self {
Self::new(
rt,
QemuInstrumentationFilter::None,
QemuAsanOptions::Snapshot,
)
}
#[must_use]
pub fn new(
mut rt: Pin<Box<AsanGiovese>>,
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),
@ -675,17 +759,19 @@ impl QemuAsanHelper {
QemuAsanOptions::DetectLeaks => (false, true),
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
};
rt.set_snapshot_shadow(snapshot);
Self {
enabled: true,
detect_leaks,
empty: true,
rt: AsanGiovese::new(snapshot),
rt,
filter,
}
}
#[must_use]
pub fn with_error_callback(
mut rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter,
error_callback: AsanErrorCallback,
options: QemuAsanOptions,
@ -697,18 +783,24 @@ impl QemuAsanHelper {
QemuAsanOptions::DetectLeaks => (false, true),
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
};
rt.set_snapshot_shadow(snapshot);
rt.set_error_callback(error_callback);
Self {
enabled: true,
detect_leaks,
empty: true,
rt: AsanGiovese::with_error_callback(snapshot, error_callback),
rt,
filter,
}
}
#[must_use]
pub fn with_asan_report(filter: QemuInstrumentationFilter, options: QemuAsanOptions) -> Self {
Self::with_error_callback(filter, Box::new(asan_report), options)
pub fn with_asan_report(
rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter,
options: QemuAsanOptions,
) -> Self {
Self::with_error_callback(rt, filter, Box::new(asan_report), options)
}
#[must_use]
@ -726,12 +818,11 @@ impl QemuAsanHelper {
}
pub fn alloc(&mut self, pc: GuestAddr, start: GuestAddr, end: GuestAddr) {
self.rt.alloc_remove(start, end);
self.rt.alloc_insert(pc, start, end);
self.rt.allocation(pc, start, end);
}
pub fn dealloc(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
self.rt.alloc_free(emulator, pc, addr);
self.rt.deallocation(emulator, pc, addr);
}
#[allow(clippy::unused_self)]
@ -830,12 +921,6 @@ impl QemuAsanHelper {
}
}
impl Default for QemuAsanHelper {
fn default() -> Self {
Self::new(QemuInstrumentationFilter::None, QemuAsanOptions::Snapshot)
}
}
impl HasInstrumentationFilter for QemuAsanHelper {
fn filter(&self) -> &QemuInstrumentationFilter {
&self.filter
@ -852,37 +937,37 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.syscalls(qasan_fake_syscall::<QT, S>);
hooks.syscalls(Hook::Function(qasan_fake_syscall::<QT, S>));
if self.rt.error_callback.is_some() {
hooks.crash(oncrash_asan::<QT, S>);
hooks.crash_function(oncrash_asan::<QT, S>);
}
}
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.reads(
Some(gen_readwrite_asan::<QT, S>),
Some(trace_read1_asan::<QT, S>),
Some(trace_read2_asan::<QT, S>),
Some(trace_read4_asan::<QT, S>),
Some(trace_read8_asan::<QT, S>),
Some(trace_read_n_asan::<QT, S>),
Hook::Function(gen_readwrite_asan::<QT, S>),
Hook::Function(trace_read1_asan::<QT, S>),
Hook::Function(trace_read2_asan::<QT, S>),
Hook::Function(trace_read4_asan::<QT, S>),
Hook::Function(trace_read8_asan::<QT, S>),
Hook::Function(trace_read_n_asan::<QT, S>),
);
hooks.writes(
Some(gen_readwrite_asan::<QT, S>),
Some(trace_write1_asan::<QT, S>),
Some(trace_write2_asan::<QT, S>),
Some(trace_write4_asan::<QT, S>),
Some(trace_write8_asan::<QT, S>),
Some(trace_write_n_asan::<QT, S>),
Hook::Function(gen_readwrite_asan::<QT, S>),
Hook::Function(trace_write1_asan::<QT, S>),
Hook::Function(trace_write2_asan::<QT, S>),
Hook::Function(trace_write4_asan::<QT, S>),
Hook::Function(trace_write8_asan::<QT, S>),
Hook::Function(trace_write_n_asan::<QT, S>),
);
}
@ -908,7 +993,7 @@ where
}
}
pub fn oncrash_asan<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, target_sig: i32)
pub fn oncrash_asan<QT, S>(hooks: &mut QemuHooks<QT, S>, target_sig: i32)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
@ -920,7 +1005,7 @@ where
}
pub fn gen_readwrite_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_info: MemAccessInfo,
@ -938,7 +1023,7 @@ where
}
pub fn trace_read1_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -952,7 +1037,7 @@ pub fn trace_read1_asan<QT, S>(
}
pub fn trace_read2_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -966,7 +1051,7 @@ pub fn trace_read2_asan<QT, S>(
}
pub fn trace_read4_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -980,7 +1065,7 @@ pub fn trace_read4_asan<QT, S>(
}
pub fn trace_read8_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -994,7 +1079,7 @@ pub fn trace_read8_asan<QT, S>(
}
pub fn trace_read_n_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -1009,7 +1094,7 @@ pub fn trace_read_n_asan<QT, S>(
}
pub fn trace_write1_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -1023,7 +1108,7 @@ pub fn trace_write1_asan<QT, S>(
}
pub fn trace_write2_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -1037,7 +1122,7 @@ pub fn trace_write2_asan<QT, S>(
}
pub fn trace_write4_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -1051,7 +1136,7 @@ pub fn trace_write4_asan<QT, S>(
}
pub fn trace_write8_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -1065,7 +1150,7 @@ pub fn trace_write8_asan<QT, S>(
}
pub fn trace_write_n_asan<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
@ -1081,17 +1166,17 @@ pub fn trace_write_n_asan<QT, S>(
#[allow(clippy::too_many_arguments)]
pub fn qasan_fake_syscall<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
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,
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
_a3: GuestAddr,
_a4: GuestAddr,
_a5: GuestAddr,
_a6: GuestAddr,
_a7: GuestAddr,
) -> SyscallHookResult
where
S: UsesInput,
@ -1100,42 +1185,14 @@ where
if sys_num == QASAN_FAKESYS_NR {
let emulator = hooks.emulator().clone();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let mut r = 0;
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
QasanAction::CheckLoad => {
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
h.read_n(&emulator, pc, a1 as GuestAddr, a2 as usize);
h.read_n(&emulator, pc, a1, a2 as usize);
}
QasanAction::CheckStore => {
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
h.write_n(&emulator, pc, 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 => {
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
h.alloc(pc, a1 as GuestAddr, a2 as GuestAddr);
}
QasanAction::Dealloc => {
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
h.dealloc(&emulator, pc, a1 as GuestAddr);
h.write_n(&emulator, pc, a1, a2 as usize);
}
QasanAction::Enable => {
h.set_enabled(true);
@ -1146,8 +1203,9 @@ where
QasanAction::SwapState => {
h.set_enabled(!h.enabled());
}
_ => (),
}
SyscallHookResult::new(Some(r))
SyscallHookResult::new(Some(0))
} else {
SyscallHookResult::new(None)
}

View File

@ -13,14 +13,14 @@ use crate::{
capstone,
emu::{ArchExtras, Emulator},
helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks,
hooks::{Hook, QemuHooks},
GuestAddr,
};
pub trait CallTraceCollector: 'static + Debug {
fn on_call<QT, S>(
&mut self,
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
@ -30,7 +30,7 @@ pub trait CallTraceCollector: 'static + Debug {
fn on_ret<QT, S>(
&mut self,
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
state: Option<&mut S>,
pc: GuestAddr,
ret_addr: GuestAddr,
@ -61,7 +61,7 @@ pub trait CallTraceCollector: 'static + Debug {
pub trait CallTraceCollectorTuple: 'static + MatchFirstType + Debug {
fn on_call_all<QT, S>(
&mut self,
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
@ -71,7 +71,7 @@ pub trait CallTraceCollectorTuple: 'static + MatchFirstType + Debug {
fn on_ret_all<QT, S>(
&mut self,
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
ret_addr: GuestAddr,
@ -97,7 +97,7 @@ pub trait CallTraceCollectorTuple: 'static + MatchFirstType + Debug {
impl CallTraceCollectorTuple for () {
fn on_call_all<QT, S>(
&mut self,
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
_call_len: usize,
@ -109,7 +109,7 @@ impl CallTraceCollectorTuple for () {
fn on_ret_all<QT, S>(
&mut self,
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
_ret_addr: GuestAddr,
@ -145,7 +145,7 @@ where
{
fn on_call_all<QT, S>(
&mut self,
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
mut state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
@ -167,7 +167,7 @@ where
fn on_ret_all<QT, S>(
&mut self,
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
mut state: Option<&mut S>,
pc: GuestAddr,
ret_addr: GuestAddr,
@ -238,7 +238,7 @@ where
self.filter.allowed(addr)
}
fn on_ret<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, state: Option<&mut S>, pc: GuestAddr)
fn on_ret<QT, S>(hooks: &mut QemuHooks<QT, S>, state: Option<&mut S>, pc: GuestAddr)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
@ -267,7 +267,7 @@ where
}
fn gen_blocks_calls<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
@ -319,40 +319,38 @@ where
capstone::InsnGroupType::CS_GRP_CALL => {
let call_len = insn.bytes().len();
// TODO do not use a closure, find a more efficient way to pass call_len
let call_cb = move |hooks: &mut QemuHooks<'_, QT, S>,
state: Option<&mut S>,
pc| {
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
let mut collectors = if let Some(h) =
hooks.helpers_mut().match_first_type_mut::<Self>()
{
h.collectors.take()
} else {
return;
};
if collectors.is_none() {
return; // TODO fix this, it can be None on races ret
}
collectors
.as_mut()
.unwrap()
.on_call_all(hooks, state, pc, call_len);
hooks
.helpers_mut()
.match_first_type_mut::<Self>()
.unwrap()
.collectors = collectors;
};
unsafe {
hooks.instruction_closure(
insn.address() as GuestAddr,
Box::new(call_cb),
false,
);
}
let call_cb = Box::new(
move |hooks: &mut QemuHooks<QT, S>, state: Option<&mut S>, pc| {
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
let mut collectors = if let Some(h) =
hooks.helpers_mut().match_first_type_mut::<Self>()
{
h.collectors.take()
} else {
return;
};
if collectors.is_none() {
return; // TODO fix this, it can be None on races ret
}
collectors
.as_mut()
.unwrap()
.on_call_all(hooks, state, pc, call_len);
hooks
.helpers_mut()
.match_first_type_mut::<Self>()
.unwrap()
.collectors = collectors;
},
);
hooks.instruction_closure(insn.address() as GuestAddr, call_cb, false);
}
capstone::InsnGroupType::CS_GRP_RET => {
hooks.instruction(insn.address() as GuestAddr, Self::on_ret, false);
hooks.instruction_function(
insn.address() as GuestAddr,
Self::on_ret,
false,
);
break 'disasm;
}
capstone::InsnGroupType::CS_GRP_INVALID
@ -400,11 +398,15 @@ where
S: UsesInput,
T: CallTraceCollectorTuple,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.blocks(Some(Self::gen_blocks_calls::<QT, S>), None, None);
hooks.blocks(
Hook::Function(Self::gen_blocks_calls::<QT, S>),
Hook::Empty,
Hook::Empty,
);
}
fn pre_exec(&mut self, emulator: &Emulator, input: &S::Input) {
@ -468,7 +470,7 @@ impl CallTraceCollector for OnCrashBacktraceCollector {
#[allow(clippy::unnecessary_cast)]
fn on_call<QT, S>(
&mut self,
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
@ -482,7 +484,7 @@ impl CallTraceCollector for OnCrashBacktraceCollector {
#[allow(clippy::unnecessary_cast)]
fn on_ret<QT, S>(
&mut self,
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
ret_addr: GuestAddr,
@ -557,7 +559,7 @@ impl CallTraceCollector for FullBacktraceCollector {
#[allow(clippy::unnecessary_cast)]
fn on_call<QT, S>(
&mut self,
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
@ -574,7 +576,7 @@ impl CallTraceCollector for FullBacktraceCollector {
#[allow(clippy::unnecessary_cast)]
fn on_ret<QT, S>(
&mut self,
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
ret_addr: GuestAddr,

View File

@ -20,7 +20,7 @@ use crate::{
helper::{
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter,
},
hooks::QemuHooks,
hooks::{Hook, QemuHooks},
GuestAddr,
};
@ -83,16 +83,16 @@ impl<S> QemuHelper<S> for QemuCmpLogHelper
where
S: UsesInput + HasMetadata,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.cmps_raw(
Some(gen_unique_cmp_ids::<QT, S>),
Some(trace_cmp1_cmplog),
Some(trace_cmp2_cmplog),
Some(trace_cmp4_cmplog),
Some(trace_cmp8_cmplog),
hooks.cmps(
Hook::Function(gen_unique_cmp_ids::<QT, S>),
Hook::Raw(trace_cmp1_cmplog),
Hook::Raw(trace_cmp2_cmplog),
Hook::Raw(trace_cmp4_cmplog),
Hook::Raw(trace_cmp8_cmplog),
);
}
}
@ -127,22 +127,22 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.cmps_raw(
Some(gen_hashed_cmp_ids::<QT, S>),
Some(trace_cmp1_cmplog),
Some(trace_cmp2_cmplog),
Some(trace_cmp4_cmplog),
Some(trace_cmp8_cmplog),
hooks.cmps(
Hook::Function(gen_hashed_cmp_ids::<QT, S>),
Hook::Raw(trace_cmp1_cmplog),
Hook::Raw(trace_cmp2_cmplog),
Hook::Raw(trace_cmp4_cmplog),
Hook::Raw(trace_cmp8_cmplog),
);
}
}
pub fn gen_unique_cmp_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
@ -174,7 +174,7 @@ where
}
pub fn gen_hashed_cmp_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
@ -192,25 +192,25 @@ where
Some(hash_me(pc.into()) & (CMPLOG_MAP_W as u64 - 1))
}
pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8, _data: u64) {
pub extern "C" fn trace_cmp1_cmplog(_: *const (), id: u64, v0: u8, v1: u8) {
unsafe {
__libafl_targets_cmplog_instructions(id as usize, 1, u64::from(v0), u64::from(v1));
}
}
pub extern "C" fn trace_cmp2_cmplog(id: u64, v0: u16, v1: u16, _data: u64) {
pub extern "C" fn trace_cmp2_cmplog(_: *const (), id: u64, v0: u16, v1: u16) {
unsafe {
__libafl_targets_cmplog_instructions(id as usize, 2, u64::from(v0), u64::from(v1));
}
}
pub extern "C" fn trace_cmp4_cmplog(id: u64, v0: u32, v1: u32, _data: u64) {
pub extern "C" fn trace_cmp4_cmplog(_: *const (), id: u64, v0: u32, v1: u32) {
unsafe {
__libafl_targets_cmplog_instructions(id as usize, 4, u64::from(v0), u64::from(v1));
}
}
pub extern "C" fn trace_cmp8_cmplog(id: u64, v0: u64, v1: u64, _data: u64) {
pub extern "C" fn trace_cmp8_cmplog(_: *const (), id: u64, v0: u64, v1: u64) {
unsafe {
__libafl_targets_cmplog_instructions(id as usize, 8, v0, v1);
}
@ -238,7 +238,7 @@ impl QemuCmpLogRoutinesHelper {
self.filter.allowed(addr)
}
extern "C" fn on_call(_pc: GuestAddr, k: u64) {
extern "C" fn on_call(k: u64, _pc: GuestAddr) {
unsafe {
if CMPLOG_ENABLED == 0 {
return;
@ -266,7 +266,7 @@ impl QemuCmpLogRoutinesHelper {
}
fn gen_blocks_calls<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
@ -317,7 +317,7 @@ impl QemuCmpLogRoutinesHelper {
match u32::from(detail.0) {
capstone::InsnGroupType::CS_GRP_CALL => {
let k = (hash_me(pc.into())) & (CMPLOG_MAP_W as u64 - 1);
emu.set_hook(insn.address() as GuestAddr, Self::on_call, k, false);
emu.set_hook(k, insn.address() as GuestAddr, Self::on_call, false);
}
capstone::InsnGroupType::CS_GRP_RET
| capstone::InsnGroupType::CS_GRP_INVALID
@ -363,10 +363,14 @@ impl<S> QemuHelper<S> for QemuCmpLogRoutinesHelper
where
S: UsesInput,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.blocks(Some(Self::gen_blocks_calls::<QT, S>), None, None);
hooks.blocks(
Hook::Function(Self::gen_blocks_calls::<QT, S>),
Hook::Empty,
Hook::Empty,
);
}
}

View File

@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
use crate::{
emu::{GuestAddr, GuestUsize},
helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks,
hooks::{Hook, QemuHooks},
Emulator,
};
@ -89,14 +89,14 @@ impl<S> QemuHelper<S> for QemuDrCovHelper
where
S: UsesInput + HasMetadata,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.blocks(
Some(gen_unique_block_ids::<QT, S>),
Some(gen_block_lengths::<QT, S>),
Some(exec_trace_block::<QT, S>),
Hook::Function(gen_unique_block_ids::<QT, S>),
Hook::Function(gen_block_lengths::<QT, S>),
Hook::Function(exec_trace_block::<QT, S>),
);
}
@ -192,7 +192,7 @@ where
}
pub fn gen_unique_block_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
@ -247,7 +247,7 @@ where
}
pub fn gen_block_lengths<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
block_length: GuestUsize,
@ -271,7 +271,7 @@ pub fn gen_block_lengths<QT, S>(
.insert(pc, block_length);
}
pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, id: u64)
pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<QT, S>, _state: Option<&mut S>, id: u64)
where
S: HasMetadata,
S: UsesInput,

View File

@ -13,7 +13,7 @@ use crate::{
helper::{
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter,
},
hooks::QemuHooks,
hooks::{Hook, QemuHooks},
};
#[cfg_attr(
@ -87,17 +87,20 @@ impl<S> QemuHelper<S> for QemuEdgeCoverageHelper
where
S: UsesInput + HasMetadata,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
if self.use_hitcounts {
hooks.edges_raw(
Some(gen_unique_edge_ids::<QT, S>),
Some(trace_edge_hitcount),
hooks.edges(
Hook::Function(gen_unique_edge_ids::<QT, S>),
Hook::Raw(trace_edge_hitcount),
);
} else {
hooks.edges_raw(Some(gen_unique_edge_ids::<QT, S>), Some(trace_edge_single));
hooks.edges(
Hook::Function(gen_unique_edge_ids::<QT, S>),
Hook::Raw(trace_edge_single),
);
}
}
}
@ -156,19 +159,19 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
if self.use_hitcounts {
hooks.edges_raw(
Some(gen_hashed_edge_ids::<QT, S>),
Some(trace_edge_hitcount_ptr),
hooks.edges(
Hook::Function(gen_hashed_edge_ids::<QT, S>),
Hook::Raw(trace_edge_hitcount_ptr),
);
} else {
hooks.edges_raw(
Some(gen_hashed_edge_ids::<QT, S>),
Some(trace_edge_single_ptr),
hooks.edges(
Hook::Function(gen_hashed_edge_ids::<QT, S>),
Hook::Raw(trace_edge_single_ptr),
);
}
}
@ -226,21 +229,21 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
if self.use_hitcounts {
hooks.blocks_raw(
Some(gen_hashed_block_ids::<QT, S>),
None,
Some(trace_block_transition_hitcount),
hooks.blocks(
Hook::Function(gen_hashed_block_ids::<QT, S>),
Hook::Empty,
Hook::Raw(trace_block_transition_hitcount),
);
} else {
hooks.blocks_raw(
Some(gen_hashed_block_ids::<QT, S>),
None,
Some(trace_block_transition_single),
hooks.blocks(
Hook::Function(gen_hashed_block_ids::<QT, S>),
Hook::Empty,
Hook::Raw(trace_block_transition_single),
);
}
}
@ -249,7 +252,7 @@ where
thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0));
pub fn gen_unique_edge_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
@ -296,20 +299,20 @@ where
}
}
pub extern "C" fn trace_edge_hitcount(id: u64, _data: u64) {
pub extern "C" fn trace_edge_hitcount(_: *const (), id: u64) {
unsafe {
EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1);
}
}
pub extern "C" fn trace_edge_single(id: u64, _data: u64) {
pub extern "C" fn trace_edge_single(_: *const (), id: u64) {
unsafe {
EDGES_MAP[id as usize] = 1;
}
}
pub fn gen_hashed_edge_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
@ -331,14 +334,14 @@ where
Some((hash_me(src as u64) ^ hash_me(dest as u64)) & (unsafe { EDGES_MAP_PTR_NUM } as u64 - 1))
}
pub extern "C" fn trace_edge_hitcount_ptr(id: u64, _data: u64) {
pub extern "C" fn trace_edge_hitcount_ptr(_: *const (), id: u64) {
unsafe {
let ptr = EDGES_MAP_PTR.add(id as usize);
*ptr = (*ptr).wrapping_add(1);
}
}
pub extern "C" fn trace_edge_single_ptr(id: u64, _data: u64) {
pub extern "C" fn trace_edge_single_ptr(_: *const (), id: u64) {
unsafe {
let ptr = EDGES_MAP_PTR.add(id as usize);
*ptr = 1;
@ -347,7 +350,7 @@ pub extern "C" fn trace_edge_single_ptr(id: u64, _data: u64) {
/*
pub fn gen_addr_block_ids<QT, S>(
_hooks: &mut QemuHooks<'_, QT, S>,
_hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
@ -362,7 +365,7 @@ where
*/
pub fn gen_hashed_block_ids<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
@ -383,7 +386,7 @@ where
Some(hash_me(pc as u64))
}
pub extern "C" fn trace_block_transition_hitcount(id: u64, _data: u64) {
pub extern "C" fn trace_block_transition_hitcount(_: *const (), id: u64) {
unsafe {
PREV_LOC.with(|prev_loc| {
let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_PTR_NUM - 1);
@ -394,7 +397,7 @@ pub extern "C" fn trace_block_transition_hitcount(id: u64, _data: u64) {
}
}
pub extern "C" fn trace_block_transition_single(id: u64, _data: u64) {
pub extern "C" fn trace_block_transition_single(_: *const (), id: u64) {
unsafe {
PREV_LOC.with(|prev_loc| {
let x = ((*prev_loc.get() ^ id) as usize) & (EDGES_MAP_PTR_NUM - 1);

View File

@ -14,11 +14,7 @@ use std::{
ffi::{CStr, CString},
ptr::null_mut,
};
use std::{
slice::from_raw_parts,
str::from_utf8_unchecked,
sync::{Mutex, OnceLock},
};
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
#[cfg(emulation_mode = "usermode")]
use libc::c_int;
@ -224,11 +220,12 @@ pub enum VerifyAccess {
Write = libc::PROT_READ | libc::PROT_WRITE,
}
// syshook_ret
#[repr(C)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "python", derive(FromPyObject))]
pub struct SyscallHookResult {
pub retval: u64,
pub retval: GuestAddr,
pub skip_syscall: bool,
}
@ -237,7 +234,7 @@ pub struct SyscallHookResult {
impl SyscallHookResult {
#[new]
#[must_use]
pub fn new(value: Option<u64>) -> Self {
pub fn new(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
@ -254,7 +251,7 @@ impl SyscallHookResult {
#[cfg(not(feature = "python"))]
impl SyscallHookResult {
#[must_use]
pub fn new(value: Option<u64>) -> Self {
pub fn new(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
@ -340,13 +337,6 @@ extern "C" {
static guest_base: usize;
static mut mmap_next_start: GuestAddr;
static mut libafl_on_thread_hook: unsafe extern "C" fn(u32);
static mut libafl_pre_syscall_hook:
unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult;
static mut libafl_post_syscall_hook:
unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64;
static mut libafl_dump_core_hook: unsafe extern "C" fn(i32);
static mut libafl_force_dfl: i32;
}
@ -370,6 +360,7 @@ extern "C" fn qemu_cleanup_atexit() {
}
}
// TODO rely completely on libafl_qemu_sys
extern "C" {
//static libafl_page_size: GuestUsize;
fn libafl_page_from_addr(addr: GuestAddr) -> GuestAddr;
@ -395,87 +386,10 @@ extern "C" {
fn libafl_flush_jit();
fn libafl_qemu_trigger_breakpoint(cpu: CPUStatePtr);
fn libafl_qemu_set_hook(
addr: GuestAddr,
callback: extern "C" fn(GuestAddr, u64),
data: u64,
invalidate_block: i32,
) -> usize;
// fn libafl_qemu_remove_hook(num: usize, invalidate_block: i32) -> i32;
fn libafl_qemu_remove_hooks_at(addr: GuestAddr, invalidate_block: i32) -> usize;
fn strlen(s: *const u8) -> usize;
// void libafl_add_edge_hook(uint64_t (*gen)(target_ulong src, target_ulong dst), void (*exec)(uint64_t id));
fn libafl_add_edge_hook(
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(u64, u64)>,
data: u64,
);
// void libafl_add_block_hook(uint64_t (*gen)(target_ulong pc), void (*exec)(uint64_t id));
fn libafl_add_block_hook(
gen: Option<extern "C" fn(GuestAddr, u64) -> u64>,
post_gen: Option<extern "C" fn(GuestAddr, GuestUsize, u64)>,
exec: Option<extern "C" fn(u64, u64)>,
data: u64,
);
// void libafl_add_read_hook(uint64_t (*gen)(target_ulong pc, size_t size, uint64_t data),
// void (*exec1)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec2)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec4)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec8)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data),
// uint64_t data);
fn libafl_add_read_hook(
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
data: u64,
);
// void libafl_add_write_hook(uint64_t (*gen)(target_ulong pc, size_t size, uint64_t data),
// void (*exec1)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec2)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec4)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec8)(uint64_t id, target_ulong addr, uint64_t data),
// void (*exec_n)(uint64_t id, target_ulong addr, size_t size, uint64_t data),
// uint64_t data);
fn libafl_add_write_hook(
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
data: u64,
);
// void libafl_add_cmp_hook(uint64_t (*gen)(target_ulong pc, size_t size, uint64_t data),
// void (*exec1)(uint64_t id, uint8_t v0, uint8_t v1, uint64_t data),
// void (*exec2)(uint64_t id, uint16_t v0, uint16_t v1, uint64_t data),
// void (*exec4)(uint64_t id, uint32_t v0, uint32_t v1, uint64_t data),
// void (*exec8)(uint64_t id, uint64_t v0, uint64_t v1, uint64_t data),
// uint64_t data);
fn libafl_add_cmp_hook(
gen: Option<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, u8, u8, u64)>,
exec2: Option<extern "C" fn(u64, u16, u16, u64)>,
exec4: Option<extern "C" fn(u64, u32, u32, u64)>,
exec8: Option<extern "C" fn(u64, u64, u64, u64)>,
data: u64,
);
// void libafl_add_backdoor_hook(void (*exec)(uint64_t id, uint64_t data),
// uint64_t data)
fn libafl_add_backdoor_hook(exec: extern "C" fn(GuestAddr, u64), data: u64);
fn libafl_qemu_add_gdb_cmd(
callback: extern "C" fn(*const u8, usize, *const ()) -> i32,
callback: extern "C" fn(*const (), *const u8, usize) -> i32,
data: *const (),
);
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
@ -550,7 +464,7 @@ pub(crate) struct FatPtr(pub *const c_void, pub *const c_void);
static mut GDB_COMMANDS: Vec<FatPtr> = vec![];
extern "C" fn gdb_cmd(buf: *const u8, len: usize, data: *const ()) -> i32 {
extern "C" fn gdb_cmd(data: *const (), buf: *const u8, len: usize) -> i32 {
unsafe {
let closure = &mut *(data as *mut Box<dyn for<'r> FnMut(&Emulator, &'r str) -> bool>);
let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len));
@ -845,11 +759,72 @@ impl CPU {
}
}
static EMULATOR_IS_INITIALIZED: OnceLock<Mutex<bool>> = OnceLock::new();
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct HookId(pub(crate) usize);
#[derive(Clone, Debug)]
pub struct Emulator {
_private: (),
use std::pin::Pin;
#[derive(Debug)]
pub struct HookData(u64);
impl<T> From<Pin<&mut T>> for HookData {
fn from(value: Pin<&mut T>) -> Self {
unsafe { HookData(core::mem::transmute(value)) }
}
}
impl<T> From<Pin<&T>> for HookData {
fn from(value: Pin<&T>) -> Self {
unsafe { HookData(core::mem::transmute(value)) }
}
}
impl<T> From<&'static mut T> for HookData {
fn from(value: &'static mut T) -> Self {
unsafe { HookData(core::mem::transmute(value)) }
}
}
impl<T> From<&'static T> for HookData {
fn from(value: &'static T) -> Self {
unsafe { HookData(core::mem::transmute(value)) }
}
}
impl<T> From<*mut T> for HookData {
fn from(value: *mut T) -> Self {
unsafe { HookData(core::mem::transmute(value)) }
}
}
impl<T> From<*const T> for HookData {
fn from(value: *const T) -> Self {
unsafe { HookData(core::mem::transmute(value)) }
}
}
impl From<u64> for HookData {
fn from(value: u64) -> Self {
HookData(value)
}
}
impl From<u32> for HookData {
fn from(value: u32) -> Self {
HookData(value as u64)
}
}
impl From<u16> for HookData {
fn from(value: u16) -> Self {
HookData(value as u64)
}
}
impl From<u8> for HookData {
fn from(value: u8) -> Self {
HookData(value as u64)
}
}
#[derive(Debug)]
@ -940,19 +915,17 @@ impl From<EmuError> for libafl::Error {
}
}
static mut EMULATOR_IS_INITIALIZED: bool = false;
#[derive(Clone, Debug)]
pub struct Emulator {
_private: (),
}
#[allow(clippy::unused_self)]
impl Emulator {
#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new(args: &[String], env: &[(String, String)]) -> Result<Emulator, EmuError> {
let mut is_initialized = EMULATOR_IS_INITIALIZED
.get_or_init(|| Mutex::new(false))
.lock()
.unwrap();
if *is_initialized {
return Err(EmuError::MultipleInstances);
}
if args.is_empty() {
return Err(EmuError::EmptyArgs);
}
@ -961,6 +934,14 @@ impl Emulator {
if i32::try_from(argc).is_err() {
return Err(EmuError::TooManyArgs(argc));
}
unsafe {
if EMULATOR_IS_INITIALIZED {
return Err(EmuError::MultipleInstances);
}
EMULATOR_IS_INITIALIZED = true;
}
#[allow(clippy::cast_possible_wrap)]
let argc = argc as i32;
@ -985,11 +966,21 @@ impl Emulator {
libc::atexit(qemu_cleanup_atexit);
libafl_qemu_sys::syx_snapshot_init();
}
*is_initialized = true;
}
Ok(Emulator { _private: () })
}
#[must_use]
pub fn get() -> Option<Self> {
unsafe {
if EMULATOR_IS_INITIALIZED {
Some(Self::new_empty())
} else {
None
}
}
}
#[must_use]
pub(crate) fn new_empty() -> Emulator {
Emulator { _private: () }
@ -1141,22 +1132,6 @@ impl Emulator {
libafl_force_dfl = 1;
}
}
pub fn set_hook(
&self,
addr: GuestAddr,
callback: extern "C" fn(GuestAddr, u64),
data: u64,
invalidate_block: bool,
) -> usize {
unsafe { libafl_qemu_set_hook(addr.into(), callback, data, i32::from(invalidate_block)) }
}
#[must_use]
pub fn remove_hook(&self, addr: GuestAddr, invalidate_block: bool) -> usize {
unsafe { libafl_qemu_remove_hooks_at(addr.into(), i32::from(invalidate_block)) }
}
/// This function will run the emulator until the next breakpoint, or until finish.
/// # Safety
///
@ -1282,72 +1257,250 @@ impl Emulator {
}
}
pub fn add_edge_hooks(
// TODO set T lifetime to be like Emulator
pub fn set_hook<T: Into<HookData>>(
&self,
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
exec: Option<extern "C" fn(u64, u64)>,
data: u64,
) {
unsafe { libafl_add_edge_hook(gen, exec, data) }
data: T,
addr: GuestAddr,
callback: extern "C" fn(T, GuestAddr),
invalidate_block: bool,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, GuestAddr) = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_qemu_set_hook(
addr.into(),
Some(callback),
data,
i32::from(invalidate_block),
);
HookId(num)
}
}
pub fn add_block_hooks(
&self,
gen: Option<extern "C" fn(GuestAddr, u64) -> u64>,
post_gen: Option<extern "C" fn(GuestAddr, GuestUsize, u64)>,
exec: Option<extern "C" fn(u64, u64)>,
data: u64,
) {
unsafe { libafl_add_block_hook(gen, post_gen, exec, data) }
#[must_use]
pub fn remove_hook(&self, id: HookId, invalidate_block: bool) -> bool {
unsafe { libafl_qemu_sys::libafl_qemu_remove_hook(id.0, i32::from(invalidate_block)) != 0 }
}
pub fn add_read_hooks(
#[must_use]
pub fn remove_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize {
unsafe {
libafl_qemu_sys::libafl_qemu_remove_hooks_at(addr.into(), i32::from(invalidate_block))
}
}
pub fn add_edge_hooks<T: Into<HookData>>(
&self,
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
data: u64,
) {
unsafe { libafl_add_read_hook(gen, exec1, exec2, exec4, exec8, exec_n, data) }
data: T,
gen: Option<extern "C" fn(T, GuestAddr, GuestAddr) -> u64>,
exec: Option<extern "C" fn(T, u64)>,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<extern "C" fn(u64, GuestAddr, GuestAddr) -> u64> =
core::mem::transmute(gen);
let exec: Option<extern "C" fn(u64, u64)> = core::mem::transmute(exec);
let num = libafl_qemu_sys::libafl_add_edge_hook(gen, exec, data);
HookId(num)
}
}
pub fn add_block_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<extern "C" fn(T, GuestAddr) -> u64>,
post_gen: Option<extern "C" fn(T, GuestAddr, GuestUsize)>,
exec: Option<extern "C" fn(T, u64)>,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<extern "C" fn(u64, GuestAddr) -> u64> = core::mem::transmute(gen);
let post_gen: Option<extern "C" fn(u64, GuestAddr, GuestUsize)> =
core::mem::transmute(post_gen);
let exec: Option<extern "C" fn(u64, u64)> = core::mem::transmute(exec);
let num = libafl_qemu_sys::libafl_add_block_hook(gen, post_gen, exec, data);
HookId(num)
}
}
pub fn add_read_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<extern "C" fn(T, GuestAddr, MemAccessInfo) -> u64>,
exec1: Option<extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<extern "C" fn(T, u64, GuestAddr, usize)>,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<extern "C" fn(u64, GuestAddr, libafl_qemu_sys::MemOpIdx) -> u64> =
core::mem::transmute(gen);
let exec1: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec1);
let exec2: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec2);
let exec4: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec4);
let exec8: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec8);
let exec_n: Option<extern "C" fn(u64, u64, GuestAddr, usize)> =
core::mem::transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_read_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,
);
HookId(num)
}
}
// TODO add MemOp info
pub fn add_write_hooks(
pub fn add_write_hooks<T: Into<HookData>>(
&self,
gen: Option<extern "C" fn(GuestAddr, MemAccessInfo, u64) -> u64>,
exec1: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec2: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec4: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec8: Option<extern "C" fn(u64, GuestAddr, u64)>,
exec_n: Option<extern "C" fn(u64, GuestAddr, usize, u64)>,
data: u64,
) {
unsafe { libafl_add_write_hook(gen, exec1, exec2, exec4, exec8, exec_n, data) }
data: T,
gen: Option<extern "C" fn(T, GuestAddr, MemAccessInfo) -> u64>,
exec1: Option<extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<extern "C" fn(T, u64, GuestAddr, usize)>,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<extern "C" fn(u64, GuestAddr, libafl_qemu_sys::MemOpIdx) -> u64> =
core::mem::transmute(gen);
let exec1: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec1);
let exec2: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec2);
let exec4: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec4);
let exec8: Option<extern "C" fn(u64, u64, GuestAddr)> = core::mem::transmute(exec8);
let exec_n: Option<extern "C" fn(u64, u64, GuestAddr, usize)> =
core::mem::transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_write_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,
);
HookId(num)
}
}
pub fn add_cmp_hooks(
pub fn add_cmp_hooks<T: Into<HookData>>(
&self,
gen: Option<extern "C" fn(GuestAddr, usize, u64) -> u64>,
exec1: Option<extern "C" fn(u64, u8, u8, u64)>,
exec2: Option<extern "C" fn(u64, u16, u16, u64)>,
exec4: Option<extern "C" fn(u64, u32, u32, u64)>,
exec8: Option<extern "C" fn(u64, u64, u64, u64)>,
data: u64,
) {
unsafe { libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data) }
data: T,
gen: Option<extern "C" fn(T, GuestAddr, usize) -> u64>,
exec1: Option<extern "C" fn(T, u64, u8, u8)>,
exec2: Option<extern "C" fn(T, u64, u16, u16)>,
exec4: Option<extern "C" fn(T, u64, u32, u32)>,
exec8: Option<extern "C" fn(T, u64, u64, u64)>,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<extern "C" fn(u64, GuestAddr, usize) -> u64> =
core::mem::transmute(gen);
let exec1: Option<extern "C" fn(u64, u64, u8, u8)> = core::mem::transmute(exec1);
let exec2: Option<extern "C" fn(u64, u64, u16, u16)> = core::mem::transmute(exec2);
let exec4: Option<extern "C" fn(u64, u64, u32, u32)> = core::mem::transmute(exec4);
let exec8: Option<extern "C" fn(u64, u64, u64, u64)> = core::mem::transmute(exec8);
let num = libafl_qemu_sys::libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data);
HookId(num)
}
}
pub fn add_backdoor_hook(&self, exec: extern "C" fn(GuestAddr, u64), data: u64) {
unsafe { libafl_add_backdoor_hook(exec, data) };
pub fn add_backdoor_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, GuestAddr),
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, GuestAddr) = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_backdoor_hook(Some(callback), data);
HookId(num)
}
}
#[cfg(emulation_mode = "usermode")]
pub fn set_on_thread_hook(&self, hook: extern "C" fn(tid: u32)) {
#[allow(clippy::type_complexity)]
pub fn add_pre_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> SyscallHookResult,
) -> HookId {
unsafe {
libafl_on_thread_hook = hook;
let data: u64 = data.into().0;
let callback: extern "C" fn(
u64,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> libafl_qemu_sys::syshook_ret = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
HookId(num)
}
}
#[cfg(emulation_mode = "usermode")]
#[allow(clippy::type_complexity)]
pub fn add_post_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(
u64,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data);
HookId(num)
}
}
#[cfg(emulation_mode = "usermode")]
pub fn add_new_thread_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, tid: u32) -> bool,
) -> HookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, u32) -> bool = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
HookId(num)
}
}
@ -1420,26 +1573,6 @@ impl Emulator {
}
}
#[cfg(emulation_mode = "usermode")]
pub fn set_pre_syscall_hook(
&self,
hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult,
) {
unsafe {
libafl_pre_syscall_hook = hook;
}
}
#[cfg(emulation_mode = "usermode")]
pub fn set_post_syscall_hook(
&self,
hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64,
) {
unsafe {
libafl_post_syscall_hook = hook;
}
}
#[allow(clippy::type_complexity)]
pub fn add_gdb_cmd(&self, callback: Box<dyn FnMut(&Self, &str) -> bool>) {
unsafe {
@ -1519,6 +1652,7 @@ pub mod pybind {
static mut PY_GENERIC_HOOKS: Vec<(GuestAddr, PyObject)> = vec![];
extern "C" fn py_syscall_hook_wrapper(
data: u64,
sys_num: i32,
a0: u64,
a1: u64,
@ -1554,7 +1688,7 @@ pub mod pybind {
)
}
extern "C" fn py_generic_hook_wrapper(_pc: GuestAddr, idx: u64) {
extern "C" fn py_generic_hook_wrapper(idx: u64, _pc: GuestAddr) {
let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 };
Python::with_gil(|py| {
obj.call0(py).expect("Error in the hook");
@ -1678,7 +1812,7 @@ pub mod pybind {
unsafe {
PY_SYSCALL_HOOK = Some(hook);
}
self.emu.set_pre_syscall_hook(py_syscall_hook_wrapper);
self.emu.add_pre_syscall_hook(0u64, py_syscall_hook_wrapper);
}
fn set_hook(&self, addr: GuestAddr, hook: PyObject) {
@ -1686,15 +1820,15 @@ pub mod pybind {
let idx = PY_GENERIC_HOOKS.len();
PY_GENERIC_HOOKS.push((addr, hook));
self.emu
.set_hook(addr, py_generic_hook_wrapper, idx as u64, true);
.set_hook(idx as u64, addr, py_generic_hook_wrapper, true);
}
}
fn remove_hook(&self, addr: GuestAddr) -> usize {
fn remove_hooks_at(&self, addr: GuestAddr) -> usize {
unsafe {
PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
}
self.emu.remove_hook(addr, true)
self.emu.remove_hooks_at(addr, true)
}
}
}

View File

@ -37,7 +37,7 @@ where
QT: QemuHelperTuple<S>,
{
inner: InProcessExecutor<'a, H, OT, S>,
hooks: &'a mut QemuHooks<'a, QT, S>,
hooks: &'a mut QemuHooks<QT, S>,
first_exec: bool,
}
@ -138,7 +138,7 @@ where
QT: QemuHelperTuple<S>,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<'a, QT, S>,
hooks: &'a mut QemuHooks<QT, S>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -186,11 +186,11 @@ where
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<'a, QT, S> {
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, QT, S> {
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.hooks
}
@ -280,7 +280,7 @@ where
SP: ShMemProvider,
{
first_exec: bool,
hooks: &'a mut QemuHooks<'a, QT, S>,
hooks: &'a mut QemuHooks<QT, S>,
inner: InProcessForkExecutor<'a, H, OT, S, SP>,
}
@ -311,7 +311,7 @@ where
SP: ShMemProvider,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<'a, QT, S>,
hooks: &'a mut QemuHooks<QT, S>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -349,11 +349,11 @@ where
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<'a, QT, S> {
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<'a, QT, S> {
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.hooks
}

View File

@ -16,13 +16,13 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = true;
fn init_hooks<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
fn init_hooks<QT>(&self, _hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
}
fn first_exec<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, _hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
@ -48,11 +48,11 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool;
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>;
fn first_exec_all<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec_all<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>;
@ -74,13 +74,13 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks_all<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
fn init_hooks_all<QT>(&self, _hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
}
fn first_exec_all<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
fn first_exec_all<QT>(&self, _hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
@ -108,7 +108,7 @@ where
{
const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
@ -116,7 +116,7 @@ where
self.1.init_hooks_all(hooks);
}
fn first_exec_all<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec_all<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ use crate::SYS_newfstatat;
use crate::{
emu::{Emulator, MmapPerms, SyscallHookResult},
helper::{QemuHelper, QemuHelperTuple},
hooks::QemuHooks,
hooks::{Hook, QemuHooks},
GuestAddr, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap,
SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
};
@ -487,23 +487,23 @@ impl<S> QemuHelper<S> for QemuSnapshotHelper
where
S: UsesInput + HasMetadata,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<'_, QT, S>)
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
hooks.writes(
None,
Some(trace_write1_snapshot::<QT, S>),
Some(trace_write2_snapshot::<QT, S>),
Some(trace_write4_snapshot::<QT, S>),
Some(trace_write8_snapshot::<QT, S>),
Some(trace_write_n_snapshot::<QT, S>),
Hook::Empty,
Hook::Function(trace_write1_snapshot::<QT, S>),
Hook::Function(trace_write2_snapshot::<QT, S>),
Hook::Function(trace_write4_snapshot::<QT, S>),
Hook::Function(trace_write8_snapshot::<QT, S>),
Hook::Function(trace_write_n_snapshot::<QT, S>),
);
if !self.accurate_unmap {
hooks.syscalls(filter_mmap_snapshot::<QT, S>);
hooks.syscalls(Hook::Function(filter_mmap_snapshot::<QT, S>));
}
hooks.after_syscalls(trace_mmap_snapshot::<QT, S>);
hooks.after_syscalls(Hook::Function(trace_mmap_snapshot::<QT, S>));
}
fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
@ -516,7 +516,7 @@ where
}
pub fn trace_write1_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
@ -529,7 +529,7 @@ pub fn trace_write1_snapshot<QT, S>(
}
pub fn trace_write2_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
@ -542,7 +542,7 @@ pub fn trace_write2_snapshot<QT, S>(
}
pub fn trace_write4_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
@ -555,7 +555,7 @@ pub fn trace_write4_snapshot<QT, S>(
}
pub fn trace_write8_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
@ -568,7 +568,7 @@ pub fn trace_write8_snapshot<QT, S>(
}
pub fn trace_write_n_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
@ -584,17 +584,17 @@ pub fn trace_write_n_snapshot<QT, S>(
#[allow(clippy::too_many_arguments)]
#[allow(non_upper_case_globals)]
pub fn filter_mmap_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
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,
a0: GuestAddr,
a1: GuestAddr,
_a2: GuestAddr,
_a3: GuestAddr,
_a4: GuestAddr,
_a5: GuestAddr,
_a6: GuestAddr,
_a7: GuestAddr,
) -> SyscallHookResult
where
S: UsesInput,
@ -612,19 +612,19 @@ where
#[allow(clippy::too_many_arguments)]
#[allow(non_upper_case_globals)]
pub fn trace_mmap_snapshot<QT, S>(
hooks: &mut QemuHooks<'_, QT, S>,
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
result: u64,
result: GuestAddr,
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
_a4: u64,
_a5: u64,
_a6: u64,
_a7: u64,
) -> u64
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
a3: GuestAddr,
_a4: GuestAddr,
_a5: GuestAddr,
_a6: GuestAddr,
_a7: GuestAddr,
) -> GuestAddr
where
S: UsesInput,
QT: QemuHelperTuple<S>,
@ -633,15 +633,15 @@ where
match i64::from(sys_num) {
SYS_read | SYS_pread64 => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a1 as GuestAddr, a2 as usize);
h.access(a1, a2 as usize);
}
SYS_readlinkat => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a2 as GuestAddr, a3 as usize);
h.access(a2, a3 as usize);
}
SYS_futex => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a0 as GuestAddr, a3 as usize);
h.access(a0, a3 as usize);
}
#[cfg(not(any(
cpu_target = "arm",
@ -652,27 +652,27 @@ where
SYS_newfstatat => {
if a2 != 0 {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a2 as GuestAddr, 4096); // stat is not greater than a page
h.access(a2, 4096); // stat is not greater than a page
}
}
#[cfg(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "i386"))]
SYS_fstatat64 => {
if a2 != 0 {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a2 as GuestAddr, 4096); // stat is not greater than a page
h.access(a2, 4096); // stat is not greater than a page
}
}
SYS_statfs | SYS_fstatfs | SYS_fstat => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a1 as GuestAddr, 4096); // stat is not greater than a page
h.access(a1, 4096); // stat is not greater than a page
}
SYS_getrandom => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.access(a0 as GuestAddr, a1 as usize);
h.access(a0, a1 as usize);
}
// mmap syscalls
sys_const => {
if result as GuestAddr == GuestAddr::MAX
if result == GuestAddr::MAX
/* -1 */
{
return result;
@ -684,7 +684,7 @@ where
if sys_const == SYS_mmap2 {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
h.add_mapped(result, a1 as usize, Some(prot));
}
}
@ -692,24 +692,24 @@ where
if sys_const == SYS_mmap {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
h.add_mapped(result, a1 as usize, Some(prot));
}
}
if sys_const == SYS_mremap {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
// TODO get the old permissions from the removed mapping
h.remove_mapped(a0 as GuestAddr, a1 as usize);
h.add_mapped(result as GuestAddr, a2 as usize, None);
h.remove_mapped(a0, a1 as usize);
h.add_mapped(result, a2 as usize, None);
} else if sys_const == SYS_mprotect {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
h.add_mapped(a0 as GuestAddr, a1 as usize, Some(prot));
h.add_mapped(a0, a1 as usize, Some(prot));
}
} else if sys_const == SYS_munmap {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
if !h.accurate_unmap && !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) {
h.remove_mapped(a0 as GuestAddr, a1 as usize);
if !h.accurate_unmap && !h.is_unmap_allowed(a0, a1 as usize) {
h.remove_mapped(a0, a1 as usize);
}
}
}

View File

@ -214,7 +214,7 @@ where
if self.use_cmplog.unwrap_or(false) {
let mut hooks = QemuHooks::new(
emulator,
emulator.clone(),
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
tuple_list!(
QemuEdgeCoverageHelper::default(),
@ -325,8 +325,10 @@ where
}
}
} else {
let mut hooks =
QemuHooks::new(emulator, tuple_list!(QemuEdgeCoverageHelper::default()));
let mut hooks = QemuHooks::new(
emulator.clone(),
tuple_list!(QemuEdgeCoverageHelper::default()),
);
let executor = QemuExecutor::new(
&mut hooks,