Unify qemu executor (#2483)
* Remove stateless qemu executor * All harnesses take a reference to an emulator as parameter now * harness takes an emulator as first parameter, and input as second parameter (opposite of previous definition) * bump libafl qemu dependencies to the latest version
This commit is contained in:
parent
00806b177d
commit
13ba32ed2a
@ -156,7 +156,7 @@ fn fuzz(
|
|||||||
CmpLogChildModule::default(),
|
CmpLogChildModule::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut emulator = Emulator::new(
|
let emulator = Emulator::new(
|
||||||
args.as_slice(),
|
args.as_slice(),
|
||||||
env.as_slice(),
|
env.as_slice(),
|
||||||
emulator_modules,
|
emulator_modules,
|
||||||
@ -356,7 +356,7 @@ fn fuzz(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let executor = QemuForkExecutor::new(
|
let executor = QemuForkExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -329,7 +329,7 @@ fn fuzz(
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
let mut buf = target.as_slice();
|
let mut buf = target.as_slice();
|
||||||
let mut len = buf.len();
|
let mut len = buf.len();
|
||||||
@ -366,12 +366,12 @@ fn fuzz(
|
|||||||
//QemuSnapshotHelper::new()
|
//QemuSnapshotHelper::new()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut emulator =
|
let emulator =
|
||||||
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
|
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
|
||||||
|
|
||||||
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -223,11 +223,11 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
|
|
||||||
let modules = tuple_list!(EdgeCoverageChildModule::default(),);
|
let modules = tuple_list!(EdgeCoverageChildModule::default(),);
|
||||||
|
|
||||||
let mut emulator =
|
let emulator =
|
||||||
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
|
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
|
||||||
|
|
||||||
let mut executor = QemuForkExecutor::new(
|
let mut executor = QemuForkExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer),
|
tuple_list!(edges_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -178,7 +178,7 @@ pub fn fuzz() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
let mut buf = target.as_slice();
|
let mut buf = target.as_slice();
|
||||||
let mut len = buf.len();
|
let mut len = buf.len();
|
||||||
@ -241,7 +241,7 @@ pub fn fuzz() {
|
|||||||
false,
|
false,
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut emulator = Emulator::new_with_qemu(
|
let emulator = Emulator::new_with_qemu(
|
||||||
qemu,
|
qemu,
|
||||||
emulator_modules,
|
emulator_modules,
|
||||||
NopEmulatorExitHandler,
|
NopEmulatorExitHandler,
|
||||||
@ -250,7 +250,7 @@ pub fn fuzz() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
(),
|
(),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -149,12 +149,13 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
|||||||
state.add_metadata(tokens);
|
state.add_metadata(tokens);
|
||||||
|
|
||||||
let harness = Harness::new(self.qemu)?;
|
let harness = Harness::new(self.qemu)?;
|
||||||
let mut harness = |input: &BytesInput| harness.run(input);
|
let mut harness =
|
||||||
|
|_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| harness.run(input);
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
let mut emulator = Emulator::new_with_qemu(
|
let emulator = Emulator::new_with_qemu(
|
||||||
*self.qemu,
|
*self.qemu,
|
||||||
modules,
|
modules,
|
||||||
NopEmulatorExitHandler,
|
NopEmulatorExitHandler,
|
||||||
@ -165,7 +166,7 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
|||||||
if self.options.is_cmplog_core(self.core_id) {
|
if self.options.is_cmplog_core(self.core_id) {
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
observers,
|
observers,
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
@ -203,7 +204,7 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
|||||||
} else {
|
} else {
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
observers,
|
observers,
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -25,9 +25,9 @@ lto = "fat"
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../../libafl/" }
|
libafl = { path = "../../../libafl" }
|
||||||
libafl_bolts = { path = "../../../libafl_bolts/" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
libafl_qemu = { path = "../../../libafl_qemu/", features = [
|
libafl_qemu = { path = "../../../libafl_qemu", features = [
|
||||||
"arm",
|
"arm",
|
||||||
"systemmode",
|
"systemmode",
|
||||||
] }
|
] }
|
||||||
|
@ -32,7 +32,7 @@ use libafl_qemu::{
|
|||||||
command::{EndCommand, StartCommand, StdCommandManager},
|
command::{EndCommand, StartCommand, StdCommandManager},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
executor::QemuExecutor,
|
||||||
modules::edges::{
|
modules::edges::{
|
||||||
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
||||||
},
|
},
|
||||||
@ -103,7 +103,7 @@ pub fn fuzz() {
|
|||||||
let modules = tuple_list!(EdgeCoverageModule::default());
|
let modules = tuple_list!(EdgeCoverageModule::default());
|
||||||
|
|
||||||
// Create emulator
|
// Create emulator
|
||||||
let mut emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap();
|
let emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap();
|
||||||
|
|
||||||
// Set breakpoints of interest with corresponding commands.
|
// Set breakpoints of interest with corresponding commands.
|
||||||
emu.add_breakpoint(
|
emu.add_breakpoint(
|
||||||
@ -127,14 +127,8 @@ pub fn fuzz() {
|
|||||||
println!("Devices = {:?}", devices);
|
println!("Devices = {:?}", devices);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness =
|
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| unsafe {
|
||||||
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _, _, _>| unsafe {
|
emulator.run(input).unwrap().try_into().unwrap()
|
||||||
qemu_executor_state
|
|
||||||
.emulator_mut()
|
|
||||||
.run(input)
|
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
@ -197,8 +191,8 @@ pub fn fuzz() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let mut executor = StatefulQemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut emu,
|
emu,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -32,7 +32,7 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
command::NopCommandManager,
|
command::NopCommandManager,
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
executor::QemuExecutor,
|
||||||
modules::edges::{
|
modules::edges::{
|
||||||
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
||||||
},
|
},
|
||||||
@ -93,7 +93,7 @@ pub fn fuzz() {
|
|||||||
|
|
||||||
let emulator_modules = tuple_list!(EdgeCoverageModule::default());
|
let emulator_modules = tuple_list!(EdgeCoverageModule::default());
|
||||||
|
|
||||||
let mut emulator = Emulator::new(
|
let emulator = Emulator::new(
|
||||||
args.as_slice(),
|
args.as_slice(),
|
||||||
env.as_slice(),
|
env.as_slice(),
|
||||||
emulator_modules,
|
emulator_modules,
|
||||||
@ -129,8 +129,7 @@ pub fn fuzz() {
|
|||||||
let snap = qemu.create_fast_snapshot(true);
|
let snap = qemu.create_fast_snapshot(true);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |input: &BytesInput, state: &mut QemuExecutorState<_, _, _, _>| {
|
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||||
let emulator = state.emulator_mut();
|
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
let mut buf = target.as_slice();
|
let mut buf = target.as_slice();
|
||||||
let len = buf.len();
|
let len = buf.len();
|
||||||
@ -229,8 +228,8 @@ pub fn fuzz() {
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let mut executor = StatefulQemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -29,7 +29,7 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
command::StdCommandManager,
|
command::StdCommandManager,
|
||||||
emu::Emulator,
|
emu::Emulator,
|
||||||
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
executor::QemuExecutor,
|
||||||
modules::edges::{
|
modules::edges::{
|
||||||
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
|
||||||
},
|
},
|
||||||
@ -66,20 +66,14 @@ pub fn fuzz() {
|
|||||||
// Choose modules to use
|
// Choose modules to use
|
||||||
let modules = tuple_list!(EdgeCoverageModule::default());
|
let modules = tuple_list!(EdgeCoverageModule::default());
|
||||||
|
|
||||||
let mut emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
|
let emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
|
||||||
|
|
||||||
let devices = emu.list_devices();
|
let devices = emu.list_devices();
|
||||||
println!("Devices = {:?}", devices);
|
println!("Devices = {:?}", devices);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness =
|
let mut harness = |emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| unsafe {
|
||||||
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _, _, _>| unsafe {
|
emulator.run(input).unwrap().try_into().unwrap()
|
||||||
qemu_executor_state
|
|
||||||
.emulator_mut()
|
|
||||||
.run(input)
|
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
@ -142,8 +136,8 @@ pub fn fuzz() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let mut executor = StatefulQemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut emu,
|
emu,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
@ -33,8 +33,8 @@ pub type StatefulInProcessExecutor<'a, H, OT, S, ES> =
|
|||||||
/// The process executor simply calls a target function, as boxed `FnMut` trait object
|
/// The process executor simply calls a target function, as boxed `FnMut` trait object
|
||||||
/// The internal state of the executor is made available to the harness.
|
/// The internal state of the executor is made available to the harness.
|
||||||
pub type OwnedInProcessExecutor<OT, S, ES> = StatefulGenericInProcessExecutor<
|
pub type OwnedInProcessExecutor<OT, S, ES> = StatefulGenericInProcessExecutor<
|
||||||
dyn FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind,
|
dyn FnMut(&mut ES, &<S as UsesInput>::Input) -> ExitKind,
|
||||||
Box<dyn FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind>,
|
Box<dyn FnMut(&mut ES, &<S as UsesInput>::Input) -> ExitKind>,
|
||||||
(),
|
(),
|
||||||
OT,
|
OT,
|
||||||
S,
|
S,
|
||||||
@ -46,7 +46,7 @@ pub type OwnedInProcessExecutor<OT, S, ES> = StatefulGenericInProcessExecutor<
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
pub struct StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -63,7 +63,7 @@ where
|
|||||||
|
|
||||||
impl<H, HB, HT, OT, S, ES> Debug for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
impl<H, HB, HT, OT, S, ES> Debug for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S> + Debug,
|
OT: ObserversTuple<S> + Debug,
|
||||||
@ -79,7 +79,7 @@ where
|
|||||||
|
|
||||||
impl<H, HB, HT, OT, S, ES> UsesState for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
impl<H, HB, HT, OT, S, ES> UsesState for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -90,7 +90,7 @@ where
|
|||||||
|
|
||||||
impl<H, HB, HT, OT, S, ES> UsesObservers for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
impl<H, HB, HT, OT, S, ES> UsesObservers for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -103,7 +103,7 @@ impl<EM, H, HB, HT, OT, S, Z, ES> Executor<EM, Z>
|
|||||||
for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
EM: UsesState<State = S>,
|
EM: UsesState<State = S>,
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -125,7 +125,7 @@ where
|
|||||||
}
|
}
|
||||||
self.inner.hooks.pre_exec_all(state, input);
|
self.inner.hooks.pre_exec_all(state, input);
|
||||||
|
|
||||||
let ret = self.harness_fn.borrow_mut()(input, &mut self.exposed_executor_state);
|
let ret = self.harness_fn.borrow_mut()(&mut self.exposed_executor_state, input);
|
||||||
|
|
||||||
self.inner.hooks.post_exec_all(state, input);
|
self.inner.hooks.post_exec_all(state, input);
|
||||||
self.inner.leave_target(fuzzer, state, mgr, input);
|
self.inner.leave_target(fuzzer, state, mgr, input);
|
||||||
@ -135,7 +135,7 @@ where
|
|||||||
|
|
||||||
impl<H, HB, HT, OT, S, ES> HasObservers for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
impl<H, HB, HT, OT, S, ES> HasObservers for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -154,7 +154,7 @@ where
|
|||||||
|
|
||||||
impl<'a, H, OT, S, ES> StatefulInProcessExecutor<'a, H, OT, S, ES>
|
impl<'a, H, OT, S, ES> StatefulInProcessExecutor<'a, H, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: HasExecutions + HasSolutions + HasCorpus + State,
|
S: HasExecutions + HasSolutions + HasCorpus + State,
|
||||||
{
|
{
|
||||||
@ -265,7 +265,7 @@ where
|
|||||||
|
|
||||||
impl<H, HB, HT, OT, S, ES> StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
impl<H, HB, HT, OT, S, ES> StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -284,7 +284,7 @@ where
|
|||||||
|
|
||||||
impl<H, HB, HT, OT, S, ES> StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
impl<H, HB, HT, OT, S, ES> StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&S::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &S::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
@ -414,7 +414,7 @@ where
|
|||||||
impl<H, HB, HT, OT, S, ES> HasInProcessHooks<S>
|
impl<H, HB, HT, OT, S, ES> HasInProcessHooks<S>
|
||||||
for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
for StatefulGenericInProcessExecutor<H, HB, HT, OT, S, ES>
|
||||||
where
|
where
|
||||||
H: FnMut(&<S as UsesInput>::Input, &mut ES) -> ExitKind + ?Sized,
|
H: FnMut(&mut ES, &<S as UsesInput>::Input) -> ExitKind + ?Sized,
|
||||||
HB: BorrowMut<H>,
|
HB: BorrowMut<H>,
|
||||||
HT: ExecutorHooksTuple<S>,
|
HT: ExecutorHooksTuple<S>,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
|
@ -110,14 +110,14 @@ goblin = "0.8"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
strum = "0.26"
|
strum = "0.26"
|
||||||
strum_macros = "0.26"
|
strum_macros = "0.26"
|
||||||
syscall-numbers = "3.0"
|
syscall-numbers = "4.0"
|
||||||
meminterval = "0.4"
|
meminterval = "0.4"
|
||||||
thread_local = "1.1.4"
|
thread_local = "1.1.4"
|
||||||
capstone = "0.12.0"
|
capstone = "0.12.0"
|
||||||
rangemap = "1.3"
|
rangemap = "1.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
object = "0.36"
|
object = "0.36"
|
||||||
addr2line = "0.23"
|
addr2line = "0.24"
|
||||||
typed-arena = "2.0"
|
typed-arena = "2.0"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
enum-map = "2.7"
|
enum-map = "2.7"
|
||||||
|
@ -58,6 +58,7 @@ const WRAPPER_HEADER: &str = r#"
|
|||||||
#include "hw/core/sysemu-cpu-ops.h"
|
#include "hw/core/sysemu-cpu-ops.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "sysemu/tcg.h"
|
#include "sysemu/tcg.h"
|
||||||
|
#include "sysemu/runstate.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
#include "libafl/syx-snapshot/device-save.h"
|
#include "libafl/syx-snapshot/device-save.h"
|
||||||
@ -148,6 +149,7 @@ pub fn generate(
|
|||||||
.allowlist_type("libafl_mapinfo")
|
.allowlist_type("libafl_mapinfo")
|
||||||
.allowlist_type("IntervalTreeRoot")
|
.allowlist_type("IntervalTreeRoot")
|
||||||
.allowlist_function("qemu_user_init")
|
.allowlist_function("qemu_user_init")
|
||||||
|
.allowlist_function("qemu_system_debug_request")
|
||||||
.allowlist_function("target_mmap")
|
.allowlist_function("target_mmap")
|
||||||
.allowlist_function("target_mprotect")
|
.allowlist_function("target_mprotect")
|
||||||
.allowlist_function("target_munmap")
|
.allowlist_function("target_munmap")
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
|
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
use core::ptr;
|
|
||||||
use core::{
|
use core::{
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl::{
|
use libafl::{
|
||||||
@ -15,7 +15,7 @@ use libafl::{
|
|||||||
events::{EventFirer, EventRestarter},
|
events::{EventFirer, EventRestarter},
|
||||||
executors::{
|
executors::{
|
||||||
hooks::inprocess::InProcessExecutorHandlerData,
|
hooks::inprocess::InProcessExecutorHandlerData,
|
||||||
inprocess::{HasInProcessHooks, InProcessExecutor},
|
inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks},
|
||||||
Executor, ExitKind, HasObservers,
|
Executor, ExitKind, HasObservers,
|
||||||
},
|
},
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
@ -30,58 +30,29 @@ use libafl_bolts::{
|
|||||||
os::unix_signals::{siginfo_t, ucontext_t, Signal},
|
os::unix_signals::{siginfo_t, ucontext_t, Signal},
|
||||||
tuples::RefIndexable,
|
tuples::RefIndexable,
|
||||||
};
|
};
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
use libafl_qemu_sys::qemu_system_debug_request;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
use crate::emu::EmulatorModules;
|
use crate::EmulatorModules;
|
||||||
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorExitHandler};
|
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorExitHandler};
|
||||||
|
|
||||||
/// A version of `QemuExecutor` with a state accessible from the harness.
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub mod stateful;
|
extern "C" {
|
||||||
|
// Original QEMU user signal handler
|
||||||
pub struct QemuExecutorState<'a, CM, EH, ET, S>
|
fn libafl_qemu_handle_crash(signal: i32, info: *mut siginfo_t, puc: *mut c_void);
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
{
|
|
||||||
emulator: &'a mut Emulator<CM, EH, ET, S>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
pub struct QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||||
where
|
where
|
||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
|
S: Unpin + State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
{
|
{
|
||||||
inner: InProcessExecutor<'a, H, OT, S>,
|
inner: StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
|
||||||
state: QemuExecutorState<'a, CM, EH, ET, S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, H, OT, ET, S> Debug for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S> + Debug,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
|
||||||
S: Unpin + State + HasExecutions + Debug,
|
|
||||||
OT: ObserversTuple<S> + Debug,
|
|
||||||
ET: EmulatorModuleTuple<S> + Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("QemuExecutor")
|
|
||||||
.field("emulator", &self.state.emulator)
|
|
||||||
.field("inner", &self.inner)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
extern "C" {
|
|
||||||
// Original QEMU user signal handler
|
|
||||||
fn libafl_qemu_handle_crash(signal: i32, info: *mut siginfo_t, puc: *mut c_void);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -103,17 +74,12 @@ pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, ET, S>(
|
|||||||
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
||||||
None => ptr::null_mut(),
|
None => ptr::null_mut(),
|
||||||
};
|
};
|
||||||
libafl_qemu_handle_crash(signal as i32, info, puc);
|
libafl_qemu_handle_crash(signal as i32, std::ptr::from_mut::<siginfo_t>(info), puc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub(crate) static mut BREAK_ON_TMOUT: bool = false;
|
pub(crate) static mut BREAK_ON_TMOUT: bool = false;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
|
||||||
extern "C" {
|
|
||||||
fn qemu_system_debug_request();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>(
|
pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>(
|
||||||
signal: Signal,
|
signal: Signal,
|
||||||
@ -136,58 +102,19 @@ pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, CM, EH, ET, S> QemuExecutorState<'a, CM, EH, ET, S>
|
impl<'a, CM, EH, H, OT, ET, S> Debug for QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
||||||
where
|
where
|
||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
|
S: Unpin + State + HasExecutions,
|
||||||
|
OT: ObserversTuple<S> + Debug,
|
||||||
ET: EmulatorModuleTuple<S> + Debug,
|
ET: EmulatorModuleTuple<S> + Debug,
|
||||||
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
|
||||||
{
|
{
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
pub fn new<E, EM, OF, OT, Z>(emulator: &'a mut Emulator<CM, EH, ET, S>) -> Result<Self, Error>
|
f.debug_struct("QemuExecutor")
|
||||||
where
|
.field("inner", &self.inner)
|
||||||
E: Executor<EM, Z, State = S> + HasInProcessHooks<S> + HasObservers,
|
.finish()
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
Ok(QemuExecutorState { emulator })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
pub fn new<E, EM, OF, OT, Z>(emulator: &'a mut Emulator<CM, EH, ET, S>) -> Result<Self, Error>
|
|
||||||
where
|
|
||||||
E: Executor<EM, Z, State = S> + HasInProcessHooks<S> + HasObservers,
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
Z: HasObjective<Objective = OF, State = S> + ExecutionProcessor + HasScheduler,
|
|
||||||
{
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
{
|
|
||||||
let handler = |emulator_modules: &mut EmulatorModules<ET, S>, host_sig| {
|
|
||||||
eprintln!("Crashed with signal {host_sig}");
|
|
||||||
unsafe {
|
|
||||||
libafl::executors::inprocess::generic_inproc_crash_handler::<E, EM, OF, Z>();
|
|
||||||
}
|
|
||||||
if let Some(cpu) = emulator_modules.qemu().current_cpu() {
|
|
||||||
eprint!("Context:\n{}", cpu.display_context());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
emulator.modules_mut().crash_closure(Box::new(handler));
|
|
||||||
}
|
|
||||||
Ok(QemuExecutorState { emulator })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
|
|
||||||
self.emulator
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn emulator_mut(&mut self) -> &mut Emulator<CM, EH, ET, S> {
|
|
||||||
self.emulator
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,13 +122,13 @@ impl<'a, CM, EH, H, OT, ET, S> QemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|||||||
where
|
where
|
||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
S: Unpin + State + HasExecutions,
|
S: Unpin + State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
ET: EmulatorModuleTuple<S> + Debug,
|
ET: EmulatorModuleTuple<S> + Debug,
|
||||||
{
|
{
|
||||||
pub fn new<EM, OF, Z>(
|
pub fn new<EM, OF, Z>(
|
||||||
emulator: &'a mut Emulator<CM, EH, ET, S>,
|
emulator: Emulator<CM, EH, ET, S>,
|
||||||
harness_fn: &'a mut H,
|
harness_fn: &'a mut H,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -215,31 +142,51 @@ where
|
|||||||
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
||||||
Z: HasObjective<Objective = OF, State = S> + HasScheduler + ExecutionProcessor,
|
Z: HasObjective<Objective = OF, State = S> + HasScheduler + ExecutionProcessor,
|
||||||
{
|
{
|
||||||
let mut inner = InProcessExecutor::with_timeout(
|
let mut inner = StatefulInProcessExecutor::with_timeout(
|
||||||
harness_fn, observers, fuzzer, state, event_mgr, timeout,
|
harness_fn, emulator, observers, fuzzer, state, event_mgr, timeout,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
{
|
{
|
||||||
inner.inprocess_hooks_mut().crash_handler =
|
inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::<
|
||||||
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z, ET, S>
|
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
|
||||||
as *const c_void;
|
EM,
|
||||||
|
OF,
|
||||||
|
Z,
|
||||||
|
ET,
|
||||||
|
S,
|
||||||
|
> as *const c_void;
|
||||||
|
|
||||||
|
let handler = |emulator_modules: &mut EmulatorModules<ET, S>, host_sig| {
|
||||||
|
eprintln!("Crashed with signal {host_sig}");
|
||||||
|
unsafe {
|
||||||
|
libafl::executors::inprocess::generic_inproc_crash_handler::<Self, EM, OF, Z>();
|
||||||
|
}
|
||||||
|
if let Some(cpu) = emulator_modules.qemu().current_cpu() {
|
||||||
|
eprint!("Context:\n{}", cpu.display_context());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inner
|
||||||
|
.exposed_executor_state_mut()
|
||||||
|
.modules_mut()
|
||||||
|
.crash_closure(Box::new(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
inner.inprocess_hooks_mut().timeout_handler =
|
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
||||||
inproc_qemu_timeout_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z>
|
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>>,
|
||||||
as *const c_void;
|
EM,
|
||||||
|
OF,
|
||||||
|
Z,
|
||||||
|
> as *const c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state =
|
Ok(Self { inner })
|
||||||
QemuExecutorState::new::<InProcessExecutor<'a, H, OT, S>, EM, OF, OT, Z>(emulator)?;
|
|
||||||
|
|
||||||
Ok(Self { inner, state })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner(&self) -> &InProcessExecutor<'a, H, OT, S> {
|
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>> {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,43 +197,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, OT, S> {
|
pub fn inner_mut(
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, ET, S> QemuExecutorState<'a, CM, EH, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
{
|
|
||||||
fn pre_exec<E, EM, OF, Z>(&mut self, input: &E::Input)
|
|
||||||
where
|
|
||||||
E: Executor<EM, Z, State = S>,
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
self.emulator.first_exec_all();
|
|
||||||
|
|
||||||
self.emulator.pre_exec_all(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec<E, EM, OT, OF, Z>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &E::Input,
|
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, EH, ET, S>> {
|
||||||
observers: &mut OT,
|
&mut self.inner
|
||||||
exit_kind: &mut ExitKind,
|
|
||||||
) where
|
|
||||||
E: Executor<EM, Z, State = S> + HasObservers,
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
self.emulator.post_exec_all(input, observers, exit_kind);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +209,7 @@ where
|
|||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
OF: Feedback<S>,
|
OF: Feedback<S>,
|
||||||
@ -309,13 +223,18 @@ where
|
|||||||
mgr: &mut EM,
|
mgr: &mut EM,
|
||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
self.state.pre_exec::<Self, EM, OF, Z>(input);
|
self.inner.exposed_executor_state_mut().first_exec_all();
|
||||||
|
|
||||||
|
self.inner.exposed_executor_state_mut().pre_exec_all(input);
|
||||||
|
|
||||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.state.post_exec::<Self, EM, OT, OF, Z>(
|
|
||||||
|
self.inner.exposed_executor_state.post_exec_all(
|
||||||
input,
|
input,
|
||||||
&mut *self.inner.observers_mut(),
|
&mut *self.inner.inner.observers_mut(),
|
||||||
&mut exit_kind,
|
&mut exit_kind,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(exit_kind)
|
Ok(exit_kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,7 +243,7 @@ impl<'a, CM, EH, H, OT, ET, S> UsesState for QemuExecutor<'a, CM, EH, H, OT, ET,
|
|||||||
where
|
where
|
||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + State + HasExecutions,
|
S: Unpin + State + HasExecutions,
|
||||||
@ -336,7 +255,7 @@ impl<'a, CM, EH, H, OT, ET, S> UsesObservers for QemuExecutor<'a, CM, EH, H, OT,
|
|||||||
where
|
where
|
||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + State + HasExecutions,
|
S: Unpin + State + HasExecutions,
|
||||||
@ -348,7 +267,7 @@ impl<'a, CM, EH, H, OT, ET, S> HasObservers for QemuExecutor<'a, CM, EH, H, OT,
|
|||||||
where
|
where
|
||||||
CM: CommandManager<EH, ET, S>,
|
CM: CommandManager<EH, ET, S>,
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
EH: EmulatorExitHandler<ET, S>,
|
||||||
H: FnMut(&S::Input) -> ExitKind,
|
H: FnMut(&mut Emulator<CM, EH, ET, S>, &S::Input) -> ExitKind,
|
||||||
S: Unpin + State + HasExecutions,
|
S: Unpin + State + HasExecutions,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
@ -378,7 +297,7 @@ where
|
|||||||
Z: UsesState<State = S>,
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
|
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
|
||||||
state: QemuExecutorState<'a, CM, EH, ET, S>,
|
emulator: Emulator<CM, EH, ET, S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
@ -397,8 +316,8 @@ where
|
|||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("QemuForkExecutor")
|
f.debug_struct("QemuForkExecutor")
|
||||||
.field("emulator", &self.state.emulator)
|
|
||||||
.field("inner", &self.inner)
|
.field("inner", &self.inner)
|
||||||
|
.field("emulator", &self.emulator)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,7 +338,7 @@ where
|
|||||||
Z: HasObjective<Objective = OF, State = S>,
|
Z: HasObjective<Objective = OF, State = S>,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
emulator: &'a mut Emulator<CM, EH, ET, S>,
|
emulator: Emulator<CM, EH, ET, S>,
|
||||||
harness_fn: &'a mut H,
|
harness_fn: &'a mut H,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -440,7 +359,7 @@ where
|
|||||||
timeout,
|
timeout,
|
||||||
shmem_provider,
|
shmem_provider,
|
||||||
)?,
|
)?,
|
||||||
state: QemuExecutorState { emulator },
|
emulator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,7 +372,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
|
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
|
||||||
self.state.emulator
|
&self.emulator
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emulator_mut(&mut self) -> &Emulator<CM, EH, ET, S> {
|
||||||
|
&mut self.emulator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,217 +0,0 @@
|
|||||||
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
|
|
||||||
use core::{
|
|
||||||
ffi::c_void,
|
|
||||||
fmt::{self, Debug, Formatter},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use libafl::{
|
|
||||||
events::{EventFirer, EventRestarter},
|
|
||||||
executors::{
|
|
||||||
inprocess::{stateful::StatefulInProcessExecutor, HasInProcessHooks},
|
|
||||||
Executor, ExitKind, HasObservers,
|
|
||||||
},
|
|
||||||
feedbacks::Feedback,
|
|
||||||
fuzzer::HasObjective,
|
|
||||||
observers::{ObserversTuple, UsesObservers},
|
|
||||||
state::{HasCorpus, HasExecutions, HasSolutions, State, UsesState},
|
|
||||||
Error, ExecutionProcessor, HasScheduler,
|
|
||||||
};
|
|
||||||
use libafl_bolts::tuples::RefIndexable;
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
use crate::executor::inproc_qemu_crash_handler;
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
|
||||||
use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT};
|
|
||||||
use crate::{
|
|
||||||
command::CommandManager, executor::QemuExecutorState, modules::EmulatorModuleTuple, Emulator,
|
|
||||||
EmulatorExitHandler,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, H, OT, ET, S> Debug for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
OT: ObserversTuple<S> + Debug,
|
|
||||||
ET: EmulatorModuleTuple<S> + Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("StatefulQemuExecutor")
|
|
||||||
.field("inner", &self.inner)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, H, OT, ET, S> StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
ET: EmulatorModuleTuple<S> + Debug,
|
|
||||||
{
|
|
||||||
pub fn new<EM, OF, Z>(
|
|
||||||
emulator: &'a mut Emulator<CM, EH, ET, S>,
|
|
||||||
harness_fn: &'a mut H,
|
|
||||||
observers: OT,
|
|
||||||
fuzzer: &mut Z,
|
|
||||||
state: &mut S,
|
|
||||||
event_mgr: &mut EM,
|
|
||||||
timeout: Duration,
|
|
||||||
) -> Result<Self, Error>
|
|
||||||
where
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
|
||||||
Z: HasObjective<Objective = OF, State = S> + HasScheduler + ExecutionProcessor,
|
|
||||||
{
|
|
||||||
let qemu_state = QemuExecutorState::new::<
|
|
||||||
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
|
|
||||||
EM,
|
|
||||||
OF,
|
|
||||||
OT,
|
|
||||||
Z,
|
|
||||||
>(emulator)?;
|
|
||||||
|
|
||||||
let mut inner = StatefulInProcessExecutor::with_timeout(
|
|
||||||
harness_fn, qemu_state, observers, fuzzer, state, event_mgr, timeout,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
{
|
|
||||||
inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::<
|
|
||||||
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
|
|
||||||
EM,
|
|
||||||
OF,
|
|
||||||
Z,
|
|
||||||
ET,
|
|
||||||
S,
|
|
||||||
> as *const c_void;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
|
||||||
{
|
|
||||||
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
|
||||||
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
|
|
||||||
EM,
|
|
||||||
OF,
|
|
||||||
Z,
|
|
||||||
> as *const c_void;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self { inner })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inner(
|
|
||||||
&self,
|
|
||||||
) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>> {
|
|
||||||
&self.inner
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
|
||||||
pub fn break_on_timeout(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
BREAK_ON_TMOUT = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inner_mut(
|
|
||||||
&mut self,
|
|
||||||
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>> {
|
|
||||||
&mut self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, EM, H, OT, OF, ET, S, Z> Executor<EM, Z>
|
|
||||||
for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
OF: Feedback<S>,
|
|
||||||
ET: EmulatorModuleTuple<S> + Debug,
|
|
||||||
Z: HasObjective<Objective = OF, State = S>,
|
|
||||||
{
|
|
||||||
fn run_target(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut Z,
|
|
||||||
state: &mut Self::State,
|
|
||||||
mgr: &mut EM,
|
|
||||||
input: &Self::Input,
|
|
||||||
) -> Result<ExitKind, Error> {
|
|
||||||
self.inner
|
|
||||||
.exposed_executor_state_mut()
|
|
||||||
.pre_exec::<Self, EM, OF, Z>(input);
|
|
||||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
|
||||||
self.inner
|
|
||||||
.exposed_executor_state
|
|
||||||
.post_exec::<Self, EM, OT, OF, Z>(
|
|
||||||
input,
|
|
||||||
&mut *self.inner.inner.observers_mut(),
|
|
||||||
&mut exit_kind,
|
|
||||||
);
|
|
||||||
Ok(exit_kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, H, OT, ET, S> UsesState for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
{
|
|
||||||
type State = S;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, H, OT, ET, S> UsesObservers for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
{
|
|
||||||
type Observers = OT;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CM, EH, H, OT, ET, S> HasObservers for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
|
|
||||||
where
|
|
||||||
CM: CommandManager<EH, ET, S>,
|
|
||||||
EH: EmulatorExitHandler<ET, S>,
|
|
||||||
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
|
|
||||||
S: Unpin + State + HasExecutions,
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
|
|
||||||
self.inner.observers()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observers_mut(&mut self) -> RefIndexable<&mut Self::Observers, Self::Observers> {
|
|
||||||
self.inner.observers_mut()
|
|
||||||
}
|
|
||||||
}
|
|
@ -218,13 +218,6 @@ where
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
harness_bytes(buf);
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.use_cmplog.unwrap_or(false) {
|
if self.use_cmplog.unwrap_or(false) {
|
||||||
let modules = {
|
let modules = {
|
||||||
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
||||||
@ -237,7 +230,14 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut emulator = Emulator::new_with_qemu(
|
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let buf = target.as_slice();
|
||||||
|
harness_bytes(buf);
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
let emulator = Emulator::new_with_qemu(
|
||||||
qemu,
|
qemu,
|
||||||
modules,
|
modules,
|
||||||
NopEmulatorExitHandler,
|
NopEmulatorExitHandler,
|
||||||
@ -245,7 +245,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
@ -347,7 +347,14 @@ where
|
|||||||
} else {
|
} else {
|
||||||
let tools = tuple_list!(EdgeCoverageModule::default());
|
let tools = tuple_list!(EdgeCoverageModule::default());
|
||||||
|
|
||||||
let mut emulator = Emulator::new_with_qemu(
|
let mut harness = |_emulator: &mut Emulator<_, _, _, _>, input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let buf = target.as_slice();
|
||||||
|
harness_bytes(buf);
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
let emulator = Emulator::new_with_qemu(
|
||||||
qemu,
|
qemu,
|
||||||
tools,
|
tools,
|
||||||
NopEmulatorExitHandler,
|
NopEmulatorExitHandler,
|
||||||
@ -355,7 +362,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(edges_observer, time_observer),
|
tuple_list!(edges_observer, time_observer),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user