libafl_qemu (#211)

* empty libafl_qemu crate

* fuzzbench qemu fuzzer skeleton

* emu.run() works without bp

* working emu loop

* resolve elf symbols

* running Qemu fuzzer without coverage

* qemu fuzzer with edge coverage

* merge into inprocess::GLOBAL_STATE

* create QemuExecutor and remove QemuEmulator

* qemu hooks and persist edges mapping storing them in State

* windows fix

* add libafl_qemu to workspace

* windows fix

* some clippy

* clippy

* fix fuzzbench_qemu

* fix fuzzbench_qemu makefile

* fuck you macos
This commit is contained in:
Andrea Fioraldi 2021-07-08 15:21:14 +02:00 committed by GitHub
parent d7dad357e2
commit d472a1242a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1198 additions and 146 deletions

View File

@ -11,6 +11,7 @@ members = [
"libafl_cc",
"libafl_targets",
"libafl_frida",
"libafl_qemu",
"libafl_tests",
]
default-members = [

View File

@ -34,7 +34,7 @@ use libafl::{
token_mutations::I2SRandReplace,
tokens_mutations, Tokens,
},
observers::{StdMapObserver, TimeObserver},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,

2
fuzzers/fuzzbench_qemu/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/
qemu-libafl-bridge/

View File

@ -0,0 +1,30 @@
[package]
name = "fuzzbench_qemu"
version = "0.5.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"
[features]
default = ["std"]
std = []
[profile.release]
lto = true
codegen-units = 1
opt-level = 3
debug = true
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
which = { version = "4.0.2" }
num_cpus = "1.0"
[dependencies]
libafl = { path = "../../libafl/" }
libafl_qemu = { path = "../../libafl_qemu/" }
clap = { version = "3.0.0-beta.2", features = ["default"] }
nix = "0.20.0"
[lib]
name = "fuzzbench_qemu"
crate-type = ["staticlib"]

View File

@ -0,0 +1,61 @@
FUZZER_NAME=fuzzbench_qemu
PROJECT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
ifeq ($(strip $(CPU_TARGET)),)
CPU_TARGET=$(shell uname -m)
ifeq ($(strip $(CPU_TARGET)),i686)
CPU_TARGET=i386
else ifeq ($(strip $(CPU_TARGET)),arm64v8)
CPU_TARGET=aarch64
endif
endif
ifeq ($(strip $(BUILD_TARGET)),)
BUILD_TARGET=release
endif
ifeq ($(strip $(DEBUG)),1)
BUILD_TARGET=debug
endif
ifeq ($(strip $(BUILD_TARGET)),release)
CARGO_ARGS+= --release
endif
UNAME := $(shell uname)
.PHONY: clean short_test all
all: build/qemu-$(CPU_TARGET)
target/$(BUILD_TARGET)/lib$(FUZZER_NAME).a: src/*
cargo build $(CARGO_ARGS)
qemu-libafl-bridge:
git clone git@github.com:AFLplusplus/qemu-libafl-bridge.git
build/config.status: qemu-libafl-bridge qemu-libafl-bridge/configure
mkdir -p build
cd build && ../qemu-libafl-bridge/configure --target-list=$(CPU_TARGET)-linux-user --with-libafl-bridge="$(PROJECT_DIR)/target/$(BUILD_TARGET)/lib$(FUZZER_NAME).a"
build/qemu-$(CPU_TARGET): target/$(BUILD_TARGET)/lib$(FUZZER_NAME).a build/config.status
$(MAKE) -C build
pull: qemu-libafl-bridge
cd qemu-libafl-bridge && git pull
clean:
rm -rf build
cargo clean
ifeq ($(UNAME), Linux)
short_test: target/$(BUILD_TARGET)/lib$(FUZZER_NAME).a
@echo "Skipping short test"
else
short_test:
@echo "Skipping build and short test"
endif

View File

@ -0,0 +1,17 @@
# Fuzzbench Harness
This folder contains an example fuzzer tailored for fuzzbench.
It uses the best possible setting, with the exception of a SimpleRestartingEventManager instead of an LlmpEventManager - since fuzzbench is single threaded.
Real fuzz campaigns should consider using multithreaded LlmpEventManager, see the other examples.
## Build
To build this example, run `cargo build --release`.
This will build the fuzzer compilers (`libafl_cc` and `libafl_cpp`) with `src/lib.rs` as fuzzer.
The fuzzer uses the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback.
These can then be used to build libfuzzer harnesses in the software project of your choice.
Finally, just run the resulting binary with `out_dir`, `in_dir`.
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).

View File

@ -0,0 +1,16 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// printf("Got %ld bytes.\n", Size);
if (Size >= 4 && *(uint16_t*)Data == 0xaabb && *(uint16_t*)&Data[2] == 0xccab)
abort();
}
int main() {
char buf [10] = {0};
LLVMFuzzerTestOneInput(buf, 10);
}

View File

@ -0,0 +1,342 @@
//! A singlethreaded QEMU fuzzer that can auto-restart.
use clap::{App, Arg};
use core::{cell::RefCell, time::Duration};
#[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},
},
corpus::{Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler},
events::SimpleRestartingEventManager,
executors::{ExitKind, TimeoutExecutor},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
mutators::{
scheduled::{havoc_mutations, StdScheduledMutator},
tokens_mutations, Tokens,
},
observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver},
stages::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
Error,
};
use libafl_qemu::{
amd64::Amd64Regs, elf::EasyElf, emu, filter_qemu_args, hooks, MmapPerms, QemuExecutor,
};
/// The fuzzer main (as `no_mangle` C function)
#[no_mangle]
pub fn libafl_qemu_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")
.about("The directory to place finds in ('corpus')")
.long("libafl-out")
.required(true)
.takes_value(true),
)
.arg(
Arg::new("in")
.about("The directory to read initial inputs from ('seeds')")
.long("libafl-in")
.required(true)
.takes_value(true),
)
.arg(
Arg::new("tokens")
.long("libafl-tokens")
.about("A file to read tokens from, to be used during fuzzing")
.takes_value(true),
)
.arg(
Arg::new("logfile")
.long("libafl-logfile")
.about("Duplicates all output to this file")
.default_value("libafl.log"),
)
.arg(
Arg::new("timeout")
.long("libafl-timeout")
.about("Timeout for each individual execution, in milliseconds")
.default_value("1000"),
)
.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());
let timeout = Duration::from_millis(
res.value_of("timeout")
.unwrap()
.to_string()
.parse()
.expect("Could not parse timeout in milliseconds"),
);
fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout)
.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,
timeout: Duration,
) -> Result<(), Error> {
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").unwrap();
println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr);
emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
emu::run();
println!(
"Break at {:#x}",
emu::read_reg::<_, u64>(Amd64Regs::Rip).unwrap()
);
let stack_ptr: u64 = emu::read_reg(Amd64Regs::Rsp).unwrap();
let mut ret_addr = [0u64];
emu::read_mem(stack_ptr, &mut ret_addr);
let ret_addr = ret_addr[0];
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 stats = SimpleStats::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 (state, mut mgr) = match SimpleRestartingEventManager::launch(stats, &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 = unsafe { &mut hooks::EDGES_MAP };
let edges_counter = unsafe { &mut hooks::MAX_EDGES_NUM };
let edges_observer =
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// 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 = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::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),
)
});
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::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();
if buf.len() > 32 {
buf = &buf[0..32];
}
emu::write_mem(input_addr, buf);
emu::write_reg(Amd64Regs::Rdi, input_addr).unwrap();
emu::write_reg(Amd64Regs::Rsi, buf.len()).unwrap();
emu::write_reg(Amd64Regs::Rip, test_one_input_ptr).unwrap();
emu::write_reg(Amd64Regs::Rsp, stack_ptr).unwrap();
emu::run();
ExitKind::Ok
};
let executor = QemuExecutor::new(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)?;
executor.hook_edge_generation(hooks::gen_unique_edges_id);
executor.hook_edge_execution(hooks::exec_log_hitcount);
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = TimeoutExecutor::new(executor, timeout);
// Read tokens
if let Some(tokenfile) = tokenfile {
if state.metadata().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from_tokens_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());
}
// Setup a mutational stage with a basic bytes mutator
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
// Remove target ouput (logs still survive)
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();
dup2(null_fd, io::stdout().as_raw_fd())?;
dup2(null_fd, io::stderr().as_raw_fd())?;
}
// reopen file to make sure we're at the end
log.replace(
OpenOptions::new()
.append(true)
.create(true)
.open(&logfile)?,
);
fuzzer
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop");
// Never reached
Ok(())
}

View File

@ -0,0 +1,2 @@
#[cfg(target_os = "linux")]
pub mod fuzzer;

View File

@ -15,11 +15,6 @@ use crate::bolts::os::unix_signals::setup_signal_handler;
#[cfg(all(windows, feature = "std"))]
use crate::bolts::os::windows_exceptions::setup_exception_handler;
#[cfg(unix)]
pub use unix_signal_handler::{nop_handler, HandlerFuncPtr};
#[cfg(all(windows, feature = "std"))]
pub use windows_exception_handler::{nop_handler, HandlerFuncPtr};
use crate::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
@ -44,11 +39,9 @@ where
/// The observers, observing each run
observers: OT,
/// On crash C function pointer
#[cfg(any(unix, all(windows, feature = "std")))]
crash_handler: HandlerFuncPtr,
crash_handler: *const c_void,
/// On timeout C function pointer
#[cfg(unix)]
timeout_handler: HandlerFuncPtr,
timeout_handler: *const c_void,
phantom: PhantomData<(I, S)>,
}
@ -68,7 +61,7 @@ where
) -> Result<ExitKind, Error> {
#[cfg(unix)]
unsafe {
let data = &mut unix_signal_handler::GLOBAL_STATE;
let data = &mut GLOBAL_STATE;
write_volatile(
&mut data.current_input_ptr,
input as *const _ as *const c_void,
@ -88,7 +81,7 @@ where
}
#[cfg(all(windows, feature = "std"))]
unsafe {
let data = &mut windows_exception_handler::GLOBAL_STATE;
let data = &mut GLOBAL_STATE;
write_volatile(
&mut data.current_input_ptr,
input as *const _ as *const c_void,
@ -111,18 +104,12 @@ where
#[cfg(unix)]
unsafe {
write_volatile(
&mut unix_signal_handler::GLOBAL_STATE.current_input_ptr,
ptr::null(),
);
write_volatile(&mut GLOBAL_STATE.current_input_ptr, ptr::null());
compiler_fence(Ordering::SeqCst);
}
#[cfg(all(windows, feature = "std"))]
unsafe {
write_volatile(
&mut windows_exception_handler::GLOBAL_STATE.current_input_ptr,
ptr::null(),
);
write_volatile(&mut GLOBAL_STATE.current_input_ptr, ptr::null());
compiler_fence(Ordering::SeqCst);
}
@ -175,14 +162,15 @@ where
{
#[cfg(unix)]
unsafe {
let data = &mut unix_signal_handler::GLOBAL_STATE;
let data = &mut GLOBAL_STATE;
setup_signal_handler(data)?;
compiler_fence(Ordering::SeqCst);
Ok(Self {
harness_fn,
observers,
crash_handler: unix_signal_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S, Z>,
crash_handler: unix_signal_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S, Z>
as *const _,
timeout_handler: unix_signal_handler::inproc_timeout_handler::<
EM,
I,
@ -191,13 +179,13 @@ where
OT,
S,
Z,
>,
> as *const _,
phantom: PhantomData,
})
}
#[cfg(all(windows, feature = "std"))]
unsafe {
let data = &mut windows_exception_handler::GLOBAL_STATE;
let data = &mut GLOBAL_STATE;
setup_exception_handler(data)?;
compiler_fence(Ordering::SeqCst);
@ -212,8 +200,9 @@ where
OT,
S,
Z,
>,
// timeout_handler: windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S, Z>,
> as *const _,
// timeout_handler: windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S, Z> as *const _,
timeout_handler: ptr::null(),
phantom: PhantomData,
})
}
@ -238,33 +227,21 @@ where
}
}
#[cfg(unix)]
mod unix_signal_handler {
use alloc::vec::Vec;
use core::ptr;
use libc::{c_void, siginfo_t, ucontext_t};
#[cfg(feature = "std")]
use std::io::{stdout, Write};
pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub fuzzer_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: *const c_void,
pub timeout_handler: *const c_void,
}
use crate::{
bolts::os::unix_signals::{Handler, Signal},
corpus::{Corpus, Testcase},
events::{Event, EventFirer, EventRestarter},
executors::ExitKind,
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasClientPerfStats, HasSolutions},
};
unsafe impl Send for InProcessExecutorHandlerData {}
unsafe impl Sync for InProcessExecutorHandlerData {}
pub type HandlerFuncPtr =
unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut InProcessExecutorHandlerData);
// TODO merge GLOBAL_STATE with the Windows one
/// Signal handling on unix systems needs some nasty unsafe.
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
/// Exception handling needs some nasty unsafe.
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
/// The state ptr for signal handling
state_ptr: ptr::null_mut(),
/// The event manager ptr for signal handling
@ -276,32 +253,45 @@ mod unix_signal_handler {
/// The current input for signal handling
current_input_ptr: ptr::null(),
/// The crash handler fn
crash_handler: nop_handler,
crash_handler: ptr::null(),
/// The timeout handler fn
timeout_handler: nop_handler,
timeout_handler: ptr::null(),
};
#[cfg(unix)]
mod unix_signal_handler {
use alloc::vec::Vec;
use core::{mem::transmute, ptr};
use libc::{siginfo_t, ucontext_t};
#[cfg(feature = "std")]
use std::io::{stdout, Write};
use crate::{
bolts::os::unix_signals::{Handler, Signal},
corpus::{Corpus, Testcase},
events::{Event, EventFirer, EventRestarter},
executors::{
inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE},
ExitKind,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasClientPerfStats, HasSolutions},
};
pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub fuzzer_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: HandlerFuncPtr,
pub timeout_handler: HandlerFuncPtr,
}
unsafe impl Send for InProcessExecutorHandlerData {}
unsafe impl Sync for InProcessExecutorHandlerData {}
pub type HandlerFuncPtr =
unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut InProcessExecutorHandlerData);
/// A handler that does nothing.
pub fn nop_handler(
/*pub fn nop_handler(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
_data: &mut InProcessExecutorHandlerData,
) {
}
}*/
#[cfg(unix)]
impl Handler for InProcessExecutorHandlerData {
@ -310,9 +300,17 @@ mod unix_signal_handler {
let data = &mut GLOBAL_STATE;
match signal {
Signal::SigUser2 | Signal::SigAlarm => {
(data.timeout_handler)(signal, info, context, data);
if !data.timeout_handler.is_null() {
let func: HandlerFuncPtr = transmute(data.timeout_handler);
(func)(signal, info, context, data);
}
}
_ => {
if !data.crash_handler.is_null() {
let func: HandlerFuncPtr = transmute(data.crash_handler);
(func)(signal, info, context, data);
}
}
_ => (data.crash_handler)(signal, info, context, data),
}
}
}
@ -544,7 +542,7 @@ mod unix_signal_handler {
#[cfg(all(windows, feature = "std"))]
mod windows_exception_handler {
use alloc::vec::Vec;
use core::{ffi::c_void, ptr};
use core::{mem::transmute, ptr};
#[cfg(feature = "std")]
use std::io::{stdout, Write};
@ -557,7 +555,10 @@ mod windows_exception_handler {
},
corpus::{Corpus, Testcase},
events::{Event, EventFirer, EventRestarter},
executors::ExitKind,
executors::{
inprocess::{InProcessExecutorHandlerData, GLOBAL_STATE},
ExitKind,
},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
@ -568,49 +569,22 @@ mod windows_exception_handler {
pub type HandlerFuncPtr =
unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut InProcessExecutorHandlerData);
/// Signal handling on unix systems needs some nasty unsafe.
pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData {
/// The state ptr for signal handling
state_ptr: ptr::null_mut(),
/// The event manager ptr for signal handling
event_mgr_ptr: ptr::null_mut(),
/// The fuzzer ptr for signal handling
fuzzer_ptr: ptr::null_mut(),
/// The observers ptr for signal handling
observers_ptr: ptr::null(),
/// The current input for signal handling
current_input_ptr: ptr::null(),
/// The crash handler fn
crash_handler: nop_handler,
// The timeout handler fn
//timeout_handler: nop_handler,
};
pub struct InProcessExecutorHandlerData {
pub state_ptr: *mut c_void,
pub event_mgr_ptr: *mut c_void,
pub fuzzer_ptr: *mut c_void,
pub observers_ptr: *const c_void,
pub current_input_ptr: *const c_void,
pub crash_handler: HandlerFuncPtr,
//pub timeout_handler: HandlerFuncPtr,
}
unsafe impl Send for InProcessExecutorHandlerData {}
unsafe impl Sync for InProcessExecutorHandlerData {}
pub unsafe fn nop_handler(
/*pub unsafe fn nop_handler(
_code: ExceptionCode,
_exception_pointers: *mut EXCEPTION_POINTERS,
_data: &mut InProcessExecutorHandlerData,
) {
}
}*/
impl Handler for InProcessExecutorHandlerData {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn handle(&mut self, code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) {
unsafe {
let data = &mut GLOBAL_STATE;
(data.crash_handler)(code, exception_pointers, data)
if !data.crash_handler.is_null() {
let func: HandlerFuncPtr = transmute(data.crash_handler);
(func)(code, exception_pointers, data);
}
}
}
@ -718,11 +692,11 @@ mod windows_exception_handler {
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use core::{marker::PhantomData, ptr};
use crate::{
bolts::tuples::tuple_list,
executors::{inprocess, Executor, ExitKind, InProcessExecutor},
executors::{Executor, ExitKind, InProcessExecutor},
inputs::NopInput,
};
@ -733,10 +707,8 @@ mod tests {
let mut in_process_executor = InProcessExecutor::<_, NopInput, (), ()> {
harness_fn: &mut harness,
observers: tuple_list!(),
#[cfg(any(unix, all(windows, feature = "std")))]
crash_handler: inprocess::nop_handler,
#[cfg(unix)]
timeout_handler: inprocess::nop_handler,
crash_handler: ptr::null(),
timeout_handler: ptr::null(),
phantom: PhantomData,
};
let input = NopInput {};

View File

@ -14,7 +14,7 @@ pub mod combined;
pub use combined::CombinedExecutor;
pub mod shadow;
pub use shadow::{HasShadowObserverHooks, ShadowExecutor};
pub use shadow::ShadowExecutor;
use crate::{
inputs::{HasTargetBytes, Input},

View File

@ -9,26 +9,6 @@ use crate::{
Error,
};
pub trait HasShadowObserverHooks<EM, I, S, SOT, Z> {
/// Run the pre exec hook for all the shadow [`crate::observers::Observer`]`s`
fn pre_exec_shadow_observers(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error>;
/// Run the post exec hook for all the shadow [`crate::observers::Observer`]`s`
fn post_exec_shadow_observers(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<(), Error>;
}
/// A [`ShadowExecutor`] wraps an executor and a set of shadow observers
pub struct ShadowExecutor<E, I, S, SOT> {
executor: E,

View File

@ -467,6 +467,61 @@ where
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
fn generate_initial_internal<G, E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
num: usize,
forced: bool,
) -> Result<(), Error>
where
G: Generator<I, R>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self.rand_mut())?;
if forced {
let _ = fuzzer.add_input(self, executor, manager, input)?;
added += 1;
} else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
if res != ExecuteInputResult::None {
added += 1;
}
}
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} over {} initial testcases", added, num),
phantom: PhantomData,
},
)?;
Ok(())
}
/// Generate `num` initial inputs, using the passed-in generator and force the addition to corpus.
pub fn generate_initial_inputs_forced<G, E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
num: usize,
) -> Result<(), Error>
where
G: Generator<I, R>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, true)
}
/// Generate `num` initial inputs, using the passed-in generator.
pub fn generate_initial_inputs<G, E, EM, Z>(
&mut self,
@ -481,23 +536,7 @@ where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self.rand_mut())?;
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
if res != ExecuteInputResult::None {
added += 1;
}
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} over {} initial testcases", added, num),
phantom: PhantomData,
},
)?;
Ok(())
self.generate_initial_internal(fuzzer, executor, generator, manager, num, false)
}
/// Creates a new `State`, taking ownership of all of the individual components during fuzzing.

27
libafl_qemu/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "libafl_qemu"
version = "0.5.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "QEMU user backend library for LibAFL"
documentation = "https://docs.rs/libafl_qemu"
repository = "https://github.com/AFLplusplus/LibAFL/"
readme = "../README.md"
license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "qemu", "instrumentation"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "../libafl", version = "0.5.0" }
libafl_targets = { path = "../libafl_targets", version = "0.5.0" }
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
num = "0.4"
num_enum = "0.5.1"
goblin = "0.4.2"
libc = "0.2.97"
[build-dependencies]
cc = { version = "1.0" }

21
libafl_qemu/build.rs Normal file
View File

@ -0,0 +1,21 @@
use std::{env, path::Path};
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = out_dir.to_string_lossy().to_string();
let src_dir = Path::new("src");
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "linux" {
println!("cargo:rerun-if-changed=src/weaks.c");
cc::Build::new()
.file(src_dir.join("weaks.c"))
.compile("weaks");
println!("cargo:rustc-link-search=native={}", &out_dir);
}
println!("cargo:rerun-if-changed=build.rs");
}

25
libafl_qemu/src/amd64.rs Normal file
View File

@ -0,0 +1,25 @@
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
#[repr(i32)]
#[allow(clippy::pub_enum_variant_names)]
pub enum Amd64Regs {
Rax = 0,
Rbx = 1,
Rcx = 2,
Rdx = 3,
Rsi = 4,
Rdi = 5,
Rbp = 6,
Rsp = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15,
Rip = 16,
Rflags = 17,
}

55
libafl_qemu/src/elf.rs Normal file
View File

@ -0,0 +1,55 @@
//! Utilities to parse and process ELFs
use goblin::elf::Elf;
use std::{convert::AsRef, fs::File, io::Read, path::Path, str};
use libafl::Error;
pub struct EasyElf<'a> {
elf: Elf<'a>,
}
impl<'a> EasyElf<'a> {
pub fn from_file<P>(path: P, buffer: &'a mut Vec<u8>) -> Result<Self, Error>
where
P: AsRef<Path>,
{
let elf = {
let mut binary_file = File::open(path)?;
binary_file.read_to_end(buffer)?;
Elf::parse(buffer).map_err(|e| Error::Unknown(format!("{}", e)))
}?;
Ok(Self { elf })
}
pub fn from_slice(buffer: &'a [u8]) -> Result<Self, Error> {
let elf = Elf::parse(buffer).map_err(|e| Error::Unknown(format!("{}", e)))?;
Ok(Self { elf })
}
#[must_use]
pub fn goblin(&self) -> &Elf<'a> {
&self.elf
}
#[must_use]
pub fn goblin_mut(&mut self) -> &mut Elf<'a> {
&mut self.elf
}
#[must_use]
pub fn resolve_symbol(&self, name: &str) -> Option<u64> {
for sym in self.elf.syms.iter() {
if let Some(sym_name) = self.elf.strtab.get_at(sym.st_name) {
if sym_name == name {
return if sym.st_value == 0 {
None
} else {
Some(sym.st_value)
};
}
}
}
None
}
}

172
libafl_qemu/src/emu.rs Normal file
View File

@ -0,0 +1,172 @@
//! Expose QEMU user `LibAFL` C api to Rust
use core::{convert::Into, mem::transmute, ptr::copy_nonoverlapping};
use num::Num;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{mem::size_of, slice::from_raw_parts, str::from_utf8_unchecked};
pub const SKIP_EXEC_HOOK: u32 = u32::MAX;
extern "C" {
fn libafl_qemu_write_reg(reg: i32, val: *const u8) -> i32;
fn libafl_qemu_read_reg(reg: i32, val: *mut u8) -> i32;
fn libafl_qemu_num_regs() -> i32;
fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
fn libafl_qemu_run() -> i32;
fn strlen(s: *const u8) -> usize;
/// abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, int flags, int fd, abi_ulong offset)
fn target_mmap(start: u64, len: u64, target_prot: i32, flags: i32, fd: i32, offset: u64)
-> u64;
/// int target_munmap(abi_ulong start, abi_ulong len)
fn target_munmap(start: u64, len: u64) -> i32;
static exec_path: *const u8;
static guest_base: usize;
static mut libafl_exec_edge_hook: unsafe extern "C" fn(u32);
static mut libafl_gen_edge_hook: unsafe extern "C" fn(u64, u64) -> u32;
static mut libafl_exec_block_hook: unsafe extern "C" fn(u64);
static mut libafl_gen_block_hook: unsafe extern "C" fn(u64) -> u32;
}
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
#[repr(i32)]
#[allow(clippy::pub_enum_variant_names)]
pub enum MmapPerms {
Read = libc::PROT_READ,
Write = libc::PROT_WRITE,
Execute = libc::PROT_EXEC,
ReadWrite = libc::PROT_READ | libc::PROT_WRITE,
ReadExecute = libc::PROT_READ | libc::PROT_EXEC,
WriteExecute = libc::PROT_WRITE | libc::PROT_EXEC,
ReadWriteExecute = libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
}
pub fn write_mem<T>(addr: u64, buf: &[T]) {
let host_addr = g2h(addr);
unsafe {
copy_nonoverlapping(
buf.as_ptr() as *const _ as *const u8,
host_addr,
buf.len() * size_of::<T>(),
)
}
}
pub fn read_mem<T>(addr: u64, buf: &mut [T]) {
let host_addr = g2h(addr);
unsafe {
copy_nonoverlapping(
host_addr as *const u8,
buf.as_mut_ptr() as *mut _ as *mut u8,
buf.len() * size_of::<T>(),
)
}
}
#[must_use]
pub fn num_regs() -> i32 {
unsafe { libafl_qemu_num_regs() }
}
pub fn write_reg<R, T>(reg: R, val: T) -> Result<(), String>
where
T: Num + PartialOrd + Copy,
R: Into<i32>,
{
let reg = reg.into();
let success = unsafe { libafl_qemu_write_reg(reg, &val as *const _ as *const u8) };
if success == 0 {
Err(format!("Failed to write to register {}", reg))
} else {
Ok(())
}
}
pub fn read_reg<R, T>(reg: R) -> Result<T, String>
where
T: Num + PartialOrd + Copy,
R: Into<i32>,
{
let reg = reg.into();
let mut val = T::zero();
let success = unsafe { libafl_qemu_read_reg(reg, &mut val as *mut _ as *mut u8) };
if success == 0 {
Err(format!("Failed to read register {}", reg))
} else {
Ok(val)
}
}
pub fn set_breakpoint(addr: u64) {
unsafe { libafl_qemu_set_breakpoint(addr) };
}
pub fn remove_breakpoint(addr: u64) {
unsafe { libafl_qemu_remove_breakpoint(addr) };
}
pub fn run() {
unsafe { libafl_qemu_run() };
}
#[must_use]
pub fn g2h<T>(addr: u64) -> *mut T {
unsafe { transmute(addr + guest_base as u64) }
}
#[must_use]
pub fn h2g<T>(addr: *const T) -> u64 {
unsafe { (addr as usize - guest_base) as u64 }
}
#[must_use]
pub fn binary_path<'a>() -> &'a str {
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
}
pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result<u64, String> {
let res = unsafe {
target_mmap(
addr,
size as u64,
perms.into(),
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if res == 0 {
Err(format!("Failed to map {}", addr))
} else {
Ok(res)
}
}
pub fn unmap(addr: u64, size: usize) -> Result<(), String> {
if unsafe { target_munmap(addr, size as u64) } == 0 {
Ok(())
} else {
Err(format!("Failed to unmap {}", addr))
}
}
pub fn set_exec_edge_hook(hook: extern "C" fn(u32)) {
unsafe { libafl_exec_edge_hook = hook };
}
pub fn set_gen_edge_hook(hook: extern "C" fn(u64, u64) -> u32) {
unsafe { libafl_gen_edge_hook = hook };
}
pub fn set_exec_block_hook(hook: extern "C" fn(u64)) {
unsafe { libafl_exec_block_hook = hook };
}
pub fn set_gen_block_hook(hook: extern "C" fn(u64) -> u32) {
unsafe { libafl_gen_block_hook = hook };
}

125
libafl_qemu/src/executor.rs Normal file
View File

@ -0,0 +1,125 @@
use core::{ffi::c_void, mem::transmute, ptr};
use libafl::{
corpus::Corpus,
events::{EventFirer, EventRestarter},
executors::{inprocess::GLOBAL_STATE, Executor, ExitKind, HasObservers, InProcessExecutor},
feedbacks::Feedback,
fuzzer::HasObjective,
inputs::Input,
observers::ObserversTuple,
state::{HasClientPerfStats, HasSolutions},
Error,
};
use crate::{emu, emu::SKIP_EXEC_HOOK};
static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null();
static mut GEN_BLOCK_HOOK_PTR: *const c_void = ptr::null();
extern "C" fn gen_edge_hook_wrapper<S>(src: u64, dst: u64) -> u32 {
unsafe {
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap();
let func: fn(&mut S, u64, u64) -> Option<u32> = transmute(GEN_EDGE_HOOK_PTR);
(func)(state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
extern "C" fn gen_block_hook_wrapper<S>(addr: u64) -> u32 {
unsafe {
let state = (GLOBAL_STATE.state_ptr as *mut S).as_mut().unwrap();
let func: fn(&mut S, u64) -> Option<u32> = transmute(GEN_BLOCK_HOOK_PTR);
(func)(state, addr).map_or(SKIP_EXEC_HOOK, |id| id)
}
}
pub struct QemuExecutor<'a, H, I, OT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
{
inner: InProcessExecutor<'a, H, I, OT, S>,
}
impl<'a, H, I, OT, S> QemuExecutor<'a, H, I, OT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
{
pub fn new<EM, OC, OF, Z>(
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
state: &mut S,
event_mgr: &mut EM,
) -> Result<Self, Error>
where
EM: EventFirer<I, S> + EventRestarter<S>,
OC: Corpus<I>,
OF: Feedback<I, S>,
S: HasSolutions<OC, I> + HasClientPerfStats,
Z: HasObjective<I, OF, S>,
{
Ok(Self {
inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?,
})
}
#[allow(clippy::unused_self)]
pub fn hook_edge_generation(&self, hook: fn(&mut S, u64, u64) -> Option<u32>) {
unsafe { GEN_EDGE_HOOK_PTR = hook as *const _ };
emu::set_gen_edge_hook(gen_edge_hook_wrapper::<S>);
}
#[allow(clippy::unused_self)]
pub fn hook_edge_execution(&self, hook: extern "C" fn(u32)) {
emu::set_exec_edge_hook(hook);
}
#[allow(clippy::unused_self)]
pub fn hook_block_generation(&self, hook: fn(&mut S, u64) -> Option<u32>) {
unsafe { GEN_BLOCK_HOOK_PTR = hook as *const _ };
emu::set_gen_block_hook(gen_block_hook_wrapper::<S>);
}
#[allow(clippy::unused_self)]
pub fn hook_block_execution(&self, hook: extern "C" fn(u64)) {
emu::set_exec_block_hook(hook);
}
}
impl<'a, EM, H, I, OT, S, Z> Executor<EM, I, S, Z> for QemuExecutor<'a, H, I, OT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
{
fn run_target(
&mut self,
fuzzer: &mut Z,
state: &mut S,
mgr: &mut EM,
input: &I,
) -> Result<ExitKind, Error> {
self.inner.run_target(fuzzer, state, mgr, input)
}
}
impl<'a, H, I, OT, S> HasObservers<I, OT, S> for QemuExecutor<'a, H, I, OT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input,
OT: ObserversTuple<I, S>,
{
#[inline]
fn observers(&self) -> &OT {
self.inner.observers()
}
#[inline]
fn observers_mut(&mut self) -> &mut OT {
self.inner.observers_mut()
}
}

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

@ -0,0 +1,48 @@
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use libafl::state::HasMetadata;
pub use libafl_targets::{EDGES_MAP, EDGES_MAP_SIZE, MAX_EDGES_NUM};
/// A testcase metadata saying if a testcase is favored
#[derive(Default, Serialize, Deserialize)]
pub struct QemuEdgesMapMetadata {
pub map: HashMap<(u64, u64), u32>,
}
impl QemuEdgesMapMetadata {
#[must_use]
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
}
libafl::impl_serdeany!(QemuEdgesMapMetadata);
pub fn gen_unique_edges_id<S>(state: &mut S, src: u64, dest: u64) -> Option<u32>
where
S: HasMetadata,
{
if state.metadata().get::<QemuEdgesMapMetadata>().is_none() {
state.add_metadata(QemuEdgesMapMetadata::new());
}
let meta = state
.metadata_mut()
.get_mut::<QemuEdgesMapMetadata>()
.unwrap();
Some(*meta.map.entry((src, dest)).or_insert_with(|| unsafe {
let id = MAX_EDGES_NUM;
MAX_EDGES_NUM = (MAX_EDGES_NUM + 1) & (EDGES_MAP_SIZE - 1);
id as u32
}))
}
pub extern "C" fn exec_log_hitcount(id: u32) {
unsafe { EDGES_MAP[id as usize] += 1 };
}
pub extern "C" fn exec_log_single(id: u32) {
unsafe { EDGES_MAP[id as usize] = 1 };
}

34
libafl_qemu/src/lib.rs Normal file
View File

@ -0,0 +1,34 @@
use std::env;
pub mod amd64;
pub mod x86;
pub mod elf;
pub mod hooks;
#[cfg(target_os = "linux")]
pub mod executor;
#[cfg(target_os = "linux")]
pub use executor::QemuExecutor;
#[cfg(target_os = "linux")]
pub mod emu;
#[cfg(target_os = "linux")]
pub use emu::*;
#[must_use]
pub fn filter_qemu_args() -> Vec<String> {
let mut args = vec![env::args().next().unwrap()];
let mut args_iter = env::args();
while let Some(arg) = args_iter.next() {
if arg.starts_with("--libafl") {
args.push(arg);
args.push(args_iter.next().unwrap());
} else if arg.starts_with("-libafl") {
args.push("-".to_owned() + &arg);
args.push(args_iter.next().unwrap());
}
}
args
}

66
libafl_qemu/src/weaks.c Normal file
View File

@ -0,0 +1,66 @@
#include <stdlib.h>
#include <stdint.h>
typedef int64_t abi_long;
typedef uint64_t abi_ulong;
__attribute__((weak)) int libafl_qemu_write_reg(int reg, uint8_t* val) {
(void)reg;
(void)val;
return 0;
}
__attribute__((weak)) int libafl_qemu_read_reg(int reg, uint8_t* val) {
(void)reg;
(void)val;
return 0;
}
__attribute__((weak)) int libafl_qemu_num_regs(void) {
return 0;
}
__attribute__((weak)) int libafl_qemu_set_breakpoint(uint64_t addr) {
(void)addr;
return 0;
}
__attribute__((weak)) int libafl_qemu_remove_breakpoint(uint64_t addr) {
(void)addr;
return 0;
}
__attribute__((weak)) int libafl_qemu_run() {
return 0;
}
__attribute__((weak)) uint64_t libafl_load_addr() {
return 0;
}
__attribute__((weak)) abi_long target_mmap(abi_ulong start, abi_ulong len,
int target_prot, int flags, int fd,
abi_ulong offset) {
(void)start;
(void)len;
(void)target_prot;
(void)flags;
(void)fd;
(void)offset;
return 0;
}
__attribute__((weak)) int target_munmap(abi_ulong start, abi_ulong len) {
(void)start;
(void)len;
return 0;
}
__attribute__((weak)) char* exec_path = NULL;
__attribute__((weak)) size_t guest_base = 0;
__attribute__((weak)) void (*libafl_exec_edge_hook)(uint32_t);
__attribute__((weak)) uint32_t (*libafl_gen_edge_hook)(uint64_t, uint64_t);
__attribute__((weak)) void (*libafl_exec_block_hook)(uint64_t);
__attribute__((weak)) uint32_t (*libafl_gen_block_hook)(uint64_t);

17
libafl_qemu/src/x86.rs Normal file
View File

@ -0,0 +1,17 @@
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)]
#[repr(i32)]
#[allow(clippy::pub_enum_variant_names)]
pub enum X86Regs {
Eax = 0,
Ebx = 1,
Ecx = 2,
Edx = 3,
Esi = 4,
Edi = 5,
Ebp = 6,
Esp = 7,
Eip = 8,
Eflags = 9,
}