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:
parent
86b4ff9c2f
commit
a03d733cf9
1
fuzzers/fuzzbench_fork_qemu/.gitignore
vendored
Normal file
1
fuzzers/fuzzbench_fork_qemu/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
libpng-*
|
18
fuzzers/fuzzbench_fork_qemu/Cargo.toml
Normal file
18
fuzzers/fuzzbench_fork_qemu/Cargo.toml
Normal 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"
|
43
fuzzers/fuzzbench_fork_qemu/Makefile
Normal file
43
fuzzers/fuzzbench_fork_qemu/Makefile
Normal 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) &
|
11
fuzzers/fuzzbench_fork_qemu/libfuzzer_main.c
Normal file
11
fuzzers/fuzzbench_fork_qemu/libfuzzer_main.c
Normal 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);
|
||||
|
||||
}
|
378
fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs
Normal file
378
fuzzers/fuzzbench_fork_qemu/src/fuzzer.rs
Normal 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(())
|
||||
}
|
7
fuzzers/fuzzbench_fork_qemu/src/main.rs
Normal file
7
fuzzers/fuzzbench_fork_qemu/src/main.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod fuzzer;
|
||||
|
||||
fn main() {
|
||||
#[cfg(target_os = "linux")]
|
||||
fuzzer::main()
|
||||
}
|
@ -55,6 +55,7 @@ use libafl_qemu::{
|
||||
elf::EasyElf,
|
||||
emu::Emulator,
|
||||
filter_qemu_args,
|
||||
hooks::QemuHooks,
|
||||
//snapshot::QemuSnapshotHelper,
|
||||
MmapPerms,
|
||||
QemuExecutor,
|
||||
@ -323,15 +324,19 @@ fn fuzz(
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
let executor = QemuExecutor::new(
|
||||
&mut harness,
|
||||
let hooks = QemuHooks::new(
|
||||
&emu,
|
||||
tuple_list!(
|
||||
QemuEdgeCoverageHelper::new(),
|
||||
QemuCmpLogHelper::new(),
|
||||
QemuEdgeCoverageHelper::default(),
|
||||
QemuCmpLogHelper::default(),
|
||||
//QemuAsanHelper::new(),
|
||||
//QemuSnapshotHelper::new()
|
||||
),
|
||||
);
|
||||
|
||||
let executor = QemuExecutor::new(
|
||||
hooks,
|
||||
&mut harness,
|
||||
tuple_list!(edges_observer, time_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
|
@ -42,6 +42,7 @@ use libafl_qemu::{
|
||||
//snapshot::QemuSnapshotHelper,
|
||||
MmapPerms,
|
||||
QemuExecutor,
|
||||
QemuHooks,
|
||||
Regs,
|
||||
};
|
||||
|
||||
@ -158,12 +159,12 @@ pub fn fuzz() {
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
let hooks = QemuHooks::new(&emu, tuple_list!(QemuEdgeCoverageHelper::default(),));
|
||||
|
||||
// Create a QEMU in-process executor
|
||||
let executor = QemuExecutor::new(
|
||||
hooks,
|
||||
&mut harness,
|
||||
&emu,
|
||||
// The QEMU helpers define common hooks like coverage tracking hooks
|
||||
tuple_list!(QemuEdgeCoverageHelper::new()),
|
||||
tuple_list!(edges_observer, time_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
|
@ -182,7 +182,7 @@ impl From<serde_json::Error> for Error {
|
||||
#[cfg(all(unix, feature = "std"))]
|
||||
impl From<nix::Error> for Error {
|
||||
fn from(err: nix::Error) -> Self {
|
||||
Self::Unknown(format!("{:?}", err))
|
||||
Self::Unknown(format!("Unix error: {:?}", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 std::{env, fs, ptr};
|
||||
use std::{env, fs, pin::Pin, ptr};
|
||||
|
||||
use crate::{
|
||||
emu::{Emulator, SyscallHookResult},
|
||||
executor::QemuExecutor,
|
||||
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
hooks::QemuHooks,
|
||||
GuestAddr, Regs,
|
||||
};
|
||||
|
||||
@ -172,16 +172,8 @@ pub struct QemuAsanHelper {
|
||||
|
||||
impl QemuAsanHelper {
|
||||
#[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(...)");
|
||||
Self {
|
||||
enabled: true,
|
||||
filter: QemuInstrumentationFilter::None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
filter,
|
||||
@ -410,7 +402,7 @@ impl QemuAsanHelper {
|
||||
|
||||
impl Default for QemuAsanHelper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(QemuInstrumentationFilter::None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,27 +413,25 @@ where
|
||||
{
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
//executor.hook_read_generation(gen_readwrite_asan::<I, QT, S>);
|
||||
executor.hook_read8_execution(trace_read8_asan::<I, QT, S>);
|
||||
executor.hook_read4_execution(trace_read4_asan::<I, QT, S>);
|
||||
executor.hook_read2_execution(trace_read2_asan::<I, QT, S>);
|
||||
executor.hook_read1_execution(trace_read1_asan::<I, QT, S>);
|
||||
executor.hook_read_n_execution(trace_read_n_asan::<I, QT, S>);
|
||||
//hooks.read_generation(gen_readwrite_asan::<I, QT, S>);
|
||||
hooks.read8_execution(trace_read8_asan::<I, QT, S>);
|
||||
hooks.read4_execution(trace_read4_asan::<I, QT, S>);
|
||||
hooks.read2_execution(trace_read2_asan::<I, QT, S>);
|
||||
hooks.read1_execution(trace_read1_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>);
|
||||
executor.hook_write8_execution(trace_write8_asan::<I, QT, S>);
|
||||
executor.hook_write4_execution(trace_write4_asan::<I, QT, S>);
|
||||
executor.hook_write2_execution(trace_write2_asan::<I, QT, S>);
|
||||
executor.hook_write1_execution(trace_write1_asan::<I, QT, S>);
|
||||
executor.hook_write_n_execution(trace_write_n_asan::<I, QT, S>);
|
||||
//hooks.write_generation(gen_readwrite_asan::<I, QT, S>);
|
||||
hooks.write8_execution(trace_write8_asan::<I, QT, S>);
|
||||
hooks.write4_execution(trace_write4_asan::<I, QT, S>);
|
||||
hooks.write2_execution(trace_write2_asan::<I, QT, S>);
|
||||
hooks.write1_execution(trace_write1_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) {
|
||||
@ -449,11 +439,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO add pc to generation hooks
|
||||
pub fn gen_readwrite_asan<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
pc: u64,
|
||||
_size: usize,
|
||||
) -> Option<u64>
|
||||
@ -468,11 +459,12 @@ where
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn trace_read1_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -486,7 +478,7 @@ pub fn trace_read1_asan<I, QT, S>(
|
||||
pub fn trace_read2_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -500,7 +492,7 @@ pub fn trace_read2_asan<I, QT, S>(
|
||||
pub fn trace_read4_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -514,7 +506,7 @@ pub fn trace_read4_asan<I, QT, S>(
|
||||
pub fn trace_read8_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -528,7 +520,7 @@ pub fn trace_read8_asan<I, QT, S>(
|
||||
pub fn trace_read_n_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
@ -543,7 +535,7 @@ pub fn trace_read_n_asan<I, QT, S>(
|
||||
pub fn trace_write1_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -557,7 +549,7 @@ pub fn trace_write1_asan<I, QT, S>(
|
||||
pub fn trace_write2_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -571,7 +563,7 @@ pub fn trace_write2_asan<I, QT, S>(
|
||||
pub fn trace_write4_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -585,7 +577,7 @@ pub fn trace_write4_asan<I, QT, S>(
|
||||
pub fn trace_write8_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -599,7 +591,7 @@ pub fn trace_write8_asan<I, QT, S>(
|
||||
pub fn trace_write_n_asan<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
@ -615,7 +607,7 @@ pub fn trace_write_n_asan<I, QT, S>(
|
||||
pub fn qasan_fake_syscall<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
sys_num: i32,
|
||||
a0: u64,
|
||||
a1: u64,
|
||||
|
@ -1,14 +1,16 @@
|
||||
use core::pin::Pin;
|
||||
use hashbrown::HashMap;
|
||||
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||
use libafl::{inputs::Input, state::HasMetadata};
|
||||
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 crate::{
|
||||
emu::Emulator,
|
||||
executor::QemuExecutor,
|
||||
helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
hooks::QemuHooks,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
@ -36,14 +38,7 @@ pub struct QemuCmpLogHelper {
|
||||
|
||||
impl QemuCmpLogHelper {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
filter: QemuInstrumentationFilter::None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
|
||||
pub fn new(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self { filter }
|
||||
}
|
||||
|
||||
@ -55,7 +50,7 @@ impl QemuCmpLogHelper {
|
||||
|
||||
impl Default for QemuCmpLogHelper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(QemuInstrumentationFilter::None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,17 +59,15 @@ where
|
||||
I: Input,
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
executor.hook_cmp_generation(gen_unique_cmp_ids::<I, QT, S>);
|
||||
executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
|
||||
executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
|
||||
executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
|
||||
executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
|
||||
hooks.cmp_generation(gen_unique_cmp_ids::<I, QT, S>);
|
||||
hooks.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
|
||||
hooks.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
|
||||
hooks.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
|
||||
hooks.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,14 +78,7 @@ pub struct QemuCmpLogChildHelper {
|
||||
|
||||
impl QemuCmpLogChildHelper {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
filter: QemuInstrumentationFilter::None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
|
||||
pub fn new(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self { filter }
|
||||
}
|
||||
|
||||
@ -104,7 +90,7 @@ impl QemuCmpLogChildHelper {
|
||||
|
||||
impl Default for QemuCmpLogChildHelper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(QemuInstrumentationFilter::None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,24 +101,22 @@ where
|
||||
{
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
executor.hook_cmp_generation(gen_hashed_cmp_ids::<I, QT, S>);
|
||||
executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
|
||||
executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
|
||||
executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
|
||||
executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
|
||||
hooks.cmp_generation(gen_hashed_cmp_ids::<I, QT, S>);
|
||||
hooks.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog);
|
||||
hooks.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog);
|
||||
hooks.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog);
|
||||
hooks.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_unique_cmp_ids<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
state: &mut S,
|
||||
state: Option<&mut S>,
|
||||
pc: u64,
|
||||
_size: usize,
|
||||
) -> Option<u64>
|
||||
@ -146,6 +130,7 @@ where
|
||||
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() {
|
||||
state.add_metadata(QemuCmpsMapMetadata::new());
|
||||
}
|
||||
@ -164,7 +149,7 @@ where
|
||||
pub fn gen_hashed_cmp_ids<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
pc: u64,
|
||||
_size: usize,
|
||||
) -> Option<u64>
|
||||
@ -178,7 +163,7 @@ where
|
||||
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) {
|
||||
|
@ -1,15 +1,15 @@
|
||||
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::{
|
||||
edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cell::UnsafeCell, cmp::max};
|
||||
use std::{cell::UnsafeCell, cmp::max, pin::Pin};
|
||||
|
||||
use crate::{
|
||||
emu::Emulator,
|
||||
executor::QemuExecutor,
|
||||
helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
hooks::QemuHooks,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
@ -33,19 +33,24 @@ libafl::impl_serdeany!(QemuEdgesMapMetadata);
|
||||
#[derive(Debug)]
|
||||
pub struct QemuEdgeCoverageHelper {
|
||||
filter: QemuInstrumentationFilter,
|
||||
use_hitcounts: bool,
|
||||
}
|
||||
|
||||
impl QemuEdgeCoverageHelper {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
pub fn new(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self {
|
||||
filter: QemuInstrumentationFilter::None,
|
||||
filter,
|
||||
use_hitcounts: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self { filter }
|
||||
pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
use_hitcounts: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -56,7 +61,7 @@ impl QemuEdgeCoverageHelper {
|
||||
|
||||
impl Default for QemuEdgeCoverageHelper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(QemuInstrumentationFilter::None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,35 +70,42 @@ where
|
||||
I: Input,
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>);
|
||||
executor.emulator().set_exec_edge_hook(trace_edge_hitcount);
|
||||
hooks.edge_generation(gen_unique_edge_ids::<I, QT, S>);
|
||||
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)]
|
||||
pub struct QemuEdgeCoverageChildHelper {
|
||||
filter: QemuInstrumentationFilter,
|
||||
use_hitcounts: bool,
|
||||
}
|
||||
|
||||
impl QemuEdgeCoverageChildHelper {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
pub fn new(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self {
|
||||
filter: QemuInstrumentationFilter::None,
|
||||
filter,
|
||||
use_hitcounts: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_instrumentation_filter(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self { filter }
|
||||
pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self {
|
||||
Self {
|
||||
filter,
|
||||
use_hitcounts: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@ -104,7 +116,7 @@ impl QemuEdgeCoverageChildHelper {
|
||||
|
||||
impl Default for QemuEdgeCoverageChildHelper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(QemuInstrumentationFilter::None)
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,14 +127,16 @@ where
|
||||
{
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
executor.hook_edge_generation(gen_unique_edge_ids::<I, QT, S>);
|
||||
executor.emulator().set_exec_edge_hook(trace_edge_hitcount);
|
||||
hooks.edge_generation(gen_hashed_edge_ids::<I, QT, S>);
|
||||
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>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
state: &mut S,
|
||||
state: Option<&mut S>,
|
||||
src: u64,
|
||||
dest: u64,
|
||||
) -> Option<u64>
|
||||
@ -145,6 +159,7 @@ where
|
||||
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() {
|
||||
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) {
|
||||
unsafe {
|
||||
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>(
|
||||
_emulator: &Emulator,
|
||||
_helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
pc: u64,
|
||||
) -> Option<u64> {
|
||||
Some(pc)
|
||||
@ -217,7 +246,7 @@ pub fn gen_addr_block_ids<I, QT, S>(
|
||||
pub fn gen_hashed_block_ids<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
_helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
pc: u64,
|
||||
) -> Option<u64> {
|
||||
Some(hash_me(pc))
|
||||
|
@ -12,11 +12,11 @@ use num_traits::Num;
|
||||
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
#[cfg(not(any(feature = "x86_64", feature = "aarch64")))]
|
||||
#[cfg(not(any(cpu_target = "x86_64", cpu_target = "aarch64")))]
|
||||
/// `GuestAddr` is u32 for 32-bit targets
|
||||
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
|
||||
pub type GuestAddr = u64;
|
||||
|
||||
@ -317,7 +317,10 @@ impl Emulator {
|
||||
#[allow(clippy::must_use_candidate, clippy::similar_names)]
|
||||
pub fn new(args: &[String], env: &[(String, String)]) -> Emulator {
|
||||
unsafe {
|
||||
assert!(!EMULATOR_IS_INITIALIZED);
|
||||
assert!(
|
||||
!EMULATOR_IS_INITIALIZED,
|
||||
"Only an instance of Emulator is permitted"
|
||||
);
|
||||
}
|
||||
assert!(!args.is_empty());
|
||||
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect();
|
||||
|
@ -1,16 +1,13 @@
|
||||
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
|
||||
use core::{
|
||||
ffi::c_void,
|
||||
fmt::{self, Debug, Formatter},
|
||||
mem::transmute,
|
||||
ptr::{self, addr_of},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
bolts::shmem::ShMemProvider,
|
||||
events::{EventFirer, EventRestarter},
|
||||
executors::{
|
||||
inprocess::inprocess_get_state, Executor, ExitKind, HasObservers, InProcessExecutor,
|
||||
},
|
||||
executors::{Executor, ExitKind, HasObservers, InProcessExecutor, InProcessForkExecutor},
|
||||
feedbacks::Feedback,
|
||||
fuzzer::HasObjective,
|
||||
inputs::Input,
|
||||
@ -20,444 +17,7 @@ use libafl::{
|
||||
};
|
||||
|
||||
pub use crate::emu::SyscallHookResult;
|
||||
use crate::{
|
||||
emu::{Emulator, SKIP_EXEC_HOOK},
|
||||
helper::QemuHelperTuple,
|
||||
GuestAddr,
|
||||
};
|
||||
|
||||
static mut QEMU_HELPERS_PTR: *const c_void = ptr::null();
|
||||
|
||||
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
|
||||
}
|
||||
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
|
||||
|
||||
pub struct QemuExecutor<'a, H, I, OT, QT, S>
|
||||
where
|
||||
@ -466,8 +26,7 @@ where
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
helpers: QT,
|
||||
emulator: &'a Emulator,
|
||||
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
|
||||
inner: InProcessExecutor<'a, H, I, OT, S>,
|
||||
}
|
||||
|
||||
@ -480,8 +39,7 @@ where
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("QemuExecutor")
|
||||
.field("helpers", &self.helpers)
|
||||
.field("emulator", &self.emulator)
|
||||
.field("hooks", &self.hooks)
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
@ -495,9 +53,8 @@ where
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
pub fn new<EM, OF, Z>(
|
||||
hooks: Pin<Box<QemuHooks<'a, I, QT, S>>>,
|
||||
harness_fn: &'a mut H,
|
||||
emulator: &'a Emulator,
|
||||
helpers: QT,
|
||||
observers: OT,
|
||||
fuzzer: &mut Z,
|
||||
state: &mut S,
|
||||
@ -509,13 +66,10 @@ where
|
||||
S: HasSolutions<I> + HasClientPerfMonitor,
|
||||
Z: HasObjective<I, OF, S>,
|
||||
{
|
||||
let slf = Self {
|
||||
helpers,
|
||||
emulator,
|
||||
Ok(Self {
|
||||
hooks,
|
||||
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> {
|
||||
@ -526,277 +80,16 @@ where
|
||||
&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.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>);
|
||||
self.hooks.emulator()
|
||||
}
|
||||
}
|
||||
|
||||
@ -814,11 +107,22 @@ where
|
||||
mgr: &mut EM,
|
||||
input: &I,
|
||||
) -> Result<ExitKind, Error> {
|
||||
unsafe { QEMU_HELPERS_PTR = addr_of!(self.helpers) as *const c_void };
|
||||
self.helpers.pre_exec_all(self.emulator, input);
|
||||
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);
|
||||
self.helpers.post_exec_all(self.emulator, input);
|
||||
unsafe { QEMU_HELPERS_PTR = ptr::null() };
|
||||
unsafe {
|
||||
self.hooks
|
||||
.as_mut()
|
||||
.get_unchecked_mut()
|
||||
.helpers_mut()
|
||||
.post_exec_all(&emu, input);
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
@ -840,3 +144,145 @@ where
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use core::{fmt::Debug, ops::Range};
|
||||
use libafl::{
|
||||
bolts::tuples::MatchFirstType, executors::ExitKind, inputs::Input, observers::ObserversTuple,
|
||||
};
|
||||
use core::{fmt::Debug, ops::Range, pin::Pin};
|
||||
use libafl::{bolts::tuples::MatchFirstType, inputs::Input};
|
||||
|
||||
use crate::{emu::Emulator, executor::QemuExecutor};
|
||||
use crate::{emu::Emulator, hooks::QemuHooks};
|
||||
|
||||
/// A helper for `libafl_qemu`.
|
||||
// TODO remove 'static when specialization will be stable
|
||||
@ -13,10 +11,8 @@ where
|
||||
{
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
}
|
||||
@ -30,10 +26,10 @@ pub trait QemuHelperTuple<I, S>: MatchFirstType + Debug
|
||||
where
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>;
|
||||
|
||||
fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I);
|
||||
@ -45,10 +41,10 @@ impl<I, S> QemuHelperTuple<I, S> for ()
|
||||
where
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
}
|
||||
@ -64,14 +60,14 @@ where
|
||||
Tail: QemuHelperTuple<I, S>,
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
self.0.init(executor);
|
||||
self.1.init_all(executor);
|
||||
self.0.init_hooks(hooks);
|
||||
self.1.init_hooks_all(hooks);
|
||||
}
|
||||
|
||||
fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) {
|
||||
|
860
libafl_qemu/src/hooks.rs
Normal file
860
libafl_qemu/src/hooks.rs
Normal 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>);
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
// This lint triggers too often on the current GuestAddr type when emulating 64-bit targets because
|
||||
// u64::from(GuestAddr) is a no-op, but the .into() call is needed when GuestAddr is u32.
|
||||
#![cfg_attr(
|
||||
any(feature = "x86_64", feature = "aarch64"),
|
||||
any(cpu_target = "x86_64", cpu_target = "aarch64"),
|
||||
allow(clippy::useless_conversion)
|
||||
)]
|
||||
#![allow(clippy::needless_pass_by_value)]
|
||||
|
||||
use std::env;
|
||||
|
||||
@ -33,6 +34,10 @@ pub mod elf;
|
||||
pub mod helper;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use helper::*;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod hooks;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use hooks::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod edges;
|
||||
@ -54,7 +59,7 @@ pub use asan::{init_with_asan, QemuAsanHelper};
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod executor;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use executor::QemuExecutor;
|
||||
pub use executor::{QemuExecutor, QemuForkExecutor};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod emu;
|
||||
|
@ -1,16 +1,17 @@
|
||||
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::{
|
||||
cell::UnsafeCell,
|
||||
collections::{HashMap, HashSet},
|
||||
pin::Pin,
|
||||
sync::Mutex,
|
||||
};
|
||||
use thread_local::ThreadLocal;
|
||||
|
||||
use crate::{
|
||||
emu::{Emulator, MmapPerms},
|
||||
executor::QemuExecutor,
|
||||
helper::{QemuHelper, QemuHelperTuple},
|
||||
hooks::QemuHooks,
|
||||
GuestAddr, SYS_mmap, SYS_mprotect, SYS_mremap,
|
||||
};
|
||||
|
||||
@ -184,19 +185,17 @@ where
|
||||
I: Input,
|
||||
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
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
executor.hook_write8_execution(trace_write8_snapshot::<I, QT, S>);
|
||||
executor.hook_write4_execution(trace_write4_snapshot::<I, QT, S>);
|
||||
executor.hook_write2_execution(trace_write2_snapshot::<I, QT, S>);
|
||||
executor.hook_write1_execution(trace_write1_snapshot::<I, QT, S>);
|
||||
executor.hook_write_n_execution(trace_write_n_snapshot::<I, QT, S>);
|
||||
hooks.write8_execution(trace_write8_snapshot::<I, QT, S>);
|
||||
hooks.write4_execution(trace_write4_snapshot::<I, QT, S>);
|
||||
hooks.write2_execution(trace_write2_snapshot::<I, QT, S>);
|
||||
hooks.write1_execution(trace_write1_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) {
|
||||
@ -211,7 +210,7 @@ where
|
||||
pub fn trace_write1_snapshot<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -227,7 +226,7 @@ pub fn trace_write1_snapshot<I, QT, S>(
|
||||
pub fn trace_write2_snapshot<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -243,7 +242,7 @@ pub fn trace_write2_snapshot<I, QT, S>(
|
||||
pub fn trace_write4_snapshot<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -259,7 +258,7 @@ pub fn trace_write4_snapshot<I, QT, S>(
|
||||
pub fn trace_write8_snapshot<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
) where
|
||||
@ -275,7 +274,7 @@ pub fn trace_write8_snapshot<I, QT, S>(
|
||||
pub fn trace_write_n_snapshot<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
_id: u64,
|
||||
addr: GuestAddr,
|
||||
size: usize,
|
||||
@ -293,7 +292,7 @@ pub fn trace_write_n_snapshot<I, QT, S>(
|
||||
pub fn trace_mmap_snapshot<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
_state: Option<&mut S>,
|
||||
result: u64,
|
||||
sys_num: i32,
|
||||
a0: u64,
|
||||
|
@ -34,7 +34,9 @@ use libafl::{
|
||||
};
|
||||
|
||||
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 crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
|
||||
@ -211,10 +213,17 @@ where
|
||||
};
|
||||
|
||||
if self.use_cmplog.unwrap_or(false) {
|
||||
let executor = QemuExecutor::new(
|
||||
&mut harness,
|
||||
let hooks = QemuHooks::new(
|
||||
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),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
@ -313,10 +322,12 @@ where
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let hooks =
|
||||
QemuHooks::new(emulator, tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||
|
||||
let executor = QemuExecutor::new(
|
||||
hooks,
|
||||
&mut harness,
|
||||
emulator,
|
||||
tuple_list!(QemuEdgeCoverageHelper::new()),
|
||||
tuple_list!(edges_observer, time_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
|
@ -37,9 +37,11 @@ __attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) {
|
||||
|
||||
#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) {
|
||||
|
||||
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);
|
||||
|
||||
@ -106,20 +108,20 @@ void __libafl_targets_cmplog_routines(uintptr_t k, uint8_t *ptr1, uint8_t *ptr2)
|
||||
|
||||
uint32_t hits;
|
||||
|
||||
if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_RTN) {
|
||||
libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_RTN;
|
||||
libafl_cmplog_map.headers[k].hits = 1;
|
||||
libafl_cmplog_map.headers[k].shape = len - 1;
|
||||
if (libafl_cmplog_map_ptr->headers[k].kind != CMPLOG_KIND_RTN) {
|
||||
libafl_cmplog_map_ptr->headers[k].kind = CMPLOG_KIND_RTN;
|
||||
libafl_cmplog_map_ptr->headers[k].hits = 1;
|
||||
libafl_cmplog_map_ptr->headers[k].shape = len - 1;
|
||||
hits = 0;
|
||||
} else {
|
||||
hits = libafl_cmplog_map.headers[k].hits++;
|
||||
if (libafl_cmplog_map.headers[k].shape < len)
|
||||
libafl_cmplog_map.headers[k].shape = len - 1;
|
||||
hits = libafl_cmplog_map_ptr->headers[k].hits++;
|
||||
if (libafl_cmplog_map_ptr->headers[k].shape < len)
|
||||
libafl_cmplog_map_ptr->headers[k].shape = len - 1;
|
||||
}
|
||||
|
||||
hits &= CMPLOG_MAP_RTN_H - 1;
|
||||
MEMCPY(libafl_cmplog_map.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].v0, ptr1, len);
|
||||
MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v1, ptr2, len);
|
||||
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@ typedef struct CmpLogMap {
|
||||
} CmpLogMap;
|
||||
|
||||
extern CmpLogMap libafl_cmplog_map;
|
||||
extern CmpLogMap* libafl_cmplog_map_ptr;
|
||||
|
||||
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;
|
||||
|
||||
uint16_t hits;
|
||||
if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) {
|
||||
libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS;
|
||||
libafl_cmplog_map.headers[k].hits = 1;
|
||||
libafl_cmplog_map.headers[k].shape = shape;
|
||||
if (libafl_cmplog_map_ptr->headers[k].kind != CMPLOG_KIND_INS) {
|
||||
libafl_cmplog_map_ptr->headers[k].kind = CMPLOG_KIND_INS;
|
||||
libafl_cmplog_map_ptr->headers[k].hits = 1;
|
||||
libafl_cmplog_map_ptr->headers[k].shape = shape;
|
||||
hits = 0;
|
||||
} else {
|
||||
hits = libafl_cmplog_map.headers[k].hits++;
|
||||
if (libafl_cmplog_map.headers[k].shape < shape) {
|
||||
libafl_cmplog_map.headers[k].shape = shape;
|
||||
hits = libafl_cmplog_map_ptr->headers[k].hits++;
|
||||
if (libafl_cmplog_map_ptr->headers[k].shape < shape) {
|
||||
libafl_cmplog_map_ptr->headers[k].shape = shape;
|
||||
}
|
||||
}
|
||||
|
||||
hits &= CMPLOG_MAP_H - 1;
|
||||
libafl_cmplog_map.vals.operands[k][hits].v0 = arg1;
|
||||
libafl_cmplog_map.vals.operands[k][hits].v1 = arg2;
|
||||
libafl_cmplog_map_ptr->vals.operands[k][hits].v0 = arg1;
|
||||
libafl_cmplog_map_ptr->vals.operands[k][hits].v1 = arg2;
|
||||
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,13 @@ pub const CMPLOG_KIND_RTN: u8 = 1;
|
||||
extern "C" {
|
||||
/// Logs an instruction for feedback during fuzzing
|
||||
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.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
|
@ -41,7 +41,7 @@ pub use __afl_area_ptr as EDGES_MAP_PTR;
|
||||
///
|
||||
/// # 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")]
|
||||
pub fn autotokens() -> Result<Tokens, Error> {
|
||||
unsafe {
|
||||
|
Loading…
x
Reference in New Issue
Block a user