libafl_qemu decouple hooks from the executor and QemuForkExecutor (#528)

* QemuHooks

* option state hooks

* QemuForkExecutor

* enforce no side effects in QemuForkExecutor

* child hooks fixes

* fixes

* qemu_launcher

* examples and fixes

* fix sugar

* clippy

* fmt

* no timeout for fuzzbench_fork_qemu

* Update libafl_qemu/src/hooks.rs

Co-authored-by: Alwin Berger <50980804+alwinber@users.noreply.github.com>

* clippy

Co-authored-by: Alwin Berger <50980804+alwinber@users.noreply.github.com>
This commit is contained in:
Andrea Fioraldi 2022-02-15 22:11:24 +01:00 committed by GitHub
parent 86b4ff9c2f
commit a03d733cf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1730 additions and 932 deletions

View File

@ -0,0 +1 @@
libpng-*

View File

@ -0,0 +1,18 @@
[package]
name = "fuzzbench_qemu"
version = "0.7.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"
[features]
default = ["std"]
std = []
[profile.release]
debug = true
[dependencies]
libafl = { path = "../../libafl/" }
libafl_qemu = { path = "../../libafl_qemu/", features = ["x86_64"] }
clap = { version = "3.0", features = ["default"] }
nix = "0.23"

View File

@ -0,0 +1,43 @@
FUZZER_NAME="libpng_harness"
PROJECT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
UNAME := $(shell uname)
PHONY: all
all: fuzzer
libpng-1.6.37:
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar -xvf libpng-1.6.37.tar.xz
target/release/fuzzbench_qemu: src/*
cargo build --release
libpng-1.6.37/.libs/libpng16.a: libpng-1.6.37
cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes
$(MAKE) -C libpng-1.6.37
cc -c $(PROJECT_DIR)/libfuzzer_main.c
# Build the libpng harness
c++ \
$(PROJECT_DIR)/../libfuzzer_libpng/harness.cc \
$(PROJECT_DIR)/libpng-1.6.37/.libs/libpng16.a \
libfuzzer_main.o \
-I$(PROJECT_DIR)/libpng-1.6.37/ \
-o $(FUZZER_NAME) \
-lm -lz
fuzzer: target/release/fuzzbench_qemu libpng-1.6.37/.libs/libpng16.a
clean:
rm ./$(FUZZER_NAME) libfuzzer_main.o
$(MAKE) -C libpng-1.6.37 clean
run: all
cargo run --release -- --libafl-in ../libfuzzer_libpng/corpus --libafl-out ./out ./$(FUZZER_NAME)
short_test: all
rm -rf libafl_unix_shmem_server || true
timeout 10s cargo run --release -- --libafl-in ../libfuzzer_libpng/corpus --libafl-out ./out ./$(FUZZER_NAME) &
test: all
timeout 60s cargo run --release -- --libafl-in ../libfuzzer_libpng/corpus --libafl-out ./out ./$(FUZZER_NAME) &

View File

@ -0,0 +1,11 @@
#include <stdint.h>
#include <stdlib.h>
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
int main() {
char buf [10] = {0};
LLVMFuzzerTestOneInput(buf, 10);
}

View File

@ -0,0 +1,378 @@
//! A singlethreaded QEMU fuzzer that can auto-restart.
use clap::{App, Arg};
use core::cell::RefCell;
#[cfg(unix)]
use nix::{self, unistd::dup};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::{
env,
fs::{self, File, OpenOptions},
io::{self, Write},
path::PathBuf,
process,
};
use libafl::{
bolts::{
current_nanos, current_time,
os::dup2,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::{tuple_list, Merge},
AsMutSlice, AsSlice,
},
corpus::{
Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, PowerQueueCorpusScheduler,
},
events::SimpleRestartingEventManager,
executors::{ExitKind, ShadowExecutor},
feedback_or,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
StdMOptMutator, StdScheduledMutator, Tokens,
},
observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver},
stages::{
calibrate::CalibrationStage,
power::{PowerMutationalStage, PowerSchedule},
ShadowTracingStage, StdMutationalStage,
},
state::{HasCorpus, HasMetadata, StdState},
Error,
};
use libafl_qemu::{
cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper, CMPLOG_MAP_PTR},
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE},
elf::EasyElf,
emu::Emulator,
filter_qemu_args,
hooks::QemuHooks,
MmapPerms, QemuForkExecutor, Regs,
};
/// The fuzzer main
pub fn main() {
// Registry the metadata types used in this fuzzer
// Needed only on no_std
//RegistryBuilder::register::<Tokens>();
let res = match App::new("libafl_qemu_fuzzbench")
.version("0.4.0")
.author("AFLplusplus team")
.about("LibAFL-based fuzzer with QEMU for Fuzzbench")
.arg(
Arg::new("out")
.help("The directory to place finds in ('corpus')")
.long("libafl-out")
.required(true)
.takes_value(true),
)
.arg(
Arg::new("in")
.help("The directory to read initial inputs from ('seeds')")
.long("libafl-in")
.required(true)
.takes_value(true),
)
.arg(
Arg::new("tokens")
.long("libafl-tokens")
.help("A file to read tokens from, to be used during fuzzing")
.takes_value(true),
)
.arg(
Arg::new("logfile")
.long("libafl-logfile")
.help("Duplicates all output to this file")
.default_value("libafl.log"),
)
.try_get_matches_from(filter_qemu_args())
{
Ok(res) => res,
Err(err) => {
println!(
"Syntax: {}, --libafl-in <input> --libafl-out <output>\n{:?}",
env::current_exe()
.unwrap_or_else(|_| "fuzzer".into())
.to_string_lossy(),
err.info,
);
return;
}
};
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
let mut out_dir = PathBuf::from(res.value_of("out").unwrap().to_string());
if fs::create_dir(&out_dir).is_err() {
println!("Out dir at {:?} already exists.", &out_dir);
if !out_dir.is_dir() {
println!("Out dir at {:?} is not a valid directory!", &out_dir);
return;
}
}
let mut crashes = out_dir.clone();
crashes.push("crashes");
out_dir.push("queue");
let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string());
if !in_dir.is_dir() {
println!("In dir at {:?} is not a valid directory!", &in_dir);
return;
}
let tokens = res.value_of("tokens").map(PathBuf::from);
let logfile = PathBuf::from(res.value_of("logfile").unwrap().to_string());
fuzz(out_dir, crashes, in_dir, tokens, logfile).expect("An error occurred while fuzzing");
}
/// The actual fuzzer
fn fuzz(
corpus_dir: PathBuf,
objective_dir: PathBuf,
seed_dir: PathBuf,
tokenfile: Option<PathBuf>,
logfile: PathBuf,
) -> Result<(), Error> {
env::remove_var("LD_LIBRARY_PATH");
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
let emu = Emulator::new(&args, &env);
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
let test_one_input_ptr = elf
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
.expect("Symbol LLVMFuzzerTestOneInput not found");
println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr);
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
unsafe { emu.run() };
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
let mut ret_addr = [0; 8];
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
let ret_addr = u64::from_le_bytes(ret_addr);
println!("Stack pointer = {:#x}", stack_ptr);
println!("Return address = {:#x}", ret_addr);
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
println!("Placing input at {:#x}", input_addr);
let log = RefCell::new(
OpenOptions::new()
.append(true)
.create(true)
.open(&logfile)?,
);
#[cfg(unix)]
let mut stdout_cpy = unsafe {
let new_fd = dup(io::stdout().as_raw_fd())?;
File::from_raw_fd(new_fd)
};
#[cfg(unix)]
let file_null = File::open("/dev/null")?;
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
let monitor = SimpleMonitor::new(|s| {
#[cfg(unix)]
writeln!(&mut stdout_cpy, "{}", s).unwrap();
#[cfg(windows)]
println!("{}", s);
writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
});
let mut shmem_provider = StdShMemProvider::new()?;
let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_SIZE).unwrap();
let edges = edges_shmem.as_mut_slice();
unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() };
let mut cmp_shmem = shmem_provider
.new_shmem(core::mem::size_of::<CmpLogMap>())
.unwrap();
let cmplog = cmp_shmem.as_mut_slice();
unsafe { CMPLOG_MAP_PTR = cmplog.as_mut_ptr() as *mut CmpLogMap };
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
Ok(res) => res,
Err(err) => match err {
Error::ShuttingDown => {
return Ok(());
}
_ => {
panic!("Failed to setup the restarter: {}", err);
}
},
};
// Create an observation channel using the coverage map
let edges_observer =
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_SIZE>::new("edges", edges));
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// Create an observation channel using cmplog map
let cmplog_observer =
CmpLogObserver::new("cmplog", unsafe { CMPLOG_MAP_PTR.as_mut().unwrap() }, true);
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::new_with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
let objective = CrashFeedback::new();
// create a State from scratch
let mut state = state.unwrap_or_else(|| {
StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
OnDiskCorpus::new(corpus_dir).unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// States of the feedbacks.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
)
});
let calibration = CalibrationStage::new(&mut state, &edges_observer);
// Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
// Setup a MOPT mutator
let mutator = StdMOptMutator::new(&mut state, havoc_mutations().merge(tokens_mutations()), 5)?;
let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
if len > 4096 {
buf = &buf[0..4096];
len = 4096;
}
unsafe {
emu.write_mem(input_addr, buf);
emu.write_reg(Regs::Rdi, input_addr).unwrap();
emu.write_reg(Regs::Rsi, len).unwrap();
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
emu.run();
}
ExitKind::Ok
};
let hooks = QemuHooks::new(
&emu,
tuple_list!(
QemuEdgeCoverageChildHelper::default(),
QemuCmpLogChildHelper::default(),
),
);
let executor = QemuForkExecutor::new(
hooks,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
shmem_provider,
)?;
// Show the cmplog observer
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
// Read tokens
if let Some(tokenfile) = tokenfile {
if state.metadata().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from_file(tokenfile)?);
}
}
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
.unwrap_or_else(|_| {
println!("Failed to load initial corpus at {:?}", &seed_dir);
process::exit(0);
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
let tracing = ShadowTracingStage::new(&mut executor);
// The order of the stages matter!
let mut stages = tuple_list!(calibration, tracing, i2s, power);
// Remove target ouput (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();
dup2(null_fd, io::stdout().as_raw_fd())?;
dup2(null_fd, io::stderr().as_raw_fd())?;
}
// reopen file to make sure we're at the end
log.replace(
OpenOptions::new()
.append(true)
.create(true)
.open(&logfile)?,
);
fuzzer
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop");
// Never reached
Ok(())
}

View File

@ -0,0 +1,7 @@
#[cfg(target_os = "linux")]
pub mod fuzzer;
fn main() {
#[cfg(target_os = "linux")]
fuzzer::main()
}

View File

@ -55,6 +55,7 @@ use libafl_qemu::{
elf::EasyElf, elf::EasyElf,
emu::Emulator, emu::Emulator,
filter_qemu_args, filter_qemu_args,
hooks::QemuHooks,
//snapshot::QemuSnapshotHelper, //snapshot::QemuSnapshotHelper,
MmapPerms, MmapPerms,
QemuExecutor, QemuExecutor,
@ -323,15 +324,19 @@ fn fuzz(
ExitKind::Ok ExitKind::Ok
}; };
let executor = QemuExecutor::new( let hooks = QemuHooks::new(
&mut harness,
&emu, &emu,
tuple_list!( tuple_list!(
QemuEdgeCoverageHelper::new(), QemuEdgeCoverageHelper::default(),
QemuCmpLogHelper::new(), QemuCmpLogHelper::default(),
//QemuAsanHelper::new(), //QemuAsanHelper::new(),
//QemuSnapshotHelper::new() //QemuSnapshotHelper::new()
), ),
);
let executor = QemuExecutor::new(
hooks,
&mut harness,
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,

View File

@ -42,6 +42,7 @@ use libafl_qemu::{
//snapshot::QemuSnapshotHelper, //snapshot::QemuSnapshotHelper,
MmapPerms, MmapPerms,
QemuExecutor, QemuExecutor,
QemuHooks,
Regs, Regs,
}; };
@ -158,12 +159,12 @@ pub fn fuzz() {
// A fuzzer with feedbacks and a corpus scheduler // A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),));
// Create a QEMU in-process executor // Create a QEMU in-process executor
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
hooks,
&mut harness, &mut harness,
&emu,
// The QEMU helpers define common hooks like coverage tracking hooks
tuple_list!(QemuEdgeCoverageHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,

View File

@ -182,7 +182,7 @@ impl From<serde_json::Error> for Error {
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
impl From<nix::Error> for Error { impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self { fn from(err: nix::Error) -> Self {
Self::Unknown(format!("{:?}", err)) Self::Unknown(format!("Unix error: {:?}", err))
} }
} }

View File

@ -1,11 +1,11 @@
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{inputs::Input, state::HasMetadata};
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{env, fs, ptr}; use std::{env, fs, pin::Pin, ptr};
use crate::{ use crate::{
emu::{Emulator, SyscallHookResult}, emu::{Emulator, SyscallHookResult},
executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks,
GuestAddr, Regs, GuestAddr, Regs,
}; };
@ -172,16 +172,8 @@ pub struct QemuAsanHelper {
impl QemuAsanHelper { impl QemuAsanHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new(filter: QemuInstrumentationFilter) -> Self {
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)"); assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)");
Self {
enabled: true,
filter: QemuInstrumentationFilter::None,
}
}
#[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
Self { Self {
enabled: true, enabled: true,
filter, filter,
@ -410,7 +402,7 @@ impl QemuAsanHelper {
impl Default for QemuAsanHelper { impl Default for QemuAsanHelper {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new(QemuInstrumentationFilter::None)
} }
} }
@ -421,27 +413,25 @@ where
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = false; const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
//executor.hook_read_generation(gen_readwrite_asan::<I, QT, S>); //hooks.read_generation(gen_readwrite_asan::<I, QT, S>);
executor.hook_read8_execution(trace_read8_asan::<I, QT, S>); hooks.read8_execution(trace_read8_asan::<I, QT, S>);
executor.hook_read4_execution(trace_read4_asan::<I, QT, S>); hooks.read4_execution(trace_read4_asan::<I, QT, S>);
executor.hook_read2_execution(trace_read2_asan::<I, QT, S>); hooks.read2_execution(trace_read2_asan::<I, QT, S>);
executor.hook_read1_execution(trace_read1_asan::<I, QT, S>); hooks.read1_execution(trace_read1_asan::<I, QT, S>);
executor.hook_read_n_execution(trace_read_n_asan::<I, QT, S>); hooks.read_n_execution(trace_read_n_asan::<I, QT, S>);
//executor.hook_write_generation(gen_readwrite_asan::<I, QT, S>); //hooks.write_generation(gen_readwrite_asan::<I, QT, S>);
executor.hook_write8_execution(trace_write8_asan::<I, QT, S>); hooks.write8_execution(trace_write8_asan::<I, QT, S>);
executor.hook_write4_execution(trace_write4_asan::<I, QT, S>); hooks.write4_execution(trace_write4_asan::<I, QT, S>);
executor.hook_write2_execution(trace_write2_asan::<I, QT, S>); hooks.write2_execution(trace_write2_asan::<I, QT, S>);
executor.hook_write1_execution(trace_write1_asan::<I, QT, S>); hooks.write1_execution(trace_write1_asan::<I, QT, S>);
executor.hook_write_n_execution(trace_write_n_asan::<I, QT, S>); hooks.write_n_execution(trace_write_n_asan::<I, QT, S>);
executor.hook_syscalls(qasan_fake_syscall::<I, QT, S>); hooks.syscalls(qasan_fake_syscall::<I, QT, S>);
} }
fn post_exec(&mut self, _emulator: &Emulator, _input: &I) { fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {
@ -449,11 +439,12 @@ where
} }
} }
/*
// TODO add pc to generation hooks // TODO add pc to generation hooks
pub fn gen_readwrite_asan<I, QT, S>( pub fn gen_readwrite_asan<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
pc: u64, pc: u64,
_size: usize, _size: usize,
) -> Option<u64> ) -> Option<u64>
@ -468,11 +459,12 @@ where
None None
} }
} }
*/
pub fn trace_read1_asan<I, QT, S>( pub fn trace_read1_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -486,7 +478,7 @@ pub fn trace_read1_asan<I, QT, S>(
pub fn trace_read2_asan<I, QT, S>( pub fn trace_read2_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -500,7 +492,7 @@ pub fn trace_read2_asan<I, QT, S>(
pub fn trace_read4_asan<I, QT, S>( pub fn trace_read4_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -514,7 +506,7 @@ pub fn trace_read4_asan<I, QT, S>(
pub fn trace_read8_asan<I, QT, S>( pub fn trace_read8_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -528,7 +520,7 @@ pub fn trace_read8_asan<I, QT, S>(
pub fn trace_read_n_asan<I, QT, S>( pub fn trace_read_n_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
size: usize, size: usize,
@ -543,7 +535,7 @@ pub fn trace_read_n_asan<I, QT, S>(
pub fn trace_write1_asan<I, QT, S>( pub fn trace_write1_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -557,7 +549,7 @@ pub fn trace_write1_asan<I, QT, S>(
pub fn trace_write2_asan<I, QT, S>( pub fn trace_write2_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -571,7 +563,7 @@ pub fn trace_write2_asan<I, QT, S>(
pub fn trace_write4_asan<I, QT, S>( pub fn trace_write4_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -585,7 +577,7 @@ pub fn trace_write4_asan<I, QT, S>(
pub fn trace_write8_asan<I, QT, S>( pub fn trace_write8_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -599,7 +591,7 @@ pub fn trace_write8_asan<I, QT, S>(
pub fn trace_write_n_asan<I, QT, S>( pub fn trace_write_n_asan<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
size: usize, size: usize,
@ -615,7 +607,7 @@ pub fn trace_write_n_asan<I, QT, S>(
pub fn qasan_fake_syscall<I, QT, S>( pub fn qasan_fake_syscall<I, QT, S>(
emulator: &Emulator, emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
sys_num: i32, sys_num: i32,
a0: u64, a0: u64,
a1: u64, a1: u64,

View File

@ -1,14 +1,16 @@
use core::pin::Pin;
use hashbrown::HashMap; use hashbrown::HashMap;
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{inputs::Input, state::HasMetadata};
pub use libafl_targets::{ pub use libafl_targets::{
cmplog::__libafl_targets_cmplog_instructions, CmpLogObserver, CMPLOG_MAP, CMPLOG_MAP_W, cmplog::__libafl_targets_cmplog_instructions, CmpLogMap, CmpLogObserver, CMPLOG_MAP,
CMPLOG_MAP_H, CMPLOG_MAP_PTR, CMPLOG_MAP_SIZE, CMPLOG_MAP_W,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
emu::Emulator, emu::Emulator,
executor::QemuExecutor,
helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks,
}; };
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
@ -36,14 +38,7 @@ pub struct QemuCmpLogHelper {
impl QemuCmpLogHelper { impl QemuCmpLogHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new(filter: QemuInstrumentationFilter) -> Self {
Self {
filter: QemuInstrumentationFilter::None,
}
}
#[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
Self { filter } Self { filter }
} }
@ -55,7 +50,7 @@ impl QemuCmpLogHelper {
impl Default for QemuCmpLogHelper { impl Default for QemuCmpLogHelper {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new(QemuInstrumentationFilter::None)
} }
} }
@ -64,17 +59,15 @@ where
I: Input, I: Input,
S: HasMetadata, S: HasMetadata,
{ {
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_cmp_generation(gen_unique_cmp_ids::<I, QT, S>); hooks.cmp_generation(gen_unique_cmp_ids::<I, QT, S>);
executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog); hooks.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog); hooks.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog); hooks.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog); hooks.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
} }
} }
@ -85,14 +78,7 @@ pub struct QemuCmpLogChildHelper {
impl QemuCmpLogChildHelper { impl QemuCmpLogChildHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new(filter: QemuInstrumentationFilter) -> Self {
Self {
filter: QemuInstrumentationFilter::None,
}
}
#[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
Self { filter } Self { filter }
} }
@ -104,7 +90,7 @@ impl QemuCmpLogChildHelper {
impl Default for QemuCmpLogChildHelper { impl Default for QemuCmpLogChildHelper {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new(QemuInstrumentationFilter::None)
} }
} }
@ -115,24 +101,22 @@ where
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = false; const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_cmp_generation(gen_hashed_cmp_ids::<I, QT, S>); hooks.cmp_generation(gen_hashed_cmp_ids::<I, QT, S>);
executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog); hooks.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog); hooks.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog); hooks.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog); hooks.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
} }
} }
pub fn gen_unique_cmp_ids<I, QT, S>( pub fn gen_unique_cmp_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
state: &mut S, state: Option<&mut S>,
pc: u64, pc: u64,
_size: usize, _size: usize,
) -> Option<u64> ) -> Option<u64>
@ -146,6 +130,7 @@ where
return None; return None;
} }
} }
let state = state.expect("The gen_unique_cmp_ids hook works only for in-process fuzzing");
if state.metadata().get::<QemuCmpsMapMetadata>().is_none() { if state.metadata().get::<QemuCmpsMapMetadata>().is_none() {
state.add_metadata(QemuCmpsMapMetadata::new()); state.add_metadata(QemuCmpsMapMetadata::new());
} }
@ -164,7 +149,7 @@ where
pub fn gen_hashed_cmp_ids<I, QT, S>( pub fn gen_hashed_cmp_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
pc: u64, pc: u64,
_size: usize, _size: usize,
) -> Option<u64> ) -> Option<u64>
@ -178,7 +163,7 @@ where
return None; return None;
} }
} }
Some(hash_me(pc)) Some(hash_me(pc) & (CMPLOG_MAP_W as u64 - 1))
} }
pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8) { pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8) {

View File

@ -1,15 +1,15 @@
use hashbrown::{hash_map::Entry, HashMap}; use hashbrown::{hash_map::Entry, HashMap};
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{inputs::Input, state::HasMetadata};
pub use libafl_targets::{ pub use libafl_targets::{
edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM, edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{cell::UnsafeCell, cmp::max}; use std::{cell::UnsafeCell, cmp::max, pin::Pin};
use crate::{ use crate::{
emu::Emulator, emu::Emulator,
executor::QemuExecutor,
helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
hooks::QemuHooks,
}; };
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
@ -33,19 +33,24 @@ libafl::impl_serdeany!(QemuEdgesMapMetadata);
#[derive(Debug)] #[derive(Debug)]
pub struct QemuEdgeCoverageHelper { pub struct QemuEdgeCoverageHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationFilter,
use_hitcounts: bool,
} }
impl QemuEdgeCoverageHelper { impl QemuEdgeCoverageHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new(filter: QemuInstrumentationFilter) -> Self {
Self { Self {
filter: QemuInstrumentationFilter::None, filter,
use_hitcounts: true,
} }
} }
#[must_use] #[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self { pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self {
Self { filter } Self {
filter,
use_hitcounts: false,
}
} }
#[must_use] #[must_use]
@ -56,7 +61,7 @@ impl QemuEdgeCoverageHelper {
impl Default for QemuEdgeCoverageHelper { impl Default for QemuEdgeCoverageHelper {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new(QemuInstrumentationFilter::None)
} }
} }
@ -65,35 +70,42 @@ where
I: Input, I: Input,
S: HasMetadata, S: HasMetadata,
{ {
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>); hooks.edge_generation(gen_unique_edge_ids::<I, QT, S>);
executor.emulator().set_exec_edge_hook(trace_edge_hitcount); if self.use_hitcounts {
hooks.emulator().set_exec_edge_hook(trace_edge_hitcount);
} else {
hooks.emulator().set_exec_edge_hook(trace_edge_single);
}
} }
} }
pub type QemuEdgeCoverageWithBlocksHelper = QemuEdgeCoverageChildHelper; pub type QemuCollidingEdgeCoverageHelper = QemuEdgeCoverageChildHelper;
#[derive(Debug)] #[derive(Debug)]
pub struct QemuEdgeCoverageChildHelper { pub struct QemuEdgeCoverageChildHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationFilter,
use_hitcounts: bool,
} }
impl QemuEdgeCoverageChildHelper { impl QemuEdgeCoverageChildHelper {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new(filter: QemuInstrumentationFilter) -> Self {
Self { Self {
filter: QemuInstrumentationFilter::None, filter,
use_hitcounts: true,
} }
} }
#[must_use] #[must_use]
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self { pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self {
Self { filter } Self {
filter,
use_hitcounts: false,
}
} }
#[must_use] #[must_use]
@ -104,7 +116,7 @@ impl QemuEdgeCoverageChildHelper {
impl Default for QemuEdgeCoverageChildHelper { impl Default for QemuEdgeCoverageChildHelper {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new(QemuInstrumentationFilter::None)
} }
} }
@ -115,14 +127,16 @@ where
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = false; const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>); hooks.edge_generation(gen_hashed_edge_ids::<I, QT, S>);
executor.emulator().set_exec_edge_hook(trace_edge_hitcount); if self.use_hitcounts {
hooks.emulator().set_exec_edge_hook(trace_edge_hitcount_ptr);
} else {
hooks.emulator().set_exec_edge_hook(trace_edge_single_ptr);
}
} }
} }
@ -131,7 +145,7 @@ thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0));
pub fn gen_unique_edge_ids<I, QT, S>( pub fn gen_unique_edge_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
state: &mut S, state: Option<&mut S>,
src: u64, src: u64,
dest: u64, dest: u64,
) -> Option<u64> ) -> Option<u64>
@ -145,6 +159,7 @@ where
return None; return None;
} }
} }
let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing");
if state.metadata().get::<QemuEdgesMapMetadata>().is_none() { if state.metadata().get::<QemuEdgesMapMetadata>().is_none() {
state.add_metadata(QemuEdgesMapMetadata::new()); state.add_metadata(QemuEdgesMapMetadata::new());
} }
@ -174,25 +189,6 @@ where
} }
} }
pub fn gen_hashed_edge_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: &mut S,
src: u64,
dest: u64,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageChildHelper>() {
if !h.must_instrument(src) && !h.must_instrument(dest) {
return None;
}
}
Some(hash_me(src) ^ hash_me(dest))
}
pub extern "C" fn trace_edge_hitcount(id: u64) { pub extern "C" fn trace_edge_hitcount(id: u64) {
unsafe { unsafe {
EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1);
@ -205,10 +201,43 @@ pub extern "C" fn trace_edge_single(id: u64) {
} }
} }
pub fn gen_hashed_edge_ids<I, QT, S>(
_emulator: &Emulator,
helpers: &mut QT,
_state: Option<&mut S>,
src: u64,
dest: u64,
) -> Option<u64>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageChildHelper>() {
if !h.must_instrument(src) && !h.must_instrument(dest) {
return None;
}
}
Some((hash_me(src) ^ hash_me(dest)) & (unsafe { EDGES_MAP_PTR_SIZE } as u64 - 1))
}
pub extern "C" fn trace_edge_hitcount_ptr(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) {
unsafe {
let ptr = EDGES_MAP_PTR.add(id as usize);
*ptr = 1;
}
}
pub fn gen_addr_block_ids<I, QT, S>( pub fn gen_addr_block_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
_helpers: &mut QT, _helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
pc: u64, pc: u64,
) -> Option<u64> { ) -> Option<u64> {
Some(pc) Some(pc)
@ -217,7 +246,7 @@ pub fn gen_addr_block_ids<I, QT, S>(
pub fn gen_hashed_block_ids<I, QT, S>( pub fn gen_hashed_block_ids<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
_helpers: &mut QT, _helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
pc: u64, pc: u64,
) -> Option<u64> { ) -> Option<u64> {
Some(hash_me(pc)) Some(hash_me(pc))

View File

@ -12,11 +12,11 @@ use num_traits::Num;
use std::{slice::from_raw_parts, str::from_utf8_unchecked}; use std::{slice::from_raw_parts, str::from_utf8_unchecked};
use strum_macros::EnumIter; use strum_macros::EnumIter;
#[cfg(not(any(feature = "x86_64", feature = "aarch64")))] #[cfg(not(any(cpu_target = "x86_64", cpu_target = "aarch64")))]
/// `GuestAddr` is u32 for 32-bit targets /// `GuestAddr` is u32 for 32-bit targets
pub type GuestAddr = u32; pub type GuestAddr = u32;
#[cfg(any(feature = "x86_64", feature = "aarch64"))] #[cfg(any(cpu_target = "x86_64", cpu_target = "aarch64"))]
/// `GuestAddr` is u64 for 64-bit targets /// `GuestAddr` is u64 for 64-bit targets
pub type GuestAddr = u64; pub type GuestAddr = u64;
@ -317,7 +317,10 @@ impl Emulator {
#[allow(clippy::must_use_candidate, clippy::similar_names)] #[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new(args: &[String], env: &[(String, String)]) -> Emulator { pub fn new(args: &[String], env: &[(String, String)]) -> Emulator {
unsafe { unsafe {
assert!(!EMULATOR_IS_INITIALIZED); assert!(
!EMULATOR_IS_INITIALIZED,
"Only an instance of Emulator is permitted"
);
} }
assert!(!args.is_empty()); assert!(!args.is_empty());
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect(); let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect();

View File

@ -1,16 +1,13 @@
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL` //! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
use core::{ use core::{
ffi::c_void,
fmt::{self, Debug, Formatter}, fmt::{self, Debug, Formatter},
mem::transmute, pin::Pin,
ptr::{self, addr_of},
}; };
use libafl::{ use libafl::{
bolts::shmem::ShMemProvider,
events::{EventFirer, EventRestarter}, events::{EventFirer, EventRestarter},
executors::{ executors::{Executor, ExitKind, HasObservers, InProcessExecutor, InProcessForkExecutor},
inprocess::inprocess_get_state, Executor, ExitKind, HasObservers, InProcessExecutor,
},
feedbacks::Feedback, feedbacks::Feedback,
fuzzer::HasObjective, fuzzer::HasObjective,
inputs::Input, inputs::Input,
@ -20,444 +17,7 @@ use libafl::{
}; };
pub use crate::emu::SyscallHookResult; pub use crate::emu::SyscallHookResult;
use crate::{ use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
emu::{Emulator, SKIP_EXEC_HOOK},
helper::QemuHelperTuple,
GuestAddr,
};
static mut QEMU_HELPERS_PTR: *const c_void = ptr::null();
static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_edge_hook_wrapper<I, QT, S>(src: u64, dst: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, &mut S, u64, u64) -> Option<u64> =
transmute(GEN_EDGE_HOOK_PTR);
(func)(&emulator, helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut EDGE_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn edge_hooks_wrapper<I, QT, S>(id: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &EDGE_HOOKS } {
let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id);
}
}
static mut GEN_BLOCK_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_block_hook_wrapper<I, QT, S>(pc: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, &mut S, u64) -> Option<u64> = transmute(GEN_EDGE_HOOK_PTR);
(func)(&emulator, helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut BLOCK_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn block_hooks_wrapper<I, QT, S>(id: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &BLOCK_HOOKS } {
let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id);
}
}
static mut GEN_READ_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_read_hook_wrapper<I, QT, S>(size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, &mut S, usize) -> Option<u64> =
transmute(GEN_READ_HOOK_PTR);
(func)(&emulator, helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut GEN_WRITE_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_write_hook_wrapper<I, QT, S>(size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, &mut S, usize) -> Option<u64> =
transmute(GEN_WRITE_HOOK_PTR);
(func)(&emulator, helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
// function signature for Read or Write hook functions with known length (1, 2, 4, 8)
type FixedLenHook<QT, S> = fn(&Emulator, &mut QT, &mut S, u64, GuestAddr);
// function signature for Read or Write hook functions with runtime length n
type DynamicLenHook<QT, S> = fn(&Emulator, &mut QT, &mut S, u64, GuestAddr, usize);
static mut READ1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read1_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ1_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut READ2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read2_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ2_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut READ4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read4_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ4_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut READ8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read8_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ8_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut READ_N_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read_n_hooks_wrapper<I, QT, S>(id: u64, addr: u64, size: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &READ_N_HOOKS } {
let func: DynamicLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
state,
id,
addr as GuestAddr,
size as usize,
);
}
}
static mut WRITE1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write1_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE1_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut WRITE2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write2_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE2_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut WRITE4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write4_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE4_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut WRITE8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write8_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE8_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, addr as GuestAddr);
}
}
static mut WRITE_N_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write_n_hooks_wrapper<I, QT, S>(id: u64, addr: u64, size: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE_N_HOOKS } {
let func: DynamicLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
state,
id,
addr as GuestAddr,
size as usize,
);
}
}
static mut GEN_CMP_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_cmp_hook_wrapper<I, QT, S>(pc: u64, size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, &mut S, u64, usize) -> Option<u64> =
transmute(GEN_CMP_HOOK_PTR);
(func)(&emulator, helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut CMP1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp1_hooks_wrapper<I, QT, S>(id: u64, v0: u8, v1: u8)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP1_HOOKS } {
let func: fn(&Emulator, &mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, v0, v1);
}
}
static mut CMP2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp2_hooks_wrapper<I, QT, S>(id: u64, v0: u16, v1: u16)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP2_HOOKS } {
let func: fn(&Emulator, &mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, v0, v1);
}
}
static mut CMP4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp4_hooks_wrapper<I, QT, S>(id: u64, v0: u32, v1: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP4_HOOKS } {
let func: fn(&Emulator, &mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, v0, v1);
}
}
static mut CMP8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp8_hooks_wrapper<I, QT, S>(id: u64, v0: u64, v1: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP8_HOOKS } {
let func: fn(&Emulator, &mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, state, id, v0, v1);
}
}
static mut SYSCALL_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn syscall_hooks_wrapper<I, QT, S>(
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> SyscallHookResult
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let mut res = SyscallHookResult::new(None);
for hook in unsafe { &SYSCALL_HOOKS } {
#[allow(clippy::type_complexity)]
let func: fn(
&Emulator,
&mut QT,
&mut S,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult = unsafe { transmute(*hook) };
let r = (func)(
&emulator, helpers, state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7,
);
if r.skip_syscall {
res.skip_syscall = true;
res.retval = r.retval;
}
}
res
}
static mut SYSCALL_POST_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
result: u64,
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
let state = inprocess_get_state::<S>().unwrap();
let emulator = Emulator::new_empty();
let mut res = result;
for hook in unsafe { &SYSCALL_POST_HOOKS } {
#[allow(clippy::type_complexity)]
let func: fn(
&Emulator,
&mut QT,
&mut S,
u64,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64 = unsafe { transmute(*hook) };
res = (func)(
&emulator, helpers, state, res, sys_num, a0, a1, a2, a3, a4, a5, a6, a7,
);
}
res
}
pub struct QemuExecutor<'a, H, I, OT, QT, S> pub struct QemuExecutor<'a, H, I, OT, QT, S>
where where
@ -466,8 +26,7 @@ where
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
helpers: QT, hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
emulator: &'a Emulator,
inner: InProcessExecutor<'a, H, I, OT, S>, inner: InProcessExecutor<'a, H, I, OT, S>,
} }
@ -480,8 +39,7 @@ where
{ {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuExecutor") f.debug_struct("QemuExecutor")
.field("helpers", &self.helpers) .field("hooks", &self.hooks)
.field("emulator", &self.emulator)
.field("inner", &self.inner) .field("inner", &self.inner)
.finish() .finish()
} }
@ -495,9 +53,8 @@ where
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
pub fn new<EM, OF, Z>( pub fn new<EM, OF, Z>(
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
harness_fn: &'a mut H, harness_fn: &'a mut H,
emulator: &'a Emulator,
helpers: QT,
observers: OT, observers: OT,
fuzzer: &mut Z, fuzzer: &mut Z,
state: &mut S, state: &mut S,
@ -509,13 +66,10 @@ where
S: HasSolutions<I> + HasClientPerfMonitor, S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>, Z: HasObjective<I, OF, S>,
{ {
let slf = Self { Ok(Self {
helpers, hooks,
emulator,
inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?, inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?,
}; })
slf.helpers.init_all(&slf);
Ok(slf)
} }
pub fn inner(&self) -> &InProcessExecutor<'a, H, I, OT, S> { pub fn inner(&self) -> &InProcessExecutor<'a, H, I, OT, S> {
@ -526,277 +80,16 @@ where
&mut self.inner &mut self.inner
} }
pub fn hooks(&self) -> &Pin<Box<QemuHooks<'a, I, QT, S>>> {
&self.hooks
}
pub fn hooks_mut(&mut self) -> &mut Pin<Box<QemuHooks<'a, I, QT, S>>> {
&mut self.hooks
}
pub fn emulator(&self) -> &Emulator { pub fn emulator(&self) -> &Emulator {
self.emulator self.hooks.emulator()
}
#[allow(clippy::unused_self)]
pub fn hook_edge_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64) -> Option<u64>,
) {
unsafe {
GEN_EDGE_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_edge_hook(gen_edge_hook_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_edge_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64)) {
unsafe {
EDGE_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_edge_hook(edge_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_block_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, pc: u64) -> Option<u64>,
) {
unsafe {
GEN_BLOCK_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_block_hook(gen_block_hook_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_block_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64)) {
unsafe {
BLOCK_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_block_hook(block_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_read_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, size: usize) -> Option<u64>,
) {
unsafe {
GEN_READ_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_read_hook(gen_read_hook_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_read1_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ1_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read1_hook(read1_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_read2_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ2_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read2_hook(read2_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_read4_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ4_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read4_hook(read4_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_read8_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ8_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read8_hook(read8_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_read_n_execution(&self, hook: DynamicLenHook<QT, S>) {
unsafe {
READ_N_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read_n_hook(read_n_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_write_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, size: usize) -> Option<u64>,
) {
unsafe {
GEN_WRITE_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_write_hook(gen_write_hook_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_write1_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE1_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write1_hook(write1_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_write2_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE2_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write2_hook(write2_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_write4_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE4_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write4_hook(write4_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_write8_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE8_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write8_hook(write8_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_write_n_execution(&self, hook: DynamicLenHook<QT, S>) {
unsafe {
WRITE_N_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write_n_hook(write_n_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_cmp_generation(
&self,
hook: fn(&Emulator, &mut QT, &mut S, pc: u64, size: usize) -> Option<u64>,
) {
unsafe {
GEN_CMP_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_cmp_hook(gen_cmp_hook_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_cmp1_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u8, v1: u8),
) {
unsafe {
CMP1_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp1_hook(cmp1_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_cmp2_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u16, v1: u16),
) {
unsafe {
CMP2_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp2_hook(cmp2_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_cmp4_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u32, v1: u32),
) {
unsafe {
CMP4_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp4_hook(cmp4_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
pub fn hook_cmp8_execution(
&self,
hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u64, v1: u64),
) {
unsafe {
CMP8_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp8_hook(cmp8_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
#[allow(clippy::type_complexity)]
pub fn hook_syscalls(
&self,
hook: fn(
&Emulator,
&mut QT,
&mut S,
sys_num: i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult,
) {
unsafe {
SYSCALL_HOOKS.push(hook as *const _);
}
self.emulator
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::unused_self)]
#[allow(clippy::type_complexity)]
pub fn hook_after_syscalls(
&self,
hook: fn(
&Emulator,
&mut QT,
&mut S,
result: u64,
sys_num: i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64,
) {
unsafe {
SYSCALL_POST_HOOKS.push(hook as *const _);
}
self.emulator
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
} }
} }
@ -814,11 +107,22 @@ where
mgr: &mut EM, mgr: &mut EM,
input: &I, input: &I,
) -> Result<ExitKind, Error> { ) -> Result<ExitKind, Error> {
unsafe { QEMU_HELPERS_PTR = addr_of!(self.helpers) as *const c_void }; let emu = Emulator::new_empty();
self.helpers.pre_exec_all(self.emulator, input); unsafe {
self.hooks
.as_mut()
.get_unchecked_mut()
.helpers_mut()
.pre_exec_all(&emu, input);
}
let r = self.inner.run_target(fuzzer, state, mgr, input); let r = self.inner.run_target(fuzzer, state, mgr, input);
self.helpers.post_exec_all(self.emulator, input); unsafe {
unsafe { QEMU_HELPERS_PTR = ptr::null() }; self.hooks
.as_mut()
.get_unchecked_mut()
.helpers_mut()
.post_exec_all(&emu, input);
}
r r
} }
} }
@ -840,3 +144,145 @@ where
self.inner.observers_mut() self.inner.observers_mut()
} }
} }
pub struct QemuForkExecutor<'a, H, I, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
SP: ShMemProvider,
{
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
inner: InProcessForkExecutor<'a, H, I, OT, S, SP>,
}
impl<'a, H, I, OT, QT, S, SP> Debug for QemuForkExecutor<'a, H, I, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
SP: ShMemProvider,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuForkExecutor")
.field("hooks", &self.hooks)
.field("inner", &self.inner)
.finish()
}
}
impl<'a, H, I, OT, QT, S, SP> QemuForkExecutor<'a, H, I, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
SP: ShMemProvider,
{
pub fn new<EM, OF, Z>(
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
shmem_provider: SP,
) -> Result<Self, Error>
where
EM: EventFirer<I> + EventRestarter<S>,
OF: Feedback<I, S>,
S: HasSolutions<I> + HasClientPerfMonitor,
Z: HasObjective<I, OF, S>,
{
assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded");
Ok(Self {
hooks,
inner: InProcessForkExecutor::new(
harness_fn,
observers,
fuzzer,
state,
event_mgr,
shmem_provider,
)?,
})
}
pub fn inner(&self) -> &InProcessForkExecutor<'a, H, I, OT, S, SP> {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut InProcessForkExecutor<'a, H, I, OT, S, SP> {
&mut self.inner
}
pub fn hooks(&self) -> &Pin<Box<QemuHooks<'a, I, QT, S>>> {
&self.hooks
}
pub fn hooks_mut(&mut self) -> &mut Pin<Box<QemuHooks<'a, I, QT, S>>> {
&mut self.hooks
}
pub fn emulator(&self) -> &Emulator {
self.hooks.emulator()
}
}
impl<'a, EM, H, I, OT, QT, S, Z, SP> Executor<EM, I, S, Z>
for QemuForkExecutor<'a, H, I, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
SP: ShMemProvider,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error> {
let emu = Emulator::new_empty();
unsafe {
self.hooks
.as_mut()
.get_unchecked_mut()
.helpers_mut()
.pre_exec_all(&emu, input);
}
let r = self.inner.run_target(fuzzer, state, mgr, input);
unsafe {
self.hooks
.as_mut()
.get_unchecked_mut()
.helpers_mut()
.post_exec_all(&emu, input);
}
r
}
}
impl<'a, H, I, OT, QT, S, SP> HasObservers<I, OT, S> for QemuForkExecutor<'a, H, I, OT, QT, S, SP>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>,
SP: ShMemProvider,
{
#[inline]
fn observers(&self) -> &OT {
self.inner.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
self.inner.observers_mut()
}
}

View File

@ -1,9 +1,7 @@
use core::{fmt::Debug, ops::Range}; use core::{fmt::Debug, ops::Range, pin::Pin};
use libafl::{ use libafl::{bolts::tuples::MatchFirstType, inputs::Input};
bolts::tuples::MatchFirstType, executors::ExitKind, inputs::Input, observers::ObserversTuple,
};
use crate::{emu::Emulator, executor::QemuExecutor}; use crate::{emu::Emulator, hooks::QemuHooks};
/// A helper for `libafl_qemu`. /// A helper for `libafl_qemu`.
// TODO remove 'static when specialization will be stable // TODO remove 'static when specialization will be stable
@ -13,10 +11,8 @@ where
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = true; const HOOKS_DO_SIDE_EFFECTS: bool = true;
fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, _hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
} }
@ -30,10 +26,10 @@ pub trait QemuHelperTuple<I, S>: MatchFirstType + Debug
where where
I: Input, I: Input,
{ {
fn init_all<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) const HOOKS_DO_SIDE_EFFECTS: bool;
fn init_hooks_all<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>; QT: QemuHelperTuple<I, S>;
fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I); fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I);
@ -45,10 +41,10 @@ impl<I, S> QemuHelperTuple<I, S> for ()
where where
I: Input, I: Input,
{ {
fn init_all<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>) const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks_all<'a, QT>(&self, _hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
} }
@ -64,14 +60,14 @@ where
Tail: QemuHelperTuple<I, S>, Tail: QemuHelperTuple<I, S>,
I: Input, I: Input,
{ {
fn init_all<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
fn init_hooks_all<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
self.0.init(executor); self.0.init_hooks(hooks);
self.1.init_all(executor); self.1.init_hooks_all(hooks);
} }
fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) { fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) {

860
libafl_qemu/src/hooks.rs Normal file
View File

@ -0,0 +1,860 @@
//! The high-level hooks
use core::{
ffi::c_void,
fmt::{self, Debug, Formatter},
marker::{PhantomData, PhantomPinned},
mem::transmute,
pin::Pin,
ptr::{self, addr_of},
};
use libafl::{executors::inprocess::inprocess_get_state, inputs::Input};
pub use crate::emu::SyscallHookResult;
use crate::{
emu::{Emulator, SKIP_EXEC_HOOK},
helper::{QemuHelper, QemuHelperTuple},
GuestAddr,
};
static mut QEMU_HELPERS_PTR: *const c_void = ptr::null();
unsafe fn get_qemu_helpers<'a, QT>() -> &'a mut QT {
(QEMU_HELPERS_PTR as *mut QT)
.as_mut()
.expect("A high-level hook is installed but QemuHooks is not initialized")
}
static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_edge_hook_wrapper<I, QT, S>(src: u64, dst: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = get_qemu_helpers::<QT>();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u64) -> Option<u64> =
transmute(GEN_EDGE_HOOK_PTR);
(func)(&emulator, helpers, inprocess_get_state::<S>(), src, dst)
.map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut EDGE_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn edge_hooks_wrapper<I, QT, S>(id: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &EDGE_HOOKS } {
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, inprocess_get_state::<S>(), id);
}
}
static mut GEN_BLOCK_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_block_hook_wrapper<I, QT, S>(pc: u64) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = get_qemu_helpers::<QT>();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64) -> Option<u64> =
transmute(GEN_BLOCK_HOOK_PTR);
(func)(&emulator, helpers, inprocess_get_state::<S>(), pc).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut BLOCK_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn block_hooks_wrapper<I, QT, S>(id: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &BLOCK_HOOKS } {
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, inprocess_get_state::<S>(), id);
}
}
static mut GEN_READ_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_read_hook_wrapper<I, QT, S>(size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = get_qemu_helpers::<QT>();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, Option<&mut S>, usize) -> Option<u64> =
transmute(GEN_READ_HOOK_PTR);
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
size as usize,
)
.map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut GEN_WRITE_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_write_hook_wrapper<I, QT, S>(size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = get_qemu_helpers::<QT>();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, Option<&mut S>, usize) -> Option<u64> =
transmute(GEN_WRITE_HOOK_PTR);
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
size as usize,
)
.map_or(SKIP_EXEC_HOOK, |id| id)
}
}
// function signature for Read or Write hook functions with known length (1, 2, 4, 8)
type FixedLenHook<QT, S> = fn(&Emulator, &mut QT, Option<&mut S>, u64, GuestAddr);
// function signature for Read or Write hook functions with runtime length n
type DynamicLenHook<QT, S> = fn(&Emulator, &mut QT, Option<&mut S>, u64, GuestAddr, usize);
static mut READ1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read1_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &READ1_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut READ2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read2_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &READ2_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut READ4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read4_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &READ4_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut READ8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read8_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &READ8_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut READ_N_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn read_n_hooks_wrapper<I, QT, S>(id: u64, addr: u64, size: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &READ_N_HOOKS } {
let func: DynamicLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
size as usize,
);
}
}
static mut WRITE1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write1_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE1_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut WRITE2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write2_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE2_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut WRITE4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write4_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE4_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut WRITE8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write8_hooks_wrapper<I, QT, S>(id: u64, addr: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE8_HOOKS } {
let func: FixedLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
);
}
}
static mut WRITE_N_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn write_n_hooks_wrapper<I, QT, S>(id: u64, addr: u64, size: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &WRITE_N_HOOKS } {
let func: DynamicLenHook<QT, S> = unsafe { transmute(*hook) };
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
id,
addr as GuestAddr,
size as usize,
);
}
}
static mut GEN_CMP_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_cmp_hook_wrapper<I, QT, S>(pc: u64, size: u32) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
unsafe {
let helpers = get_qemu_helpers::<QT>();
let emulator = Emulator::new_empty();
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, usize) -> Option<u64> =
transmute(GEN_CMP_HOOK_PTR);
(func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
pc,
size as usize,
)
.map_or(SKIP_EXEC_HOOK, |id| id)
}
}
static mut CMP1_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp1_hooks_wrapper<I, QT, S>(id: u64, v0: u8, v1: u8)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP1_HOOKS } {
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u8, u8) = unsafe { transmute(*hook) };
(func)(&emulator, helpers, inprocess_get_state::<S>(), id, v0, v1);
}
}
static mut CMP2_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp2_hooks_wrapper<I, QT, S>(id: u64, v0: u16, v1: u16)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP2_HOOKS } {
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u16, u16) =
unsafe { transmute(*hook) };
(func)(&emulator, helpers, inprocess_get_state::<S>(), id, v0, v1);
}
}
static mut CMP4_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp4_hooks_wrapper<I, QT, S>(id: u64, v0: u32, v1: u32)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP4_HOOKS } {
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u32, u32) =
unsafe { transmute(*hook) };
(func)(&emulator, helpers, inprocess_get_state::<S>(), id, v0, v1);
}
}
static mut CMP8_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn cmp8_hooks_wrapper<I, QT, S>(id: u64, v0: u64, v1: u64)
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
for hook in unsafe { &CMP8_HOOKS } {
let func: fn(&Emulator, &mut QT, Option<&mut S>, u64, u64, u64) =
unsafe { transmute(*hook) };
(func)(&emulator, helpers, inprocess_get_state::<S>(), id, v0, v1);
}
}
static mut SYSCALL_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn syscall_hooks_wrapper<I, QT, S>(
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> SyscallHookResult
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
let mut res = SyscallHookResult::new(None);
for hook in unsafe { &SYSCALL_HOOKS } {
#[allow(clippy::type_complexity)]
let func: fn(
&Emulator,
&mut QT,
Option<&mut S>,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult = unsafe { transmute(*hook) };
let r = (func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
sys_num,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
);
if r.skip_syscall {
res.skip_syscall = true;
res.retval = r.retval;
}
}
res
}
static mut SYSCALL_POST_HOOKS: Vec<*const c_void> = vec![];
extern "C" fn syscall_after_hooks_wrapper<I, QT, S>(
result: u64,
sys_num: i32,
a0: u64,
a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
a6: u64,
a7: u64,
) -> u64
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
let helpers = unsafe { get_qemu_helpers::<QT>() };
let emulator = Emulator::new_empty();
let mut res = result;
for hook in unsafe { &SYSCALL_POST_HOOKS } {
#[allow(clippy::type_complexity)]
let func: fn(
&Emulator,
&mut QT,
Option<&mut S>,
u64,
i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64 = unsafe { transmute(*hook) };
res = (func)(
&emulator,
helpers,
inprocess_get_state::<S>(),
res,
sys_num,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
);
}
res
}
static mut HOOKS_IS_INITIALIZED: bool = false;
pub struct QemuHooks<'a, I, QT, S>
where
QT: QemuHelperTuple<I, S>,
I: Input,
{
helpers: QT,
emulator: &'a Emulator,
phantom: PhantomData<(I, S)>,
_pin: PhantomPinned,
}
impl<'a, I, QT, S> Debug for QemuHooks<'a, I, QT, S>
where
I: Input,
QT: QemuHelperTuple<I, S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuHooks")
.field("helpers", &self.helpers)
.field("emulator", &self.emulator)
.finish()
}
}
impl<'a, I, QT, S> QemuHooks<'a, I, QT, S>
where
QT: QemuHelperTuple<I, S>,
I: Input,
{
pub fn new(emulator: &'a Emulator, helpers: QT) -> Pin<Box<Self>> {
unsafe {
assert!(
!HOOKS_IS_INITIALIZED,
"Only an instance of QemuHooks is permitted"
);
HOOKS_IS_INITIALIZED = true;
}
let slf = Box::pin(Self {
emulator,
helpers,
phantom: PhantomData,
_pin: PhantomPinned,
});
slf.helpers.init_hooks_all(slf.as_ref());
unsafe {
QEMU_HELPERS_PTR = addr_of!(slf.helpers) as *const c_void;
}
slf
}
#[must_use]
pub fn match_helper<'b, T>(self: &'b Pin<&mut Self>) -> Option<&'b T>
where
T: QemuHelper<I, S>,
{
self.helpers.match_first_type::<T>()
}
#[must_use]
pub fn match_helper_mut<'b, T>(self: &'b mut Pin<&mut Self>) -> Option<&'b mut T>
where
T: QemuHelper<I, S>,
{
unsafe {
self.as_mut()
.get_unchecked_mut()
.helpers
.match_first_type_mut::<T>()
}
}
pub fn emulator(&self) -> &Emulator {
self.emulator
}
pub fn helpers(&self) -> &QT {
&self.helpers
}
pub fn helpers_mut(&mut self) -> &mut QT {
&mut self.helpers
}
pub fn edge_generation(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, src: u64, dest: u64) -> Option<u64>,
) {
unsafe {
GEN_EDGE_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_edge_hook(gen_edge_hook_wrapper::<I, QT, S>);
}
pub fn edge_execution(&self, hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64)) {
unsafe {
EDGE_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_edge_hook(edge_hooks_wrapper::<I, QT, S>);
}
pub fn block_generation(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, pc: u64) -> Option<u64>,
) {
unsafe {
GEN_BLOCK_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_block_hook(gen_block_hook_wrapper::<I, QT, S>);
}
pub fn block_execution(&self, hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64)) {
unsafe {
BLOCK_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_block_hook(block_hooks_wrapper::<I, QT, S>);
}
pub fn read_generation(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, size: usize) -> Option<u64>,
) {
unsafe {
GEN_READ_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_read_hook(gen_read_hook_wrapper::<I, QT, S>);
}
pub fn read1_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ1_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read1_hook(read1_hooks_wrapper::<I, QT, S>);
}
pub fn read2_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ2_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read2_hook(read2_hooks_wrapper::<I, QT, S>);
}
pub fn read4_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ4_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read4_hook(read4_hooks_wrapper::<I, QT, S>);
}
pub fn read8_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
READ8_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read8_hook(read8_hooks_wrapper::<I, QT, S>);
}
pub fn read_n_execution(&self, hook: DynamicLenHook<QT, S>) {
unsafe {
READ_N_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_read_n_hook(read_n_hooks_wrapper::<I, QT, S>);
}
pub fn write_generation(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, size: usize) -> Option<u64>,
) {
unsafe {
GEN_WRITE_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_write_hook(gen_write_hook_wrapper::<I, QT, S>);
}
pub fn write1_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE1_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write1_hook(write1_hooks_wrapper::<I, QT, S>);
}
pub fn write2_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE2_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write2_hook(write2_hooks_wrapper::<I, QT, S>);
}
pub fn write4_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE4_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write4_hook(write4_hooks_wrapper::<I, QT, S>);
}
pub fn write8_execution(&self, hook: FixedLenHook<QT, S>) {
unsafe {
WRITE8_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write8_hook(write8_hooks_wrapper::<I, QT, S>);
}
pub fn write_n_execution(&self, hook: DynamicLenHook<QT, S>) {
unsafe {
WRITE_N_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_write_n_hook(write_n_hooks_wrapper::<I, QT, S>);
}
pub fn cmp_generation(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, pc: u64, size: usize) -> Option<u64>,
) {
unsafe {
GEN_CMP_HOOK_PTR = hook as *const _;
}
self.emulator
.set_gen_cmp_hook(gen_cmp_hook_wrapper::<I, QT, S>);
}
pub fn cmp1_execution(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u8, v1: u8),
) {
unsafe {
CMP1_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp1_hook(cmp1_hooks_wrapper::<I, QT, S>);
}
pub fn cmp2_execution(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u16, v1: u16),
) {
unsafe {
CMP2_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp2_hook(cmp2_hooks_wrapper::<I, QT, S>);
}
pub fn cmp4_execution(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u32, v1: u32),
) {
unsafe {
CMP4_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp4_hook(cmp4_hooks_wrapper::<I, QT, S>);
}
pub fn cmp8_execution(
&self,
hook: fn(&Emulator, &mut QT, Option<&mut S>, id: u64, v0: u64, v1: u64),
) {
unsafe {
CMP8_HOOKS.push(hook as *const _);
}
self.emulator
.set_exec_cmp8_hook(cmp8_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::type_complexity)]
pub fn syscalls(
&self,
hook: fn(
&Emulator,
&mut QT,
Option<&mut S>,
sys_num: i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> SyscallHookResult,
) {
unsafe {
SYSCALL_HOOKS.push(hook as *const _);
}
self.emulator
.set_pre_syscall_hook(syscall_hooks_wrapper::<I, QT, S>);
}
#[allow(clippy::type_complexity)]
pub fn after_syscalls(
&self,
hook: fn(
&Emulator,
&mut QT,
Option<&mut S>,
result: u64,
sys_num: i32,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
u64,
) -> u64,
) {
unsafe {
SYSCALL_POST_HOOKS.push(hook as *const _);
}
self.emulator
.set_post_syscall_hook(syscall_after_hooks_wrapper::<I, QT, S>);
}
}

View File

@ -1,9 +1,10 @@
// This lint triggers too often on the current GuestAddr type when emulating 64-bit targets because // 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. // u64::from(GuestAddr) is a no-op, but the .into() call is needed when GuestAddr is u32.
#![cfg_attr( #![cfg_attr(
any(feature = "x86_64", feature = "aarch64"), any(cpu_target = "x86_64", cpu_target = "aarch64"),
allow(clippy::useless_conversion) allow(clippy::useless_conversion)
)] )]
#![allow(clippy::needless_pass_by_value)]
use std::env; use std::env;
@ -33,6 +34,10 @@ pub mod elf;
pub mod helper; pub mod helper;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub use helper::*; pub use helper::*;
#[cfg(target_os = "linux")]
pub mod hooks;
#[cfg(target_os = "linux")]
pub use hooks::*;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod edges; pub mod edges;
@ -54,7 +59,7 @@ pub use asan::{init_with_asan, QemuAsanHelper};
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod executor; pub mod executor;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub use executor::QemuExecutor; pub use executor::{QemuExecutor, QemuForkExecutor};
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod emu; pub mod emu;

View File

@ -1,16 +1,17 @@
use bio::data_structures::interval_tree::IntervalTree; use bio::data_structures::interval_tree::IntervalTree;
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use libafl::{inputs::Input, state::HasMetadata};
use std::{ use std::{
cell::UnsafeCell, cell::UnsafeCell,
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
pin::Pin,
sync::Mutex, sync::Mutex,
}; };
use thread_local::ThreadLocal; use thread_local::ThreadLocal;
use crate::{ use crate::{
emu::{Emulator, MmapPerms}, emu::{Emulator, MmapPerms},
executor::QemuExecutor,
helper::{QemuHelper, QemuHelperTuple}, helper::{QemuHelper, QemuHelperTuple},
hooks::QemuHooks,
GuestAddr, SYS_mmap, SYS_mprotect, SYS_mremap, GuestAddr, SYS_mmap, SYS_mprotect, SYS_mremap,
}; };
@ -184,19 +185,17 @@ where
I: Input, I: Input,
S: HasMetadata, S: HasMetadata,
{ {
fn init<'a, H, OT, QT>(&self, executor: &QemuExecutor<'a, H, I, OT, QT, S>) fn init_hooks<'a, QT>(&self, hooks: Pin<&QemuHooks<'a, I, QT, S>>)
where where
H: FnMut(&I) -> ExitKind,
OT: ObserversTuple<I, S>,
QT: QemuHelperTuple<I, S>, QT: QemuHelperTuple<I, S>,
{ {
executor.hook_write8_execution(trace_write8_snapshot::<I, QT, S>); hooks.write8_execution(trace_write8_snapshot::<I, QT, S>);
executor.hook_write4_execution(trace_write4_snapshot::<I, QT, S>); hooks.write4_execution(trace_write4_snapshot::<I, QT, S>);
executor.hook_write2_execution(trace_write2_snapshot::<I, QT, S>); hooks.write2_execution(trace_write2_snapshot::<I, QT, S>);
executor.hook_write1_execution(trace_write1_snapshot::<I, QT, S>); hooks.write1_execution(trace_write1_snapshot::<I, QT, S>);
executor.hook_write_n_execution(trace_write_n_snapshot::<I, QT, S>); hooks.write_n_execution(trace_write_n_snapshot::<I, QT, S>);
executor.hook_after_syscalls(trace_mmap_snapshot::<I, QT, S>); hooks.after_syscalls(trace_mmap_snapshot::<I, QT, S>);
} }
fn pre_exec(&mut self, emulator: &Emulator, _input: &I) { fn pre_exec(&mut self, emulator: &Emulator, _input: &I) {
@ -211,7 +210,7 @@ where
pub fn trace_write1_snapshot<I, QT, S>( pub fn trace_write1_snapshot<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -227,7 +226,7 @@ pub fn trace_write1_snapshot<I, QT, S>(
pub fn trace_write2_snapshot<I, QT, S>( pub fn trace_write2_snapshot<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -243,7 +242,7 @@ pub fn trace_write2_snapshot<I, QT, S>(
pub fn trace_write4_snapshot<I, QT, S>( pub fn trace_write4_snapshot<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -259,7 +258,7 @@ pub fn trace_write4_snapshot<I, QT, S>(
pub fn trace_write8_snapshot<I, QT, S>( pub fn trace_write8_snapshot<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
) where ) where
@ -275,7 +274,7 @@ pub fn trace_write8_snapshot<I, QT, S>(
pub fn trace_write_n_snapshot<I, QT, S>( pub fn trace_write_n_snapshot<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
_id: u64, _id: u64,
addr: GuestAddr, addr: GuestAddr,
size: usize, size: usize,
@ -293,7 +292,7 @@ pub fn trace_write_n_snapshot<I, QT, S>(
pub fn trace_mmap_snapshot<I, QT, S>( pub fn trace_mmap_snapshot<I, QT, S>(
_emulator: &Emulator, _emulator: &Emulator,
helpers: &mut QT, helpers: &mut QT,
_state: &mut S, _state: Option<&mut S>,
result: u64, result: u64,
sys_num: i32, sys_num: i32,
a0: u64, a0: u64,

View File

@ -34,7 +34,9 @@ use libafl::{
}; };
pub use libafl_qemu::emu::Emulator; pub use libafl_qemu::emu::Emulator;
use libafl_qemu::{cmplog, edges, QemuCmpLogHelper, QemuEdgeCoverageHelper, QemuExecutor}; use libafl_qemu::{
cmplog, edges, QemuCmpLogHelper, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks,
};
use libafl_targets::CmpLogObserver; use libafl_targets::CmpLogObserver;
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
@ -211,10 +213,17 @@ where
}; };
if self.use_cmplog.unwrap_or(false) { if self.use_cmplog.unwrap_or(false) {
let executor = QemuExecutor::new( let hooks = QemuHooks::new(
&mut harness,
emulator, emulator,
tuple_list!(QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new()), tuple_list!(
QemuEdgeCoverageHelper::default(),
QemuCmpLogHelper::default(),
),
);
let executor = QemuExecutor::new(
hooks,
&mut harness,
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,
@ -313,10 +322,12 @@ where
} }
} }
} else { } else {
let hooks =
QemuHooks::new(emulator, tuple_list!(QemuEdgeCoverageHelper::default()));
let executor = QemuExecutor::new( let executor = QemuExecutor::new(
hooks,
&mut harness, &mut harness,
emulator,
tuple_list!(QemuEdgeCoverageHelper::new()),
tuple_list!(edges_observer, time_observer), tuple_list!(edges_observer, time_observer),
&mut fuzzer, &mut fuzzer,
&mut state, &mut state,

View File

@ -37,9 +37,11 @@ __attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) {
#endif #endif
CmpLogMap* libafl_cmplog_map_ptr = &libafl_cmplog_map;
void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) { void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) {
STATIC_ASSERT(sizeof(libafl_cmplog_map.vals.operands) == sizeof(libafl_cmplog_map.vals.routines)); STATIC_ASSERT(sizeof(libafl_cmplog_map_ptr->vals.operands) == sizeof(libafl_cmplog_map_ptr->vals.routines));
__libafl_targets_cmplog(k, shape, arg1, arg2); __libafl_targets_cmplog(k, shape, arg1, arg2);
@ -106,20 +108,20 @@ void __libafl_targets_cmplog_routines(uintptr_t k, uint8_t *ptr1, uint8_t *ptr2)
uint32_t hits; uint32_t hits;
if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_RTN) { if (libafl_cmplog_map_ptr->headers[k].kind != CMPLOG_KIND_RTN) {
libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_RTN; libafl_cmplog_map_ptr->headers[k].kind = CMPLOG_KIND_RTN;
libafl_cmplog_map.headers[k].hits = 1; libafl_cmplog_map_ptr->headers[k].hits = 1;
libafl_cmplog_map.headers[k].shape = len - 1; libafl_cmplog_map_ptr->headers[k].shape = len - 1;
hits = 0; hits = 0;
} else { } else {
hits = libafl_cmplog_map.headers[k].hits++; hits = libafl_cmplog_map_ptr->headers[k].hits++;
if (libafl_cmplog_map.headers[k].shape < len) if (libafl_cmplog_map_ptr->headers[k].shape < len)
libafl_cmplog_map.headers[k].shape = len - 1; libafl_cmplog_map_ptr->headers[k].shape = len - 1;
} }
hits &= CMPLOG_MAP_RTN_H - 1; hits &= CMPLOG_MAP_RTN_H - 1;
MEMCPY(libafl_cmplog_map.vals.routines[k][hits].v0, ptr1, len); MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v0, ptr1, len);
MEMCPY(libafl_cmplog_map.vals.routines[k][hits].v1, ptr2, len); MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v1, ptr2, len);
} }

View File

@ -42,6 +42,7 @@ typedef struct CmpLogMap {
} CmpLogMap; } CmpLogMap;
extern CmpLogMap libafl_cmplog_map; extern CmpLogMap libafl_cmplog_map;
extern CmpLogMap* libafl_cmplog_map_ptr;
extern uint8_t libafl_cmplog_enabled; extern uint8_t libafl_cmplog_enabled;
@ -54,21 +55,21 @@ static inline void __libafl_targets_cmplog(uintptr_t k, uint8_t shape, uint64_t
if (!libafl_cmplog_enabled) return; if (!libafl_cmplog_enabled) return;
uint16_t hits; uint16_t hits;
if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { if (libafl_cmplog_map_ptr->headers[k].kind != CMPLOG_KIND_INS) {
libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; libafl_cmplog_map_ptr->headers[k].kind = CMPLOG_KIND_INS;
libafl_cmplog_map.headers[k].hits = 1; libafl_cmplog_map_ptr->headers[k].hits = 1;
libafl_cmplog_map.headers[k].shape = shape; libafl_cmplog_map_ptr->headers[k].shape = shape;
hits = 0; hits = 0;
} else { } else {
hits = libafl_cmplog_map.headers[k].hits++; hits = libafl_cmplog_map_ptr->headers[k].hits++;
if (libafl_cmplog_map.headers[k].shape < shape) { if (libafl_cmplog_map_ptr->headers[k].shape < shape) {
libafl_cmplog_map.headers[k].shape = shape; libafl_cmplog_map_ptr->headers[k].shape = shape;
} }
} }
hits &= CMPLOG_MAP_H - 1; hits &= CMPLOG_MAP_H - 1;
libafl_cmplog_map.vals.operands[k][hits].v0 = arg1; libafl_cmplog_map_ptr->vals.operands[k][hits].v0 = arg1;
libafl_cmplog_map.vals.operands[k][hits].v1 = arg2; libafl_cmplog_map_ptr->vals.operands[k][hits].v1 = arg2;
} }

View File

@ -33,8 +33,13 @@ pub const CMPLOG_KIND_RTN: u8 = 1;
extern "C" { extern "C" {
/// Logs an instruction for feedback during fuzzing /// Logs an instruction for feedback during fuzzing
pub fn __libafl_targets_cmplog_instructions(k: usize, shape: u8, arg1: u64, arg2: u64); pub fn __libafl_targets_cmplog_instructions(k: usize, shape: u8, arg1: u64, arg2: u64);
/// Pointer to the `CmpLog` map
pub static mut libafl_cmplog_map_ptr: *mut CmpLogMap;
} }
pub use libafl_cmplog_map_ptr as CMPLOG_MAP_PTR;
/// The header for `CmpLog` hits. /// The header for `CmpLog` hits.
#[repr(C)] #[repr(C)]
#[derive(Default, Debug, Clone, Copy)] #[derive(Default, Debug, Clone, Copy)]

View File

@ -41,7 +41,7 @@ pub use __afl_area_ptr as EDGES_MAP_PTR;
/// ///
/// # Safety /// # Safety
/// ///
/// This fn is safe to call, as long as the compilation diid not break, previously /// This fn is safe to call, as long as the compilation did not break, previously
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn autotokens() -> Result<Tokens, Error> { pub fn autotokens() -> Result<Tokens, Error> {
unsafe { unsafe {