Qemu helpers & hooks refactoring (#2267)

* Helper is now called Module.

* Emulator now contains hooks state.

* Emulator is managed by QemuExecutor.

* QEMU hooks have been completely refactored on the rust side.

* Generics cleanup.
This commit is contained in:
Romain Malmain 2024-07-17 11:46:42 +02:00 committed by GitHub
parent f5e47c33fb
commit c96ea616fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 4276 additions and 3688 deletions

View File

@ -46,13 +46,15 @@ use libafl_bolts::{
AsSlice, AsSliceMut,
};
use libafl_qemu::{
cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper},
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
command::NopCommandManager,
elf::EasyElf,
filter_qemu_args,
hooks::QemuHooks,
GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor, QemuShutdownCause,
Regs,
modules::{
cmplog::{CmpLogChildModule, CmpLogMap, CmpLogObserver},
edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
},
Emulator, GuestReg, MmapPerms, NopEmulatorExitHandler, QemuExitError, QemuExitReason,
QemuForkExecutor, QemuShutdownCause, Regs,
};
#[cfg(unix)]
use nix::unistd::dup;
@ -148,7 +150,22 @@ fn fuzz(
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&args, &env)?;
let emulator_modules = tuple_list!(
EdgeCoverageChildModule::default(),
CmpLogChildModule::default(),
);
let mut emulator = Emulator::new(
args.as_slice(),
env.as_slice(),
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let qemu = emulator.qemu();
let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
@ -217,7 +234,9 @@ fn fuzz(
// Beginning of a page should be properly aligned.
#[allow(clippy::cast_ptr_alignment)]
let cmplog_map_ptr = cmplog.as_mut_ptr().cast::<libafl_qemu::cmplog::CmpLogMap>();
let cmplog_map_ptr = cmplog
.as_mut_ptr()
.cast::<libafl_qemu::modules::cmplog::CmpLogMap>();
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{
@ -336,16 +355,8 @@ fn fuzz(
ExitKind::Ok
};
let mut hooks = QemuHooks::new(
qemu.clone(),
tuple_list!(
QemuEdgeCoverageChildHelper::default(),
QemuCmpLogChildHelper::default(),
),
);
let executor = QemuForkExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,

View File

@ -46,18 +46,19 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
// asan::{init_with_asan, QemuAsanHelper},
cmplog::{CmpLogObserver, QemuCmpLogHelper},
edges::edges_map_mut_ptr,
edges::QemuEdgeCoverageHelper,
edges::{EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
command::NopCommandManager,
elf::EasyElf,
filter_qemu_args,
hooks::QemuHooks,
// asan::{init_with_asan, QemuAsanHelper},
modules::cmplog::{CmpLogModule, CmpLogObserver},
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
Emulator,
GuestReg,
//snapshot::QemuSnapshotHelper,
MmapPerms,
NopEmulatorExitHandler,
Qemu,
QemuExecutor,
QemuExitError,
@ -358,19 +359,19 @@ fn fuzz(
ExitKind::Ok
};
let mut hooks = QemuHooks::new(
qemu.clone(),
tuple_list!(
QemuEdgeCoverageHelper::default(),
QemuCmpLogHelper::default(),
let modules = tuple_list!(
EdgeCoverageModule::default(),
CmpLogModule::default(),
// QemuAsanHelper::default(asan),
//QemuSnapshotHelper::new()
),
);
let mut emulator =
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
let executor = QemuExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,

View File

@ -5,7 +5,7 @@ macro_rules! assert_unique_feature {
() => {};
($first:tt $(,$rest:tt)*) => {
$(
#[cfg(all(not(any(doc, feature = "clippy")), feature = $first, feature = $rest))]
#[cfg(all(not(doc), feature = $first, feature = $rest))]
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)*
assert_unique_feature!($($rest),*);

View File

@ -27,10 +27,12 @@ use libafl_bolts::{
AsSlice, AsSliceMut,
};
use libafl_qemu::{
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
command::NopCommandManager,
elf::EasyElf,
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError,
QemuExitReason, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs,
modules::edges::{EdgeCoverageChildModule, EDGES_MAP_PTR, EDGES_MAP_SIZE_IN_USE},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms,
NopEmulatorExitHandler, Qemu, QemuExitError, QemuExitReason, QemuForkExecutor,
QemuShutdownCause, Regs,
};
#[derive(Default)]
@ -65,7 +67,7 @@ impl From<Version> for Str {
name = format ! ("qemu_cmin-{}", env ! ("CPU_TARGET")),
version = Version::default(),
about,
long_about = "Tool for generating minimizing corpus using QEMU instrumentation"
long_about = "Module for generating minimizing corpus using QEMU instrumentation"
)]
pub struct FuzzerOptions {
#[arg(long, help = "Output directory")]
@ -219,10 +221,13 @@ pub fn fuzz() -> Result<(), Error> {
ExitKind::Ok
};
let mut hooks = QemuHooks::new(qemu, tuple_list!(QemuEdgeCoverageChildHelper::default(),));
let modules = tuple_list!(EdgeCoverageChildModule::default(),);
let mut emulator =
Emulator::new_with_qemu(qemu, modules, NopEmulatorExitHandler, NopCommandManager)?;
let mut executor = QemuForkExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
tuple_list!(edges_observer),
&mut fuzzer,

View File

@ -235,8 +235,8 @@ windows_alias = "unsupported"
[tasks.run_unix]
command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_coverage-${CARGO_MAKE_PROFILE}"
args = [
"--coverage", "${TARGET_DIR}/drcov.log",
"--input", "./corpus",
"--coverage-path", "${TARGET_DIR}/drcov.log",
"--input-dir", "./corpus",
"--",
"${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}",
]

View File

@ -5,7 +5,7 @@ macro_rules! assert_unique_feature {
() => {};
($first:tt $(,$rest:tt)*) => {
$(
#[cfg(all(not(any(doc, feature = "clippy")), feature = $first, feature = $rest))]
#[cfg(all(not(doc), feature = $first, feature = $rest))]
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)*
assert_unique_feature!($($rest),*);

View File

@ -26,11 +26,13 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
drcov::QemuDrCovHelper, elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg,
MmapPerms, Qemu, QemuExecutor, QemuExitReason, QemuHooks,
QemuInstrumentationAddressRangeFilter, QemuRWError, QemuShutdownCause, Regs,
command::NopCommandManager,
elf::EasyElf,
modules::{drcov::DrCovModule, QemuInstrumentationAddressRangeFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms,
NopEmulatorExitHandler, Qemu, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause,
Regs,
};
use rangemap::RangeMap;
#[derive(Default)]
pub struct Version;
@ -69,14 +71,14 @@ impl From<Version> for Str {
name = format!("qemu_coverage-{}",env!("CPU_TARGET")),
version = Version::default(),
about,
long_about = "Tool for generating DrCov coverage data using QEMU instrumentation"
long_about = "Module for generating DrCov coverage data using QEMU instrumentation"
)]
pub struct FuzzerOptions {
#[arg(long, help = "Coverage file")]
coverage: String,
coverage_path: PathBuf,
#[arg(long, help = "Input directory")]
input: String,
input_dir: PathBuf,
#[arg(long, help = "Timeout in seconds", default_value = "5000", value_parser = timeout_from_millis_str)]
timeout: Duration,
@ -99,9 +101,8 @@ pub const MAX_INPUT_SIZE: usize = 1048576; // 1MB
pub fn fuzz() {
let mut options = FuzzerOptions::parse();
let corpus_dir = PathBuf::from(options.input);
let corpus_files = corpus_dir
let corpus_files = options
.input_dir
.read_dir()
.expect("Failed to read corpus dir")
.collect::<Result<Vec<DirEntry>, io::Error>>()
@ -119,6 +120,7 @@ pub fn fuzz() {
env::remove_var("LD_LIBRARY_PATH");
let env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&options.args, &env).unwrap();
let mut elf_buffer = Vec::new();
@ -227,40 +229,28 @@ pub fn fuzz() {
let scheduler = QueueScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let rangemap = qemu
.mappings()
.filter_map(|m| {
m.path()
.map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string()))
.filter(|(_, p)| !p.is_empty())
})
.enumerate()
.fold(
RangeMap::<usize, (u16, String)>::new(),
|mut rm, (i, (r, p))| {
rm.insert(r, (i as u16, p));
rm
},
);
let mut coverage = PathBuf::from(&options.coverage);
let coverage_name = coverage.file_stem().unwrap().to_str().unwrap();
let coverage_extension = coverage.extension().unwrap_or_default().to_str().unwrap();
let mut cov_path = options.coverage_path.clone();
let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap();
let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap();
let core = core_id.0;
coverage.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
let mut hooks = QemuHooks::new(
qemu,
tuple_list!(QemuDrCovHelper::new(
let emulator_modules = tuple_list!(DrCovModule::new(
QemuInstrumentationAddressRangeFilter::None,
rangemap,
coverage,
cov_path,
false,
)),
);
));
let mut emulator = Emulator::new_with_qemu(
qemu,
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let mut executor = QemuExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
(),
&mut fuzzer,
@ -274,7 +264,7 @@ pub fn fuzz() {
state
.load_initial_inputs_by_filenames(&mut fuzzer, &mut executor, &mut mgr, &files)
.unwrap_or_else(|_| {
println!("Failed to load initial corpus at {:?}", &corpus_dir);
println!("Failed to load initial corpus at {:?}", &options.input_dir);
process::exit(0);
});
log::debug!("We imported {} inputs from disk.", state.corpus().count());

View File

@ -9,14 +9,17 @@ use libafl::{
};
use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list};
#[cfg(feature = "injections")]
use libafl_qemu::injections::QemuInjectionHelper;
use libafl_qemu::modules::injections::InjectionModule;
use libafl_qemu::{
asan::{init_qemu_with_asan, QemuAsanHelper},
asan_guest::{init_qemu_with_asan_guest, QemuAsanGuestHelper},
cmplog::QemuCmpLogHelper,
edges::QemuEdgeCoverageHelper,
elf::EasyElf,
ArchExtras, GuestAddr, Qemu, QemuInstrumentationAddressRangeFilter,
modules::{
asan::{init_qemu_with_asan, AsanModule},
asan_guest::{init_qemu_with_asan_guest, AsanGuestModule},
cmplog::CmpLogModule,
edges::EdgeCoverageModule,
QemuInstrumentationAddressRangeFilter,
},
ArchExtras, GuestAddr, Qemu,
};
use crate::{
@ -134,25 +137,25 @@ impl<'a> Client<'a> {
log::debug!("start_pc @ {start_pc:#x}");
#[cfg(not(feature = "injections"))]
let injection_helper = None;
let injection_module = None;
#[cfg(feature = "injections")]
let injection_helper = self
let injection_module = self
.options
.injections
.as_ref()
.and_then(|injections_file| {
let lower = injections_file.to_lowercase();
if lower.ends_with("yaml") || lower.ends_with("yml") {
Some(QemuInjectionHelper::from_yaml(injections_file).unwrap())
Some(InjectionModule::from_yaml(injections_file).unwrap())
} else if lower.ends_with("toml") {
Some(QemuInjectionHelper::from_toml(injections_file).unwrap())
Some(InjectionModule::from_toml(injections_file).unwrap())
} else {
None
}
});
let extra_tokens = injection_helper.as_ref().map(|h| h.tokens.clone());
let extra_tokens = injection_module.as_ref().map(|h| h.tokens.clone());
qemu.entry_break(start_pc);
@ -164,7 +167,7 @@ impl<'a> Client<'a> {
let is_cmplog = self.options.is_cmplog_core(core_id);
let edge_coverage_helper = QemuEdgeCoverageHelper::new(self.coverage_filter(&qemu)?);
let edge_coverage_module = EdgeCoverageModule::new(self.coverage_filter(&qemu)?);
let instance = Instance::builder()
.options(self.options)
@ -174,96 +177,96 @@ impl<'a> Client<'a> {
.extra_tokens(extra_tokens);
if is_asan && is_cmplog {
if let Some(injection_helper) = injection_helper {
if let Some(injection_module) = injection_module {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuCmpLogHelper::default(),
QemuAsanHelper::default(asan.take().unwrap()),
injection_helper,
edge_coverage_module,
CmpLogModule::default(),
AsanModule::default(asan.take().unwrap()),
injection_module,
),
state,
)
} else {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuCmpLogHelper::default(),
QemuAsanHelper::default(asan.take().unwrap()),
edge_coverage_module,
CmpLogModule::default(),
AsanModule::default(asan.take().unwrap()),
),
state,
)
}
} else if is_asan_guest && is_cmplog {
if let Some(injection_helper) = injection_helper {
if let Some(injection_module) = injection_module {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuCmpLogHelper::default(),
QemuAsanGuestHelper::default(&qemu, asan_lib.take().unwrap()),
injection_helper
edge_coverage_module,
CmpLogModule::default(),
AsanGuestModule::default(&qemu, asan_lib.take().unwrap()),
injection_module
),
state,
)
} else {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuCmpLogHelper::default(),
QemuAsanGuestHelper::default(&qemu, asan_lib.take().unwrap()),
edge_coverage_module,
CmpLogModule::default(),
AsanGuestModule::default(&qemu, asan_lib.take().unwrap()),
),
state,
)
}
} else if is_asan {
if let Some(injection_helper) = injection_helper {
if let Some(injection_module) = injection_module {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuAsanHelper::default(asan.take().unwrap()),
injection_helper
edge_coverage_module,
AsanModule::default(asan.take().unwrap()),
injection_module
),
state,
)
} else {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuAsanHelper::default(asan.take().unwrap()),
edge_coverage_module,
AsanModule::default(asan.take().unwrap()),
),
state,
)
}
} else if is_asan_guest {
let helpers = tuple_list!(
edge_coverage_helper,
QemuAsanGuestHelper::default(&qemu, asan_lib.take().unwrap())
let modules = tuple_list!(
edge_coverage_module,
AsanGuestModule::default(&qemu, asan_lib.take().unwrap())
);
instance.build().run(helpers, state)
instance.build().run(modules, state)
} else if is_cmplog {
if let Some(injection_helper) = injection_helper {
if let Some(injection_module) = injection_module {
instance.build().run(
tuple_list!(
edge_coverage_helper,
QemuCmpLogHelper::default(),
injection_helper
edge_coverage_module,
CmpLogModule::default(),
injection_module
),
state,
)
} else {
instance.build().run(
tuple_list!(edge_coverage_helper, QemuCmpLogHelper::default()),
tuple_list!(edge_coverage_module, CmpLogModule::default()),
state,
)
}
} else if let Some(injection_helper) = injection_helper {
} else if let Some(injection_module) = injection_module {
instance
.build()
.run(tuple_list!(edge_coverage_helper, injection_helper), state)
.run(tuple_list!(edge_coverage_module, injection_module), state)
} else {
instance
.build()
.run(tuple_list!(edge_coverage_helper), state)
.run(tuple_list!(edge_coverage_module), state)
}
}
}

View File

@ -38,10 +38,13 @@ use libafl_bolts::{
tuples::{tuple_list, Merge},
};
use libafl_qemu::{
command::NopCommandManager,
modules::{
cmplog::CmpLogObserver,
edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
helpers::QemuHelperTuple,
Qemu, QemuExecutor, QemuHooks,
EmulatorModuleTuple,
},
Emulator, NopEmulatorExitHandler, Qemu, QemuExecutor,
};
use typed_builder::TypedBuilder;
@ -68,12 +71,10 @@ pub struct Instance<'a, M: Monitor> {
}
impl<'a, M: Monitor> Instance<'a, M> {
pub fn run<QT>(&mut self, helpers: QT, state: Option<ClientState>) -> Result<(), Error>
pub fn run<ET>(&mut self, modules: ET, state: Option<ClientState>) -> Result<(), Error>
where
QT: QemuHelperTuple<ClientState> + Debug,
ET: EmulatorModuleTuple<ClientState> + Debug,
{
let mut hooks = QemuHooks::new(*self.qemu, helpers);
// Create an observation channel using the coverage map
let edges_observer = unsafe {
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
@ -153,10 +154,18 @@ impl<'a, M: Monitor> Instance<'a, M> {
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut emulator = Emulator::new_with_qemu(
*self.qemu,
modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
if self.options.is_cmplog_core(self.core_id) {
// Create a QEMU in-process executor
let executor = QemuExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
observers,
&mut fuzzer,
@ -194,7 +203,7 @@ impl<'a, M: Monitor> Instance<'a, M> {
} else {
// Create a QEMU in-process executor
let mut executor = QemuExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
observers,
&mut fuzzer,

View File

@ -30,12 +30,13 @@ use libafl_bolts::{
use libafl_qemu::{
breakpoint::Breakpoint,
command::{EndCommand, StartCommand, StdCommandManager},
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
elf::EasyElf,
emu::Emulator,
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
EmulatorMemoryChunk, FastSnapshotManager, GuestPhysAddr, GuestReg, QemuHooks,
StdEmulatorExitHandler,
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
FastSnapshotManager, GuestPhysAddr, GuestReg, QemuMemoryChunk, StdEmulatorExitHandler,
};
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
@ -98,14 +99,17 @@ pub fn fuzz() {
// Choose Command Manager
let cmd_manager = StdCommandManager::new();
// Instantiate hooks
let modules = tuple_list!(EdgeCoverageModule::default());
// Create emulator
let emu = Emulator::new(&args, &env, emu_exit_handler, cmd_manager).unwrap();
let mut emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap();
// Set breakpoints of interest with corresponding commands.
emu.add_breakpoint(
Breakpoint::with_command(
main_addr,
StartCommand::new(EmulatorMemoryChunk::phys(
StartCommand::new(QemuMemoryChunk::phys(
input_addr,
unsafe { MAX_INPUT_SIZE } as GuestReg,
None,
@ -124,8 +128,10 @@ pub fn fuzz() {
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness =
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
emu.run(input, qemu_executor_state)
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _, _, _>| unsafe {
qemu_executor_state
.emulator_mut()
.run(input)
.unwrap()
.try_into()
.unwrap()
@ -182,11 +188,6 @@ pub fn fuzz() {
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut hooks = QemuHooks::new(
emu.qemu().clone(),
tuple_list!(QemuEdgeCoverageHelper::default()),
);
// Setup an havoc mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let calibration_feedback = MaxMapFeedback::new(&edges_observer);
@ -197,7 +198,7 @@ pub fn fuzz() {
// Create a QEMU in-process executor
let mut executor = StatefulQemuExecutor::new(
&mut hooks,
&mut emu,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,

View File

@ -30,10 +30,14 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
command::NopCommandManager,
elf::EasyElf,
Qemu, QemuExecutor, QemuExitError, QemuExitReason, QemuHooks, QemuRWError, QemuShutdownCause,
Regs,
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
Emulator, NopEmulatorExitHandler, QemuExitError, QemuExitReason, QemuRWError,
QemuShutdownCause, Regs,
};
use libafl_qemu_sys::GuestPhysAddr;
@ -86,15 +90,29 @@ pub fn fuzz() {
// Initialize QEMU
let args: Vec<String> = env::args().collect();
let env: Vec<(String, String)> = env::vars().collect();
let qemu = Qemu::init(&args, &env).unwrap();
let emulator_modules = tuple_list!(EdgeCoverageModule::default());
let mut emulator = Emulator::new(
args.as_slice(),
env.as_slice(),
emulator_modules,
NopEmulatorExitHandler,
NopCommandManager,
)
.unwrap();
let qemu = emulator.qemu();
qemu.set_breakpoint(main_addr);
unsafe {
match qemu.run() {
Ok(QemuExitReason::Breakpoint(_)) => {}
_ => panic!("Unexpected QEMU exit."),
}
}
qemu.remove_breakpoint(main_addr);
qemu.set_breakpoint(breakpoint); // BREAKPOINT
@ -111,7 +129,8 @@ pub fn fuzz() {
let snap = qemu.create_fast_snapshot(true);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| {
let mut harness = |input: &BytesInput, state: &mut QemuExecutorState<_, _, _, _>| {
let emulator = state.emulator_mut();
let target = input.target_bytes();
let mut buf = target.as_slice();
let len = buf.len();
@ -123,7 +142,7 @@ pub fn fuzz() {
qemu.write_phys_mem(input_addr, buf);
match qemu.run() {
match emulator.qemu().run() {
Ok(QemuExitReason::Breakpoint(_)) => {}
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
Signal::SigInterrupt,
@ -209,12 +228,9 @@ pub fn fuzz() {
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut hooks =
QemuHooks::new(qemu.clone(), tuple_list!(QemuEdgeCoverageHelper::default()));
// Create a QEMU in-process executor
let mut executor = QemuExecutor::new(
&mut hooks,
let mut executor = StatefulQemuExecutor::new(
&mut emulator,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,

View File

@ -28,10 +28,12 @@ use libafl_bolts::{
};
use libafl_qemu::{
command::StdCommandManager,
edges::{edges_map_mut_ptr, QemuEdgeCoverageHelper, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
emu::Emulator,
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
FastSnapshotManager, QemuHooks, StdEmulatorExitHandler,
modules::edges::{
edges_map_mut_ptr, EdgeCoverageModule, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND,
},
FastSnapshotManager, StdEmulatorExitHandler,
};
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
@ -61,15 +63,20 @@ pub fn fuzz() {
let cmd_manager = StdCommandManager::new();
let emu = Emulator::new(&args, &env, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
// Choose modules to use
let modules = tuple_list!(EdgeCoverageModule::default());
let mut emu = Emulator::new(&args, &env, modules, emu_exit_handler, cmd_manager).unwrap(); // Create the emulator
let devices = emu.list_devices();
println!("Devices = {:?}", devices);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness =
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
emu.run(input, qemu_executor_state)
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _, _, _>| unsafe {
qemu_executor_state
.emulator_mut()
.run(input)
.unwrap()
.try_into()
.unwrap()
@ -126,11 +133,6 @@ pub fn fuzz() {
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
let mut hooks = QemuHooks::new(
emu.qemu().clone(),
tuple_list!(QemuEdgeCoverageHelper::default()),
);
// Setup an havoc mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations());
let calibration_feedback = MaxMapFeedback::new(&edges_observer);
@ -141,7 +143,7 @@ pub fn fuzz() {
// Create a QEMU in-process executor
let mut executor = StatefulQemuExecutor::new(
&mut hooks,
&mut emu,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,

View File

@ -125,7 +125,7 @@ where
}
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()(input, &mut self.exposed_executor_state);
self.inner.hooks.post_exec_all(state, input);
self.inner.leave_target(fuzzer, state, mgr, input);

View File

@ -95,7 +95,13 @@ pub fn generate(
) -> Result<Bindings, BindgenError> {
let wrapper_h = build_dir.join("wrapper.h");
store_generated_content_if_different(&wrapper_h, WRAPPER_HEADER.as_bytes(), None, None, false);
store_generated_content_if_different(
&wrapper_h,
WRAPPER_HEADER.as_bytes(),
None,
vec![],
false,
);
let bindings = bindgen::Builder::default()
.derive_debug(true)

View File

@ -9,9 +9,9 @@ use which::which;
use crate::cargo_add_rpath;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "9d2197b73bf5e66e709f9f1669467d5c84062da0";
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
pub const QEMU_REVISION: &str = "4cafaa9a087dae6674b0fdc11ba34d3e6a8364d2";
#[allow(clippy::module_name_repetitions)]
pub struct BuildResult {

View File

@ -25,6 +25,9 @@ mod build;
pub use build::build;
#[rustversion::nightly]
use crate::build::QEMU_REVISION;
const LLVM_VERSION_MAX: i32 = 33;
static mut CARGO_RPATH: Option<Vec<String>> = None;
@ -257,7 +260,7 @@ pub fn store_generated_content_if_different(
file_to_update: &Path,
fresh_content: &[u8],
content_file_to_update: Option<Vec<u8>>,
first_line_prefix: Option<&str>,
first_line_prefixes: Vec<&str>,
force_regeneration: bool,
) {
let mut must_rewrite_file = true;
@ -299,7 +302,12 @@ pub fn store_generated_content_if_different(
};
if must_rewrite_file {
if let Some(prefix) = first_line_prefix {
println!(
"cargo::warning={} has been regenerated.",
file_to_update.file_name().unwrap().to_str().unwrap()
);
for prefix in first_line_prefixes {
writeln!(&file_to_check, "{prefix}").expect("Could not write prefix");
}
@ -309,6 +317,79 @@ pub fn store_generated_content_if_different(
}
}
#[rustversion::nightly]
fn parse_stub(
stub_bindings_file: &Path,
current_rustc_version: &Version,
) -> (bool, bool, Option<Vec<u8>>) {
let semver_re = Regex::new(r"/\* (.*) \*/").unwrap();
let qemu_hash_re = Regex::new(r"/\* qemu git hash: (.*) \*/").unwrap();
if let Ok(stub_file) = File::open(stub_bindings_file) {
let mut stub_rdr = BufReader::new(stub_file);
let mut first_line = String::new(); // rustc version
let mut second_line = String::new(); // qemu hash
let mut stub_content = Vec::<u8>::new();
assert!(
stub_rdr
.read_line(&mut first_line)
.expect("Could not read first line")
> 0,
"Error while reading first line."
);
assert!(
stub_rdr
.read_line(&mut second_line)
.expect("Could not read second line")
> 0,
"Error while reading second line."
);
if let Some((_, [version_str])) = semver_re
.captures_iter(&first_line)
.next()
.map(|caps| caps.extract())
{
// The first line matches the regex
if let Some((_, [qemu_hash_str])) = qemu_hash_re
.captures_iter(&second_line)
.next()
.map(|caps| caps.extract())
{
// The second line matches the regex
if let Ok(version) = Version::parse(version_str) {
// The first line contains a version
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
return (
(current_rustc_version > &version) || (qemu_hash_str != QEMU_REVISION),
false,
Some(stub_content),
);
}
}
}
stub_rdr.seek(SeekFrom::Start(0)).unwrap();
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(true, true, Some(stub_content))
} else {
// No stub file stored
(true, true, None)
}
}
#[rustversion::nightly]
pub fn maybe_generate_stub_bindings(
cpu_target: &str,
@ -319,58 +400,11 @@ pub fn maybe_generate_stub_bindings(
if env::var("CARGO_CFG_DOC").is_ok() && cpu_target == "x86_64" && emulation_mode == "usermode" {
let current_rustc_version =
rustc_version::version().expect("Could not get current rustc version");
let semver_re = Regex::new(r"/\* (.*) \*/").unwrap();
// We only try to store the stub if the current rustc version is strictly bigger than the one used to generate
// the versioned stub.
let (try_generate, force_regeneration, stub_content): (bool, bool, Option<Vec<u8>>) =
if let Ok(stub_file) = File::open(stub_bindings_file) {
let mut stub_rdr = BufReader::new(stub_file);
let mut first_line = String::new();
let mut stub_content = Vec::<u8>::new();
assert!(
stub_rdr
.read_line(&mut first_line)
.expect("Could not read first line")
> 0,
"Error while reading first line."
);
if let Some((_, [version_str])) = semver_re
.captures_iter(&first_line)
.next()
.map(|caps| caps.extract())
{
// The first line matches the regex
if let Ok(version) = Version::parse(version_str) {
// The first line contains a version
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(current_rustc_version > version, false, Some(stub_content))
} else {
stub_rdr.seek(SeekFrom::Start(0)).unwrap();
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(true, true, Some(stub_content))
}
} else {
stub_rdr.seek(SeekFrom::Start(0)).unwrap();
stub_rdr
.read_to_end(&mut stub_content)
.expect("could not read stub content");
(true, true, Some(stub_content))
}
} else {
// No stub file stored
(true, true, None)
};
// the versioned stub or the qemu hash differs.
let (try_generate, force_regeneration, stub_content) =
parse_stub(stub_bindings_file, &current_rustc_version);
let header = format!("/* {current_rustc_version} */");
@ -381,7 +415,10 @@ pub fn maybe_generate_stub_bindings(
.expect("Could not read generated bindings file")
.as_slice(),
stub_content,
Some(header.as_str()),
vec![
header.as_str(),
format!("/* qemu git hash: {QEMU_REVISION} */").as_str(),
],
force_regeneration,
);
}

View File

@ -21,6 +21,16 @@ use strum_macros::EnumIter;
#[cfg(all(not(feature = "clippy"), target_os = "linux"))]
mod bindings {
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(improper_ctypes)]
#![allow(unused_mut)]
#![allow(unused)]
#![allow(unused_variables)]
#![allow(clippy::all)]
#![allow(clippy::pedantic)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
#[cfg(all(not(feature = "clippy"), target_os = "linux"))]
@ -107,8 +117,6 @@ macro_rules! extern_c_checked {
use core::ops::BitAnd;
use std::ffi::c_void;
#[cfg(feature = "python")]
use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python};
#[cfg(any(feature = "clippy", not(target_os = "linux")))]
pub use x86_64_stub_bindings::*;
@ -125,19 +133,6 @@ pub type GuestVirtAddr = crate::vaddr;
pub type GuestHwAddrInfo = crate::qemu_plugin_hwaddr;
#[derive(Debug)]
#[repr(C)]
#[cfg(target_os = "linux")]
#[cfg_attr(feature = "python", pyclass(unsendable))]
pub struct MapInfo {
start: GuestAddr,
end: GuestAddr,
offset: GuestAddr,
path: Option<String>,
flags: i32,
is_priv: i32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FatPtr(pub *const c_void, pub *const c_void);
@ -214,80 +209,3 @@ extern_c_checked! {
);
pub fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
}
#[cfg(target_os = "linux")]
#[cfg_attr(feature = "python", pymethods)]
impl MapInfo {
#[must_use]
pub fn start(&self) -> GuestAddr {
self.start
}
#[must_use]
pub fn end(&self) -> GuestAddr {
self.end
}
#[must_use]
pub fn offset(&self) -> GuestAddr {
self.offset
}
#[must_use]
pub fn path(&self) -> Option<&String> {
self.path.as_ref()
}
#[must_use]
pub fn flags(&self) -> MmapPerms {
MmapPerms::try_from(self.flags).unwrap()
}
#[must_use]
pub fn is_priv(&self) -> bool {
self.is_priv != 0
}
}
impl MmapPerms {
#[must_use]
pub fn readable(&self) -> bool {
matches!(
self,
MmapPerms::Read
| MmapPerms::ReadWrite
| MmapPerms::ReadExecute
| MmapPerms::ReadWriteExecute
)
}
#[must_use]
pub fn writable(&self) -> bool {
matches!(
self,
MmapPerms::Write
| MmapPerms::ReadWrite
| MmapPerms::WriteExecute
| MmapPerms::ReadWriteExecute
)
}
#[must_use]
pub fn executable(&self) -> bool {
matches!(
self,
MmapPerms::Execute
| MmapPerms::ReadExecute
| MmapPerms::WriteExecute
| MmapPerms::ReadWriteExecute
)
}
}
#[cfg(feature = "python")]
impl IntoPy<PyObject> for MmapPerms {
fn into_py(self, py: Python) -> PyObject {
let n: i32 = self.into();
n.into_py(py)
}
}

View File

@ -2,9 +2,11 @@ use core::{slice::from_raw_parts, str::from_utf8_unchecked};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use paste::paste;
#[cfg(feature = "python")]
use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python};
use strum_macros::EnumIter;
use crate::{extern_c_checked, libafl_mapinfo, strlen, GuestAddr, MapInfo};
use crate::{extern_c_checked, libafl_mapinfo, strlen, GuestAddr, MmapPerms};
extern_c_checked! {
pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
@ -30,6 +32,95 @@ pub enum VerifyAccess {
Write = libc::PROT_READ | libc::PROT_WRITE,
}
#[derive(Debug)]
#[repr(C)]
#[cfg(target_os = "linux")]
#[cfg_attr(feature = "python", pyclass(unsendable))]
pub struct MapInfo {
start: GuestAddr,
end: GuestAddr,
offset: GuestAddr,
path: Option<String>,
flags: i32,
is_priv: i32,
}
#[cfg(target_os = "linux")]
#[cfg_attr(feature = "python", pymethods)]
impl MapInfo {
#[must_use]
pub fn start(&self) -> GuestAddr {
self.start
}
#[must_use]
pub fn end(&self) -> GuestAddr {
self.end
}
#[must_use]
pub fn offset(&self) -> GuestAddr {
self.offset
}
#[must_use]
pub fn path(&self) -> Option<&String> {
self.path.as_ref()
}
#[must_use]
pub fn flags(&self) -> MmapPerms {
MmapPerms::try_from(self.flags).unwrap()
}
#[must_use]
pub fn is_priv(&self) -> bool {
self.is_priv != 0
}
}
impl MmapPerms {
#[must_use]
pub fn readable(&self) -> bool {
matches!(
self,
MmapPerms::Read
| MmapPerms::ReadWrite
| MmapPerms::ReadExecute
| MmapPerms::ReadWriteExecute
)
}
#[must_use]
pub fn writable(&self) -> bool {
matches!(
self,
MmapPerms::Write
| MmapPerms::ReadWrite
| MmapPerms::WriteExecute
| MmapPerms::ReadWriteExecute
)
}
#[must_use]
pub fn executable(&self) -> bool {
matches!(
self,
MmapPerms::Execute
| MmapPerms::ReadExecute
| MmapPerms::WriteExecute
| MmapPerms::ReadWriteExecute
)
}
}
#[cfg(feature = "python")]
impl IntoPy<PyObject> for MmapPerms {
fn into_py(self, py: Python) -> PyObject {
let n: i32 = self.into();
n.into_py(py)
}
}
impl From<libafl_mapinfo> for MapInfo {
fn from(map_info: libafl_mapinfo) -> Self {
let path: Option<String> = if map_info.path.is_null() {

View File

@ -1,4 +1,5 @@
/* 1.81.0-nightly */
/* qemu git hash: 712661c8200804c0bb0750f237048c6c3da2d863 */
/* automatically generated by rust-bindgen 0.69.4 */
#[repr(C)]
@ -2341,7 +2342,6 @@ pub type DeviceReset = ::std::option::Option<unsafe extern "C" fn(dev: *mut Devi
#[derive(Debug, Copy, Clone)]
pub struct DeviceClass {
pub parent_class: ObjectClass,
#[doc = " @categories: device categories device belongs to"]
pub categories: [::std::os::raw::c_ulong; 1usize],
#[doc = " @fw_name: name used to identify device to firmware interfaces"]
pub fw_name: *const ::std::os::raw::c_char,
@ -13622,7 +13622,7 @@ impl Default for libafl_hook {
}
}
extern "C" {
pub fn libafl_qemu_set_hook(
pub fn libafl_qemu_add_instruction_hooks(
pc: target_ulong,
callback: ::std::option::Option<unsafe extern "C" fn(data: u64, pc: target_ulong)>,
data: u64,
@ -13630,19 +13630,19 @@ extern "C" {
) -> usize;
}
extern "C" {
pub fn libafl_qemu_remove_hooks_at(
pub fn libafl_qemu_remove_instruction_hooks_at(
addr: target_ulong,
invalidate: ::std::os::raw::c_int,
) -> usize;
}
extern "C" {
pub fn libafl_qemu_remove_hook(
pub fn libafl_qemu_remove_instruction_hook(
num: usize,
invalidate: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn libafl_search_hook(addr: target_ulong) -> *mut libafl_hook;
pub fn libafl_search_instruction_hook(addr: target_ulong) -> *mut libafl_hook;
}
extern "C" {
pub fn libafl_add_backdoor_hook(

View File

@ -1,4 +1,5 @@
/* 1.81.0-nightly */
/* qemu git hash: 712661c8200804c0bb0750f237048c6c3da2d863 */
/* automatically generated by rust-bindgen 0.69.4 */
pub const _STDINT_H: u32 = 1;
@ -36,7 +37,7 @@ pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404;
pub const __STDC_ISO_10646__: u32 = 201706;
pub const __GNU_LIBRARY__: u32 = 6;
pub const __GLIBC__: u32 = 2;
pub const __GLIBC_MINOR__: u32 = 38;
pub const __GLIBC_MINOR__: u32 = 39;
pub const _SYS_CDEFS_H: u32 = 1;
pub const __glibc_c99_flexarr_available: u32 = 1;
pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0;
@ -60,6 +61,7 @@ pub const _BITS_TIME64_H: u32 = 1;
pub const _BITS_WCHAR_H: u32 = 1;
pub const _BITS_STDINT_INTN_H: u32 = 1;
pub const _BITS_STDINT_UINTN_H: u32 = 1;
pub const _BITS_STDINT_LEAST_H: u32 = 1;
pub const INT8_MIN: i32 = -128;
pub const INT16_MIN: i32 = -32768;
pub const INT32_MIN: i32 = -2147483648;

View File

@ -14,7 +14,8 @@ use libafl_qemu_sys::GuestAddr;
use crate::{
command::{CommandManager, IsCommand},
EmulatorExitHandler, Qemu, QemuHelperTuple,
modules::EmulatorModuleTuple,
EmulatorExitHandler, Qemu,
};
#[repr(transparent)]
@ -23,16 +24,16 @@ pub struct BreakpointId(u64);
// TODO: distinguish breakpoints with IDs instead of addresses to avoid collisions.
#[derive(Debug)]
pub struct Breakpoint<CM, E, QT, S>
pub struct Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
id: BreakpointId,
addr: GuestAddr,
cmd: Option<Rc<dyn IsCommand<CM, E, QT, S>>>,
cmd: Option<Rc<dyn IsCommand<CM, EH, ET, S>>>,
disable_on_trigger: bool,
enabled: bool,
}
@ -53,81 +54,81 @@ impl Default for BreakpointId {
}
}
impl<CM, E, QT, S> Hash for Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> Hash for Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl<CM, E, QT, S> PartialEq for Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> PartialEq for Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<CM, E, QT, S> Eq for Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> Eq for Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
}
impl<CM, E, QT, S> Display for Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> Display for Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Breakpoint @vaddr 0x{:x}", self.addr)
}
}
impl<CM, E, QT, S> Borrow<BreakpointId> for Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> Borrow<BreakpointId> for Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn borrow(&self) -> &BreakpointId {
&self.id
}
}
impl<CM, E, QT, S> Borrow<GuestAddr> for Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> Borrow<GuestAddr> for Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn borrow(&self) -> &GuestAddr {
&self.addr
}
}
impl<CM, E, QT, S> Breakpoint<CM, E, QT, S>
impl<CM, EH, ET, S> Breakpoint<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
// Emu will return with the breakpoint as exit reason.
#[must_use]
@ -143,7 +144,7 @@ where
// Emu will execute the command when it meets the breakpoint.
#[must_use]
pub fn with_command<C: IsCommand<CM, E, QT, S> + 'static>(
pub fn with_command<C: IsCommand<CM, EH, ET, S> + 'static>(
addr: GuestAddr,
cmd: C,
disable_on_trigger: bool,
@ -167,21 +168,21 @@ where
self.addr
}
pub fn enable(&mut self, qemu: &Qemu) {
pub fn enable(&mut self, qemu: Qemu) {
if !self.enabled {
qemu.set_breakpoint(self.addr);
self.enabled = true;
}
}
pub fn disable(&mut self, qemu: &Qemu) {
pub fn disable(&mut self, qemu: Qemu) {
if self.enabled {
qemu.remove_breakpoint(self.addr.into());
self.enabled = false;
}
}
pub fn trigger(&mut self, qemu: &Qemu) -> Option<Rc<dyn IsCommand<CM, E, QT, S>>> {
pub fn trigger(&mut self, qemu: Qemu) -> Option<Rc<dyn IsCommand<CM, EH, ET, S>>> {
if self.disable_on_trigger {
self.disable(qemu);
}

View File

@ -16,20 +16,21 @@ use libafl_bolts::AsSlice;
use num_enum::TryFromPrimitive;
#[cfg(emulation_mode = "systemmode")]
use crate::QemuInstrumentationPagingFilter;
use crate::modules::QemuInstrumentationPagingFilter;
use crate::{
command::parser::{
EndCommandParser, InputPhysCommandParser, InputVirtCommandParser, LoadCommandParser,
NativeCommandParser, SaveCommandParser, StartPhysCommandParser, StartVirtCommandParser,
VaddrFilterAllowRangeCommandParser, VersionCommandParser,
},
executor::QemuExecutorState,
get_exit_arch_regs,
modules::{
EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
},
sync_exit::ExitArgs,
Emulator, EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult,
GuestReg, HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu,
QemuHelperTuple, QemuInstrumentationAddressRangeFilter, QemuRWError, Regs,
StdEmulatorExitHandler, StdInstrumentationFilter, CPU,
Emulator, EmulatorExitHandler, ExitHandlerError, ExitHandlerResult, GuestReg, InputLocation,
IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorExitHandler, CPU,
};
pub mod parser;
@ -52,21 +53,21 @@ pub const VERSION: u64 = bindings::LIBAFL_QEMU_HDR_VERSION_NUMBER as u64;
macro_rules! define_std_command_manager {
($name:ident, [$($native_command_parser:ident),+]) => {
pub struct $name<QT, S, SM>
pub struct $name<ET, S, SM>
where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
native_command_parsers:
HashMap<GuestReg, Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, QT, S>>>,
HashMap<GuestReg, Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, S>>>,
}
impl<QT, S, SM> $name<QT, S, SM>
impl<ET, S, SM> $name<ET, S, SM>
where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -78,7 +79,7 @@ macro_rules! define_std_command_manager {
dyn NativeCommandParser<
Self,
StdEmulatorExitHandler<SM>,
QT,
ET,
S,
>,
>),*]
@ -87,7 +88,7 @@ macro_rules! define_std_command_manager {
let mut parsers: HashMap<
GuestReg,
Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, QT, S>>,
Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, S>>,
> = HashMap::new();
for parser in native_parsers {
@ -102,17 +103,17 @@ macro_rules! define_std_command_manager {
}
}
impl<QT, S, SM> CommandManager<StdEmulatorExitHandler<SM>, QT, S> for $name<QT, S, SM>
impl<ET, S, SM> CommandManager<StdEmulatorExitHandler<SM>, ET, S> for $name<ET, S, SM>
where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn parse(
&self,
qemu: Qemu,
) -> Result<Rc<dyn IsCommand<Self, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<Self, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let arch_regs_map: &'static EnumMap<ExitArgs, Regs> = get_exit_arch_regs();
let cmd_id: GuestReg = qemu.read_reg::<Regs, GuestReg>(arch_regs_map[ExitArgs::Cmd])?;
@ -126,10 +127,10 @@ macro_rules! define_std_command_manager {
}
}
impl<QT, S, SM> Debug for $name<QT, S, SM>
impl<ET, S, SM> Debug for $name<ET, S, SM>
where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -138,10 +139,10 @@ macro_rules! define_std_command_manager {
}
}
impl<QT, S, SM> Default for $name<QT, S, SM>
impl<ET, S, SM> Default for $name<ET, S, SM>
where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -152,6 +153,19 @@ macro_rules! define_std_command_manager {
};
}
pub struct NopCommandManager;
impl<EH, ET, S> CommandManager<EH, ET, S> for NopCommandManager
where
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn parse(&self, _qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError> {
Ok(Rc::new(NopCommand))
}
}
define_std_command_manager!(
StdCommandManager,
[
@ -167,13 +181,13 @@ define_std_command_manager!(
]
);
pub trait CommandManager<E, QT, S>: Sized
pub trait CommandManager<EH, ET, S>: Sized
where
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn parse(&self, qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, E, QT, S>>, CommandError>;
fn parse(&self, qemu: Qemu) -> Result<Rc<dyn IsCommand<Self, EH, ET, S>>, CommandError>;
}
#[derive(Debug, Clone, Enum, TryFromPrimitive)]
@ -184,12 +198,12 @@ pub enum NativeExitKind {
Crash = bindings::LibaflQemuEndStatus_LIBAFL_QEMU_END_CRASH.0 as u64, // Crash reported in the VM
}
pub trait IsCommand<CM, E, QT, S>: Debug + Display
pub trait IsCommand<CM, EH, ET, S>: Debug + Display
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
/// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of
/// the QEMU VM to run the command.
@ -202,11 +216,10 @@ where
/// - `InnerHandlerResult`: How the high-level handler should behave
fn run(
&self,
emu: &Emulator<CM, E, QT, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
emu: &mut Emulator<CM, EH, ET, S>,
input: &S::Input,
ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, E, QT, S>>, ExitHandlerError>;
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError>;
}
#[cfg(emulation_mode = "systemmode")]
@ -227,14 +240,44 @@ impl From<QemuRWError> for CommandError {
}
}
#[derive(Debug, Clone)]
pub struct NopCommand;
impl Display for NopCommand {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "NopCommand")
}
}
impl<CM, EH, ET, S> IsCommand<CM, EH, ET, S> for NopCommand
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn usable_at_runtime(&self) -> bool {
true
}
fn run(
&self,
_emu: &mut Emulator<CM, EH, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError> {
Ok(None)
}
}
#[derive(Debug, Clone)]
pub struct SaveCommand;
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for SaveCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -244,27 +287,25 @@ where
fn run(
&self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
#[cfg(emulation_mode = "systemmode")] qemu_executor_state: &mut QemuExecutorState<QT, S>,
#[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState<
QT,
S,
>,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let qemu = emu.qemu();
{
let emu_exit_handler = emu.exit_handler().borrow_mut();
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
emu_exit_handler
.set_snapshot_id(snapshot_id)
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?;
}
#[cfg(emulation_mode = "systemmode")]
{
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
let emulator_modules = emu.modules_mut().modules_mut();
let mut allowed_paging_ids = HashSet::new();
@ -273,7 +314,7 @@ where
let paging_filter =
HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut(
qemu_helpers,
emulator_modules,
);
*paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids);
@ -286,11 +327,11 @@ where
#[derive(Debug, Clone)]
pub struct LoadCommand;
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for LoadCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for LoadCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -300,11 +341,10 @@ where
fn run(
&self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let qemu = emu.qemu();
let emu_exit_handler = emu.exit_handler().borrow_mut();
@ -328,15 +368,15 @@ where
#[derive(Debug, Clone)]
pub struct InputCommand {
location: EmulatorMemoryChunk,
location: QemuMemoryChunk,
cpu: CPU,
}
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for InputCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for InputCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -346,11 +386,10 @@ where
fn run(
&self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
input: &S::Input,
ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let qemu = emu.qemu();
@ -366,14 +405,14 @@ where
#[derive(Debug, Clone)]
pub struct StartCommand {
input_location: EmulatorMemoryChunk,
input_location: QemuMemoryChunk,
}
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for StartCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for StartCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -383,11 +422,10 @@ where
fn run(
&self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
input: &S::Input,
ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let emu_exit_handler = emu.exit_handler().borrow_mut();
let qemu = emu.qemu();
@ -420,11 +458,11 @@ where
#[derive(Debug, Clone)]
pub struct EndCommand(Option<ExitKind>);
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for EndCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for EndCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -434,11 +472,10 @@ where
fn run(
&self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let emu_exit_handler = emu.exit_handler().borrow_mut();
@ -462,11 +499,11 @@ where
#[derive(Debug, Clone)]
pub struct VersionCommand(u64);
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for VersionCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for VersionCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -476,11 +513,10 @@ where
fn run(
&self,
_emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let guest_version = self.0;
@ -503,11 +539,11 @@ where
}
#[cfg(emulation_mode = "systemmode")]
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for PagingFilterCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for PagingFilterCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -517,16 +553,15 @@ where
fn run(
&self,
_emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
let qemu_modules = emu.modules_mut().modules_mut();
let paging_filter =
HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut(qemu_helpers);
HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut(qemu_modules);
*paging_filter = self.filter.clone();
@ -534,11 +569,11 @@ where
}
}
impl<CM, QT, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S> for AddressRangeFilterCommand
impl<CM, ET, S, SM> IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S> for AddressRangeFilterCommand
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -549,17 +584,16 @@ where
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
fn run(
&self,
_emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
_emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_input: &S::Input,
_ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, QT, S>>, ExitHandlerError>
) -> Result<Option<ExitHandlerResult<CM, StdEmulatorExitHandler<SM>, ET, S>>, ExitHandlerError>
{
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
let qemu_modules = &mut ();
let addr_range_filter =
HasInstrumentationFilter::<QemuInstrumentationAddressRangeFilter>::filter_mut(
qemu_helpers,
qemu_modules,
);
*addr_range_filter = self.filter.clone();
@ -639,7 +673,7 @@ impl Display for PagingFilterCommand {
impl StartCommand {
#[must_use]
pub fn new(input_location: EmulatorMemoryChunk) -> Self {
pub fn new(input_location: QemuMemoryChunk) -> Self {
Self { input_location }
}
}
@ -653,7 +687,7 @@ impl EndCommand {
impl InputCommand {
#[must_use]
pub fn new(location: EmulatorMemoryChunk, cpu: CPU) -> Self {
pub fn new(location: QemuMemoryChunk, cpu: CPU) -> Self {
Self { location, cpu }
}
}

View File

@ -13,19 +13,22 @@ use crate::{
bindings, CommandError, CommandManager, EndCommand, FilterCommand, InputCommand, IsCommand,
LoadCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand,
},
modules::{
EmulatorModuleTuple, QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
},
sync_exit::ExitArgs,
EmulatorExitHandler, EmulatorMemoryChunk, GuestReg, IsSnapshotManager, Qemu, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter, Regs, StdEmulatorExitHandler, StdInstrumentationFilter,
EmulatorExitHandler, GuestReg, IsSnapshotManager, Qemu, QemuMemoryChunk, Regs,
StdEmulatorExitHandler,
};
pub static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
pub trait NativeCommandParser<CM, E, QT, S>
pub trait NativeCommandParser<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn command_id(&self) -> GuestReg;
@ -33,16 +36,16 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, E, QT, S>>, CommandError>;
) -> Result<Rc<dyn IsCommand<CM, EH, ET, S>>, CommandError>;
}
pub struct InputPhysCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S>
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for InputPhysCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -54,12 +57,12 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(InputCommand::new(
EmulatorMemoryChunk::phys(
QemuMemoryChunk::phys(
input_phys_addr,
max_input_size,
Some(qemu.current_cpu().unwrap()),
@ -70,12 +73,12 @@ where
}
pub struct InputVirtCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S>
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for InputVirtCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -87,24 +90,24 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(InputCommand::new(
EmulatorMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()),
QemuMemoryChunk::virt(input_virt_addr, max_input_size, qemu.current_cpu().unwrap()),
qemu.current_cpu().unwrap(),
)))
}
}
pub struct StartPhysCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S>
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for StartPhysCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -116,11 +119,11 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let input_phys_addr: GuestPhysAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(StartCommand::new(EmulatorMemoryChunk::phys(
Ok(Rc::new(StartCommand::new(QemuMemoryChunk::phys(
input_phys_addr,
max_input_size,
Some(qemu.current_cpu().unwrap()),
@ -129,12 +132,12 @@ where
}
pub struct StartVirtCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S>
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for StartVirtCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -146,11 +149,11 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let input_virt_addr: GuestVirtAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let max_input_size: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;
Ok(Rc::new(StartCommand::new(EmulatorMemoryChunk::virt(
Ok(Rc::new(StartCommand::new(QemuMemoryChunk::virt(
input_virt_addr,
max_input_size,
qemu.current_cpu().unwrap(),
@ -159,11 +162,11 @@ where
}
pub struct SaveCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S> for SaveCommandParser
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for SaveCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -175,17 +178,17 @@ where
&self,
_qemu: Qemu,
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
Ok(Rc::new(SaveCommand))
}
}
pub struct LoadCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S> for LoadCommandParser
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for LoadCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -197,17 +200,17 @@ where
&self,
_qemu: Qemu,
_arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
Ok(Rc::new(LoadCommand))
}
}
pub struct EndCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S> for EndCommandParser
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S> for EndCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -219,7 +222,7 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let native_exit_kind: GuestReg = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let native_exit_kind: Result<NativeExitKind, _> = u64::from(native_exit_kind).try_into();
@ -238,12 +241,12 @@ where
}
pub struct VersionCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S>
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for VersionCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -255,7 +258,7 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let client_version = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
Ok(Rc::new(VersionCommand::new(client_version)))
@ -263,12 +266,12 @@ where
}
pub struct VaddrFilterAllowRangeCommandParser;
impl<CM, QT, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, QT, S>
impl<CM, ET, S, SM> NativeCommandParser<CM, StdEmulatorExitHandler<SM>, ET, S>
for VaddrFilterAllowRangeCommandParser
where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
@ -280,7 +283,7 @@ where
&self,
qemu: Qemu,
arch_regs_map: &'static EnumMap<ExitArgs, Regs>,
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, QT, S>>, CommandError> {
) -> Result<Rc<dyn IsCommand<CM, StdEmulatorExitHandler<SM>, ET, S>>, CommandError> {
let vaddr_start: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg1])?;
let vaddr_end: GuestAddr = qemu.read_reg(arch_regs_map[ExitArgs::Arg2])?;

1240
libafl_qemu/src/emu/hooks.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ use std::{
cell::{OnceCell, Ref, RefCell, RefMut},
hash::Hash,
ops::Add,
pin::Pin,
rc::Rc,
};
@ -17,10 +18,11 @@ use hashbrown::HashMap;
use libafl::{
executors::ExitKind,
inputs::HasTargetBytes,
observers::ObserversTuple,
state::{HasExecutions, State},
};
use libafl_bolts::os::unix_signals::Signal;
use libafl_qemu_sys::{CPUArchStatePtr, GuestUsize};
use libafl_qemu_sys::GuestUsize;
pub use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
#[cfg(emulation_mode = "usermode")]
pub use libafl_qemu_sys::{MapInfo, MmapPerms, MmapPermsIter};
@ -30,15 +32,14 @@ use typed_builder::TypedBuilder;
use crate::{
breakpoint::Breakpoint,
command::{CommandError, InputCommand, IsCommand},
executor::QemuExecutorState,
sync_exit::SyncExit,
sys::TCGTemp,
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, EmulatorMemoryChunk, GuestReg, HookData,
HookId, InstructionHookId, MemAccessInfo, Qemu, QemuExitError, QemuExitReason, QemuHelperTuple,
QemuInitError, QemuRWError, QemuShutdownCause, QemuSnapshotCheckResult, ReadHookId, Regs,
StdInstrumentationFilter, WriteHookId, CPU,
GuestReg, Qemu, QemuExitError, QemuExitReason, QemuInitError, QemuMemoryChunk, QemuRWError,
QemuShutdownCause, QemuSnapshotCheckResult, Regs, CPU,
};
mod hooks;
pub use hooks::*;
#[cfg(emulation_mode = "usermode")]
mod usermode;
@ -47,10 +48,14 @@ mod systemmode;
#[cfg(emulation_mode = "systemmode")]
pub use systemmode::*;
use crate::{breakpoint::BreakpointId, command::CommandManager};
use crate::{
breakpoint::BreakpointId,
command::CommandManager,
modules::{EmulatorModuleTuple, StdInstrumentationFilter},
};
type CommandRef<CM, E, QT, S> = Rc<dyn IsCommand<CM, E, QT, S>>;
type BreakpointMutRef<CM, E, QT, S> = Rc<RefCell<Breakpoint<CM, E, QT, S>>>;
type CommandRef<CM, E, ET, S> = Rc<dyn IsCommand<CM, E, ET, S>>;
type BreakpointMutRef<CM, E, ET, S> = Rc<RefCell<Breakpoint<CM, E, ET, S>>>;
#[derive(Clone, Copy)]
pub enum GuestAddrKind {
@ -59,16 +64,16 @@ pub enum GuestAddrKind {
}
#[derive(Debug, Clone)]
pub enum EmulatorExitResult<CM, E, QT, S>
pub enum EmulatorExitResult<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
QemuExit(QemuShutdownCause), // QEMU ended for some reason.
Breakpoint(Rc<RefCell<Breakpoint<CM, E, QT, S>>>), // Breakpoint triggered. Contains the address of the trigger.
SyncExit(Rc<RefCell<SyncExit<CM, E, QT, S>>>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
Breakpoint(Rc<RefCell<Breakpoint<CM, EH, ET, S>>>), // Breakpoint triggered. Contains the address of the trigger.
SyncExit(Rc<RefCell<SyncExit<CM, EH, ET, S>>>), // Synchronous backdoor: The guest triggered a backdoor and should return to LibAFL.
}
#[derive(Debug, Clone)]
@ -80,17 +85,34 @@ pub enum EmulatorExitError {
}
#[derive(Debug, Clone)]
pub enum ExitHandlerResult<CM, E, QT, S>
pub enum ExitHandlerResult<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
ReturnToHarness(EmulatorExitResult<CM, E, QT, S>), // Return to the harness immediately. Can happen at any point of the run when the handler is not supposed to handle a request.
ReturnToHarness(EmulatorExitResult<CM, EH, ET, S>), // Return to the harness immediately. Can happen at any point of the run when the handler is not supposed to handle a request.
EndOfRun(ExitKind), // The run is over and the emulator is ready for the next iteration.
}
impl<CM, EH, ET, S> ExitHandlerResult<CM, EH, ET, S>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
#[must_use]
#[allow(clippy::match_wildcard_for_single_variants)]
pub fn end_of_run(&self) -> Option<ExitKind> {
match self {
ExitHandlerResult::EndOfRun(exit_kind) => Some(*exit_kind),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub enum ExitHandlerError {
QemuExitReasonError(EmulatorExitError),
@ -115,16 +137,16 @@ pub enum SnapshotManagerCheckError {
SnapshotCheckError(QemuSnapshotCheckResult),
}
impl<CM, E, QT, S> TryFrom<ExitHandlerResult<CM, E, QT, S>> for ExitKind
impl<CM, EH, ET, S> TryFrom<ExitHandlerResult<CM, EH, ET, S>> for ExitKind
where
CM: CommandManager<E, QT, S> + Debug,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S> + Debug,
S: State + HasExecutions + Debug,
CM: CommandManager<EH, ET, S> + Debug,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S> + Debug,
S: Unpin + State + HasExecutions + Debug,
{
type Error = String;
fn try_from(value: ExitHandlerResult<CM, E, QT, S>) -> Result<Self, Self::Error> {
fn try_from(value: ExitHandlerResult<CM, EH, ET, S>) -> Result<Self, Self::Error> {
match value {
ExitHandlerResult::ReturnToHarness(unhandled_qemu_exit) => {
Err(format!("Unhandled QEMU exit: {:?}", &unhandled_qemu_exit))
@ -181,22 +203,19 @@ pub struct SnapshotId {
}
pub trait IsSnapshotManager: Debug + Clone {
fn save(&mut self, qemu: &Qemu) -> SnapshotId;
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: &Qemu,
) -> Result<(), SnapshotManagerError>;
fn save(&mut self, qemu: Qemu) -> SnapshotId;
fn restore(&mut self, snapshot_id: &SnapshotId, qemu: Qemu)
-> Result<(), SnapshotManagerError>;
fn do_check(
&self,
reference_snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError>;
fn check(
&self,
reference_snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<(), SnapshotManagerCheckError> {
let check_result = self
.do_check(reference_snapshot_id, qemu)
@ -210,24 +229,21 @@ pub trait IsSnapshotManager: Debug + Clone {
}
}
// TODO: Rework with generics for command handlers?
pub trait EmulatorExitHandler<QT, S>: Sized + Debug + Clone
pub trait EmulatorExitHandler<ET, S>: Sized + Debug + Clone
where
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn qemu_pre_run<CM: CommandManager<Self, QT, S>>(
emu: &Emulator<CM, Self, QT, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
input: &S::Input,
);
fn qemu_post_run<CM: CommandManager<Self, QT, S>>(
emu: &Emulator<CM, Self, QT, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, QT, S>, EmulatorExitError>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
input: &S::Input,
) -> Result<Option<ExitHandlerResult<CM, Self, QT, S>>, ExitHandlerError>;
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError>;
}
/// Special kind of Exit handler with no data embedded.
@ -236,24 +252,22 @@ where
#[derive(Clone, Debug)]
pub struct NopEmulatorExitHandler;
impl<QT, S> EmulatorExitHandler<QT, S> for NopEmulatorExitHandler
impl<ET, S> EmulatorExitHandler<ET, S> for NopEmulatorExitHandler
where
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn qemu_pre_run<CM: CommandManager<Self, QT, S>>(
_: &Emulator<CM, Self, QT, S>,
_: &mut QemuExecutorState<QT, S>,
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
_: &mut Emulator<CM, Self, ET, S>,
_: &S::Input,
) {
}
fn qemu_post_run<CM: CommandManager<Self, QT, S>>(
_: &Emulator<CM, Self, QT, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, QT, S>, EmulatorExitError>,
_: &mut QemuExecutorState<QT, S>,
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
_: &mut Emulator<CM, Self, ET, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
_: &S::Input,
) -> Result<Option<ExitHandlerResult<CM, Self, QT, S>>, ExitHandlerError> {
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError> {
match exit_reason {
Ok(reason) => Ok(Some(ExitHandlerResult::ReturnToHarness(reason))),
Err(error) => Err(error)?,
@ -263,14 +277,14 @@ where
#[derive(Debug, Clone)]
pub struct InputLocation {
mem_chunk: EmulatorMemoryChunk,
mem_chunk: QemuMemoryChunk,
cpu: CPU,
ret_register: Option<Regs>,
}
impl InputLocation {
#[must_use]
pub fn new(mem_chunk: EmulatorMemoryChunk, cpu: CPU, ret_register: Option<Regs>) -> Self {
pub fn new(mem_chunk: QemuMemoryChunk, cpu: CPU, ret_register: Option<Regs>) -> Self {
Self {
mem_chunk,
cpu,
@ -326,35 +340,37 @@ where
}
// TODO: replace handlers with generics to permit compile-time customization of handlers
impl<QT, S, SM> EmulatorExitHandler<QT, S> for StdEmulatorExitHandler<SM>
impl<ET, S, SM> EmulatorExitHandler<ET, S> for StdEmulatorExitHandler<SM>
where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes,
SM: IsSnapshotManager,
{
fn qemu_pre_run<CM: CommandManager<Self, QT, S>>(
emu: &Emulator<CM, Self, QT, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
fn qemu_pre_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
input: &S::Input,
) {
let input_location = {
let exit_handler = emu.exit_handler.borrow();
exit_handler.input_location.get().cloned()
};
if let Some(input_location) = exit_handler.input_location.get() {
if let Some(input_location) = input_location {
let input_command =
InputCommand::new(input_location.mem_chunk.clone(), input_location.cpu);
input_command
.run(emu, qemu_executor_state, input, input_location.ret_register)
.run(emu, input, input_location.ret_register)
.unwrap();
}
}
fn qemu_post_run<CM: CommandManager<Self, QT, S>>(
emu: &Emulator<CM, Self, QT, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, QT, S>, EmulatorExitError>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
fn qemu_post_exec<CM: CommandManager<Self, ET, S>>(
emu: &mut Emulator<CM, Self, ET, S>,
exit_reason: Result<EmulatorExitResult<CM, Self, ET, S>, EmulatorExitError>,
input: &S::Input,
) -> Result<Option<ExitHandlerResult<CM, Self, QT, S>>, ExitHandlerError> {
) -> Result<Option<ExitHandlerResult<CM, Self, ET, S>>, ExitHandlerError> {
let exit_handler = emu.exit_handler().borrow_mut();
let qemu = emu.qemu();
@ -375,7 +391,7 @@ where
};
#[allow(clippy::type_complexity)]
let (command, ret_reg): (Option<CommandRef<CM, Self, QT, S>>, Option<Regs>) =
let (command, ret_reg): (Option<CommandRef<CM, Self, ET, S>>, Option<Regs>) =
match &mut exit_reason {
EmulatorExitResult::QemuExit(shutdown_cause) => match shutdown_cause {
QemuShutdownCause::HostSignal(signal) => {
@ -399,7 +415,7 @@ where
drop(exit_handler);
if let Some(cmd) = command {
cmd.run(emu, qemu_executor_state, input, ret_reg)
cmd.run(emu, input, ret_reg)
} else {
Ok(Some(ExitHandlerResult::ReturnToHarness(exit_reason)))
}
@ -418,12 +434,12 @@ impl From<CommandError> for ExitHandlerError {
}
}
impl<CM, E, QT, S> Display for EmulatorExitResult<CM, E, QT, S>
impl<CM, EH, ET, S> Display for EmulatorExitResult<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
@ -442,50 +458,57 @@ impl From<CommandError> for EmulatorExitError {
}
}
#[derive(Clone, Debug, TypedBuilder)]
pub struct Emulator<CM, E, QT, S>
// TODO: Replace TypedBuilder by something better, it does not work correctly with default and
// inter-dependent fields.
#[derive(Debug, TypedBuilder)]
pub struct Emulator<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
modules: Pin<Box<EmulatorModules<ET, S>>>,
command_manager: CM,
exit_handler: RefCell<E>,
exit_handler: RefCell<EH>,
#[builder(default)]
breakpoints_by_addr: RefCell<HashMap<GuestAddr, BreakpointMutRef<CM, E, QT, S>>>,
breakpoints_by_addr: RefCell<HashMap<GuestAddr, BreakpointMutRef<CM, EH, ET, S>>>,
#[builder(default)]
breakpoints_by_id: RefCell<HashMap<BreakpointId, BreakpointMutRef<CM, E, QT, S>>>,
breakpoints_by_id: RefCell<HashMap<BreakpointId, BreakpointMutRef<CM, EH, ET, S>>>,
#[builder(setter(transform = |args: &[String], env: &[(String, String)]| Qemu::init(args, env).unwrap()))]
qemu: Qemu,
_phantom: PhantomData<(QT, S)>,
_phantom: PhantomData<(ET, S)>,
}
#[allow(clippy::unused_self)]
impl<CM, E, QT, S> Emulator<CM, E, QT, S>
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
#[allow(clippy::must_use_candidate, clippy::similar_names)]
pub fn new(
args: &[String],
env: &[(String, String)],
exit_handler: E,
modules: ET,
exit_handler: EH,
command_manager: CM,
) -> Result<Self, QemuInitError> {
let qemu = Qemu::init(args, env)?;
Self::new_with_qemu(qemu, exit_handler, command_manager)
Self::new_with_qemu(qemu, modules, exit_handler, command_manager)
}
pub fn new_with_qemu(
qemu: Qemu,
exit_handler: E,
modules: ET,
exit_handler: EH,
command_manager: CM,
) -> Result<Self, QemuInitError> {
Ok(Emulator {
modules: EmulatorModules::new(qemu, modules),
command_manager,
exit_handler: RefCell::new(exit_handler),
breakpoints_by_addr: RefCell::new(HashMap::new()),
@ -495,13 +518,21 @@ where
})
}
#[must_use]
pub fn qemu(&self) -> &Qemu {
&self.qemu
pub fn modules(&self) -> &EmulatorModules<ET, S> {
&self.modules
}
pub fn modules_mut(&mut self) -> &mut EmulatorModules<ET, S> {
self.modules.as_mut().get_mut()
}
#[must_use]
pub fn exit_handler(&self) -> &RefCell<E> {
pub fn qemu(&self) -> Qemu {
self.qemu
}
#[must_use]
pub fn exit_handler(&self) -> &RefCell<EH> {
&self.exit_handler
}
@ -589,9 +620,9 @@ where
self.qemu.read_reg(reg)
}
pub fn add_breakpoint(&self, mut bp: Breakpoint<CM, E, QT, S>, enable: bool) -> BreakpointId {
pub fn add_breakpoint(&self, mut bp: Breakpoint<CM, EH, ET, S>, enable: bool) -> BreakpointId {
if enable {
bp.enable(&self.qemu);
bp.enable(self.qemu);
}
let bp_id = bp.id();
@ -625,7 +656,7 @@ where
.get_mut(&bp_id)
.expect("Did not find the breakpoint")
.borrow_mut();
bp.disable(&self.qemu);
bp.disable(self.qemu);
bp.addr()
};
@ -646,12 +677,31 @@ where
self.qemu.entry_break(addr);
}
pub fn first_exec_all(&mut self) {
self.modules.first_exec_all();
}
pub fn pre_exec_all(&mut self, input: &S::Input) {
self.modules.pre_exec_all(input);
}
pub fn post_exec_all<OT>(
&mut self,
input: &S::Input,
observers: &mut OT,
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
{
self.modules.post_exec_all(input, observers, exit_kind);
}
/// This function will run the emulator until the next breakpoint, or until finish.
/// # Safety
///
/// Should, in general, be safe to call.
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
unsafe fn run_qemu(&self) -> Result<EmulatorExitResult<CM, E, QT, S>, EmulatorExitError> {
pub unsafe fn run_qemu(&self) -> Result<EmulatorExitResult<CM, EH, ET, S>, EmulatorExitError> {
match self.qemu.run() {
Ok(qemu_exit_reason) => Ok(match qemu_exit_reason {
QemuExitReason::End(qemu_shutdown_cause) => {
@ -686,21 +736,26 @@ where
/// Should, in general, be safe to call.
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
pub unsafe fn run(
&self,
&mut self,
input: &S::Input,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
) -> Result<ExitHandlerResult<CM, E, QT, S>, ExitHandlerError> {
) -> Result<ExitHandlerResult<CM, EH, ET, S>, ExitHandlerError> {
loop {
// if self.first_exec {
// self.modules_mut().first_exec_all();
// self.first_exec = false;
// }
// // First run modules callback functions
// self.modules_mut().pre_exec_all(input);
// Insert input if the location is already known
E::qemu_pre_run(self, qemu_executor_state, input);
EH::qemu_pre_exec(self, input);
// Run QEMU
let exit_reason = self.run_qemu();
// Handle QEMU exit
if let Some(exit_handler_result) =
E::qemu_post_run(self, exit_reason, qemu_executor_state, input)?
{
if let Some(exit_handler_result) = EH::qemu_post_exec(self, exit_reason, input)? {
return Ok(exit_handler_result);
}
}
@ -713,123 +768,6 @@ where
self.qemu.flush_jit();
}
// TODO set T lifetime to be like Emulator
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn set_hook<T: Into<HookData>>(
&self,
data: T,
addr: GuestAddr,
callback: extern "C" fn(T, GuestAddr),
invalidate_block: bool,
) -> InstructionHookId {
self.qemu.set_hook(data, addr, callback, invalidate_block)
}
#[must_use]
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn remove_hook(&self, id: impl HookId, invalidate_block: bool) -> bool {
self.qemu.remove_hook(id, invalidate_block)
}
#[must_use]
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn remove_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize {
self.qemu.remove_hooks_at(addr, invalidate_block)
}
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn add_edge_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, GuestAddr) -> u64>,
exec: Option<unsafe extern "C" fn(T, u64)>,
) -> EdgeHookId {
self.qemu.add_edge_hooks(data, gen, exec)
}
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn add_block_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr) -> u64>,
post_gen: Option<unsafe extern "C" fn(T, GuestAddr, GuestUsize)>,
exec: Option<unsafe extern "C" fn(T, u64)>,
) -> BlockHookId {
self.qemu.add_block_hooks(data, gen, post_gen, exec)
}
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn add_read_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
) -> ReadHookId {
self.qemu
.add_read_hooks(data, gen, exec1, exec2, exec4, exec8, exec_n)
}
// TODO add MemOp info
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn add_write_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
) -> WriteHookId {
self.qemu
.add_write_hooks(data, gen, exec1, exec2, exec4, exec8, exec_n)
}
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn add_cmp_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, usize) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, u8, u8)>,
exec2: Option<unsafe extern "C" fn(T, u64, u16, u16)>,
exec4: Option<unsafe extern "C" fn(T, u64, u32, u32)>,
exec8: Option<unsafe extern "C" fn(T, u64, u64, u64)>,
) -> CmpHookId {
self.qemu
.add_cmp_hooks(data, gen, exec1, exec2, exec4, exec8)
}
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."
)]
pub fn add_backdoor_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, CPUArchStatePtr, GuestAddr),
) -> BackdoorHookId {
self.qemu.add_backdoor_hook(data, callback)
}
#[allow(clippy::type_complexity)]
#[deprecated(
note = "This function has been moved to the `Qemu` low-level structure. Please access it through `emu.qemu()`."

View File

@ -8,8 +8,8 @@ use libafl::state::{HasExecutions, State};
use libafl_qemu_sys::GuestPhysAddr;
use crate::{
command::CommandManager, emu::IsSnapshotManager, DeviceSnapshotFilter, Emulator,
EmulatorExitHandler, Qemu, QemuHelperTuple, QemuSnapshotCheckResult, SnapshotId,
command::CommandManager, emu::IsSnapshotManager, modules::EmulatorModuleTuple,
DeviceSnapshotFilter, Emulator, EmulatorExitHandler, Qemu, QemuSnapshotCheckResult, SnapshotId,
SnapshotManagerError,
};
@ -34,7 +34,7 @@ pub enum SnapshotManager {
}
impl IsSnapshotManager for SnapshotManager {
fn save(&mut self, qemu: &Qemu) -> SnapshotId {
fn save(&mut self, qemu: Qemu) -> SnapshotId {
match self {
SnapshotManager::Qemu(qemu_sm) => qemu_sm.save(qemu),
SnapshotManager::Fast(fast_sm) => fast_sm.save(qemu),
@ -44,7 +44,7 @@ impl IsSnapshotManager for SnapshotManager {
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<(), SnapshotManagerError> {
match self {
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(snapshot_id, qemu),
@ -55,7 +55,7 @@ impl IsSnapshotManager for SnapshotManager {
fn do_check(
&self,
reference_snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
match self {
SnapshotManager::Qemu(qemu_sm) => qemu_sm.do_check(reference_snapshot_id, qemu),
@ -105,7 +105,7 @@ impl QemuSnapshotManager {
}
impl IsSnapshotManager for QemuSnapshotManager {
fn save(&mut self, qemu: &Qemu) -> SnapshotId {
fn save(&mut self, qemu: Qemu) -> SnapshotId {
let snapshot_id = SnapshotId::gen_unique_id();
qemu.save_snapshot(
self.snapshot_id_to_name(&snapshot_id).as_str(),
@ -117,7 +117,7 @@ impl IsSnapshotManager for QemuSnapshotManager {
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<(), SnapshotManagerError> {
qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync);
Ok(())
@ -126,7 +126,7 @@ impl IsSnapshotManager for QemuSnapshotManager {
fn do_check(
&self,
_reference_snapshot_id: &SnapshotId,
_qemu: &Qemu,
_qemu: Qemu,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
// We consider the qemu implementation to be 'ideal' for now.
Ok(QemuSnapshotCheckResult::default())
@ -134,7 +134,7 @@ impl IsSnapshotManager for QemuSnapshotManager {
}
impl IsSnapshotManager for FastSnapshotManager {
fn save(&mut self, qemu: &Qemu) -> SnapshotId {
fn save(&mut self, qemu: Qemu) -> SnapshotId {
let snapshot_id = SnapshotId::gen_unique_id();
self.snapshots
.insert(snapshot_id, qemu.create_fast_snapshot(true));
@ -144,7 +144,7 @@ impl IsSnapshotManager for FastSnapshotManager {
fn restore(
&mut self,
snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<(), SnapshotManagerError> {
let fast_snapshot_ptr = *self
.snapshots
@ -161,7 +161,7 @@ impl IsSnapshotManager for FastSnapshotManager {
fn do_check(
&self,
reference_snapshot_id: &SnapshotId,
qemu: &Qemu,
qemu: Qemu,
) -> Result<QemuSnapshotCheckResult, SnapshotManagerError> {
let fast_snapshot_ptr = *self.snapshots.get(reference_snapshot_id).ok_or(
SnapshotManagerError::SnapshotIdNotFound(*reference_snapshot_id),
@ -171,12 +171,12 @@ impl IsSnapshotManager for FastSnapshotManager {
}
}
impl<CM, E, QT, S> Emulator<CM, E, QT, S>
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
/// Write a value to a phsical guest address, including ROM areas.
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {

View File

@ -3,16 +3,16 @@ use libafl_qemu_sys::{GuestAddr, MmapPerms, VerifyAccess};
use crate::{
command::CommandManager,
emu::{HasExecutions, State},
Emulator, EmulatorExitHandler, GuestMaps, HookData, NewThreadHookId, PostSyscallHookId,
PreSyscallHookId, QemuHelperTuple, SyscallHookResult,
modules::EmulatorModuleTuple,
Emulator, EmulatorExitHandler, GuestMaps,
};
impl<CM, E, QT, S> Emulator<CM, E, QT, S>
impl<CM, EH, ET, S> Emulator<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
/// This function gets the memory mappings from the emulator.
#[must_use]
@ -92,58 +92,4 @@ where
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
self.qemu.unmap(addr, size)
}
#[allow(clippy::type_complexity)]
pub fn add_pre_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> SyscallHookResult,
) -> PreSyscallHookId {
self.qemu.add_pre_syscall_hook(data, callback)
}
#[allow(clippy::type_complexity)]
pub fn add_post_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr,
) -> PostSyscallHookId {
self.qemu.add_post_syscall_hook(data, callback)
}
pub fn add_new_thread_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, tid: u32) -> bool,
) -> NewThreadHookId {
self.qemu.add_new_thread_hook(data, callback)
}
#[allow(clippy::type_complexity)]
pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) {
self.qemu.set_crash_hook(callback);
}
}

View File

@ -31,41 +31,48 @@ use libafl_bolts::{
tuples::RefIndexable,
};
use crate::{helpers::QemuHelperTuple, hooks::QemuHooks, Qemu};
#[cfg(emulation_mode = "usermode")]
use crate::emu::EmulatorModules;
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorExitHandler};
/// A version of `QemuExecutor` with a state accessible from the harness.
pub mod stateful;
pub struct QemuExecutorState<'a, QT, S>
pub struct QemuExecutorState<'a, CM, EH, ET, S>
where
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
hooks: &'a mut QemuHooks<QT, S>,
first_exec: bool,
emulator: &'a mut Emulator<CM, EH, ET, S>,
}
pub struct QemuExecutor<'a, H, OT, QT, S>
pub struct QemuExecutor<'a, CM, EH, H, OT, ET, S>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
inner: InProcessExecutor<'a, H, OT, S>,
state: QemuExecutorState<'a, QT, S>,
state: QemuExecutorState<'a, CM, EH, ET, S>,
}
impl<'a, H, OT, QT, S> Debug for QemuExecutor<'a, H, OT, QT, 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: State + HasExecutions,
S: Unpin + State + HasExecutions + Debug,
OT: ObserversTuple<S> + Debug,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuExecutor")
.field("hooks", &self.state.hooks)
.field("emulator", &self.state.emulator)
.field("inner", &self.inner)
.finish()
}
@ -78,7 +85,7 @@ extern "C" {
}
#[cfg(emulation_mode = "usermode")]
pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>(
pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, ET, S>(
signal: Signal,
info: &'a mut siginfo_t,
mut context: Option<&'a mut ucontext_t>,
@ -89,8 +96,8 @@ pub unsafe fn inproc_qemu_crash_handler<'a, E, EM, OF, Z, QT, S>(
OF: Feedback<E::State>,
E::State: HasExecutions + HasSolutions + HasCorpus,
Z: HasObjective<Objective = OF, State = E::State>,
QT: QemuHelperTuple<S> + Debug + 'a,
S: State + HasExecutions + 'a,
ET: EmulatorModuleTuple<S> + Debug + 'a,
S: Unpin + State + HasExecutions + 'a,
{
let puc = match &mut context {
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
@ -129,64 +136,72 @@ pub unsafe fn inproc_qemu_timeout_handler<'a, E, EM, OF, Z>(
}
}
impl<'a, QT, S> QemuExecutorState<'a, QT, S>
impl<'a, CM, EH, ET, S> QemuExecutorState<'a, CM, EH, ET, S>
where
S: State + HasExecutions,
QT: QemuHelperTuple<S> + Debug,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S> + Debug,
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
{
pub fn new<E, EM, OF, OT, Z>(hooks: &'a mut QemuHooks<QT, S>) -> Result<Self, Error>
#[cfg(emulation_mode = "systemmode")]
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>,
{
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>,
S: State + HasExecutions + HasCorpus + HasSolutions,
Z: HasObjective<Objective = OF, State = S> + ExecutionProcessor + HasScheduler,
{
#[cfg(emulation_mode = "usermode")]
{
let handler = |hooks: &mut QemuHooks<QT, S>, host_sig| {
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) = hooks.qemu().current_cpu() {
if let Some(cpu) = emulator_modules.qemu().current_cpu() {
eprint!("Context:\n{}", cpu.display_context());
}
};
hooks.crash_closure(Box::new(handler));
emulator.modules_mut().crash_closure(Box::new(handler));
}
Ok(QemuExecutorState {
first_exec: true,
hooks,
})
Ok(QemuExecutorState { emulator })
}
#[must_use]
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.hooks
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
self.emulator
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.hooks
}
#[must_use]
pub fn qemu(&self) -> &Qemu {
self.hooks.qemu()
pub fn emulator_mut(&mut self) -> &mut Emulator<CM, EH, ET, S> {
self.emulator
}
}
impl<'a, H, OT, QT, S> QemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> QemuExecutor<'a, CM, EH, H, OT, ET, S>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: Unpin + State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<QT, S>,
emulator: &'a mut Emulator<CM, EH, ET, S>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -197,7 +212,7 @@ where
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: State + HasExecutions + HasCorpus + HasSolutions,
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
Z: HasObjective<Objective = OF, State = S> + HasScheduler + ExecutionProcessor,
{
let mut inner = InProcessExecutor::with_timeout(
@ -207,7 +222,7 @@ where
#[cfg(emulation_mode = "usermode")]
{
inner.inprocess_hooks_mut().crash_handler =
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z, QT, S>
inproc_qemu_crash_handler::<InProcessExecutor<'a, H, OT, S>, EM, OF, Z, ET, S>
as *const c_void;
}
@ -219,7 +234,7 @@ where
}
let state =
QemuExecutorState::new::<InProcessExecutor<'a, H, OT, S>, EM, OF, OT, Z>(hooks)?;
QemuExecutorState::new::<InProcessExecutor<'a, H, OT, S>, EM, OF, OT, Z>(emulator)?;
Ok(Self { inner, state })
}
@ -238,43 +253,30 @@ where
pub fn inner_mut(&mut self) -> &mut InProcessExecutor<'a, H, OT, S> {
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.state.hooks()
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.state.hooks_mut()
}
pub fn emulator(&self) -> &Qemu {
self.state.qemu()
}
}
impl<'a, QT, S> QemuExecutorState<'a, QT, S>
impl<'a, CM, EH, ET, S> QemuExecutorState<'a, CM, EH, ET, S>
where
S: State + HasExecutions + HasCorpus + HasSolutions,
QT: QemuHelperTuple<S> + Debug,
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, qemu: Qemu)
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>,
{
if self.first_exec {
self.hooks.helpers().first_exec_all(self.hooks);
self.first_exec = false;
}
self.hooks.helpers_mut().pre_exec_all(qemu, input);
self.emulator.first_exec_all();
self.emulator.pre_exec_all(input);
}
fn post_exec<E, EM, OT, OF, Z>(
&mut self,
input: &E::Input,
qemu: Qemu,
observers: &mut OT,
exit_kind: &mut ExitKind,
) where
@ -284,20 +286,20 @@ where
OF: Feedback<S>,
Z: HasObjective<Objective = OF, State = S>,
{
self.hooks
.helpers_mut()
.post_exec_all(qemu, input, observers, exit_kind);
self.emulator.post_exec_all(input, observers, exit_kind);
}
}
impl<'a, EM, H, OT, OF, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, EM, H, OT, OF, ET, S, Z> Executor<EM, Z> for QemuExecutor<'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) -> ExitKind,
S: State + HasExecutions + HasCorpus + HasSolutions,
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
OT: ObserversTuple<S>,
OF: Feedback<S>,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
Z: HasObjective<Objective = OF, State = S>,
{
fn run_target(
@ -307,12 +309,10 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let qemu = Qemu::get().unwrap();
self.state.pre_exec::<Self, EM, OF, Z>(input, qemu);
self.state.pre_exec::<Self, EM, OF, Z>(input);
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
self.state.post_exec::<Self, EM, OT, OF, Z>(
input,
qemu,
&mut *self.inner.observers_mut(),
&mut exit_kind,
);
@ -320,32 +320,38 @@ where
}
}
impl<'a, H, OT, QT, S> UsesState for QemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> UsesState for QemuExecutor<'a, CM, EH, H, OT, ET, S>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
type State = S;
}
impl<'a, H, OT, QT, S> UsesObservers for QemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> UsesObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
type Observers = OT;
}
impl<'a, H, OT, QT, S> HasObservers for QemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> HasObservers for QemuExecutor<'a, CM, EH, H, OT, ET, S>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: Unpin + State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
#[inline]
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {
@ -359,63 +365,70 @@ where
}
#[cfg(feature = "fork")]
pub struct QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
pub struct QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: Unpin + State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
inner: InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>,
state: QemuExecutorState<'a, QT, S>,
state: QemuExecutorState<'a, CM, EH, ET, S>,
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP, EM, Z> Debug for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> Debug
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S> + Debug,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: Unpin + State + HasExecutions + Debug,
OT: ObserversTuple<S> + Debug,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuForkExecutor")
.field("hooks", &self.state.hooks)
.field("emulator", &self.state.emulator)
.field("inner", &self.inner)
.finish()
}
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP, EM, Z, OF> QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z, OF> QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: Unpin + State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
SP: ShMemProvider,
EM: EventFirer<State = S> + EventRestarter,
OF: Feedback<S>,
S: HasSolutions,
S: Unpin + HasSolutions,
Z: HasObjective<Objective = OF, State = S>,
{
pub fn new(
hooks: &'a mut QemuHooks<QT, S>,
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,
shmem_provider: SP,
timeout: core::time::Duration,
timeout: Duration,
) -> Result<Self, Error> {
assert!(!QT::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded");
assert!(!ET::HOOKS_DO_SIDE_EFFECTS, "When using QemuForkExecutor, the hooks must not do any side effect as they will happen in the child process and then discarded");
Ok(Self {
inner: InProcessForkExecutor::new(
@ -427,10 +440,7 @@ where
timeout,
shmem_provider,
)?,
state: QemuExecutorState {
first_exec: true,
hooks,
},
state: QemuExecutorState { emulator },
})
}
@ -442,28 +452,22 @@ where
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.state.hooks
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.state.hooks
}
pub fn qemu(&self) -> &Qemu {
self.state.hooks.qemu()
pub fn emulator(&self) -> &Emulator<CM, EH, ET, S> {
self.state.emulator
}
}
#[cfg(feature = "fork")]
impl<'a, EM, H, OT, QT, S, Z, SP, OF> Executor<EM, Z>
for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
impl<'a, CM, EH, EM, H, OT, ET, S, Z, SP, OF> Executor<EM, Z>
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
EM: EventManager<InProcessForkExecutor<'a, H, OT, S, SP, EM, Z>, Z, State = S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
S: Unpin + State + HasMetadata + HasExecutions + HasLastReportTime + HasCorpus + HasSolutions,
OT: ObserversTuple<S> + Debug,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
SP: ShMemProvider,
OF: Feedback<S>,
Z: HasObjective<Objective = OF, State = S>,
@ -475,30 +479,20 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let qemu = *self.state.hooks.qemu();
if self.state.first_exec {
self.state.hooks.helpers().first_exec_all(self.state.hooks);
self.state.first_exec = false;
}
self.state.hooks.helpers_mut().pre_exec_all(qemu, input);
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
self.state.hooks.helpers_mut().post_exec_all(
qemu,
input,
&mut *self.inner.observers_mut(),
&mut exit_kind,
);
Ok(exit_kind)
self.inner.run_target(fuzzer, state, mgr, input)
}
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP, EM, Z> UsesObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesObservers
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
@ -507,12 +501,15 @@ where
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP, EM, Z> UsesState for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> UsesState
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,
@ -521,12 +518,15 @@ where
}
#[cfg(feature = "fork")]
impl<'a, H, OT, QT, S, SP, EM, Z> HasObservers for QemuForkExecutor<'a, H, OT, QT, S, SP, EM, Z>
impl<'a, CM, EH, H, OT, ET, S, SP, EM, Z> HasObservers
for QemuForkExecutor<'a, CM, EH, H, OT, ET, S, SP, EM, Z>
where
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input) -> ExitKind,
S: State + HasExecutions,
S: Unpin + State + HasExecutions,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
SP: ShMemProvider,
EM: UsesState<State = S>,
Z: UsesState<State = S>,

View File

@ -23,41 +23,50 @@ use libafl_bolts::tuples::RefIndexable;
use crate::executor::inproc_qemu_crash_handler;
#[cfg(emulation_mode = "systemmode")]
use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT};
use crate::{executor::QemuExecutorState, helpers::QemuHelperTuple, hooks::QemuHooks, Qemu};
use crate::{
command::CommandManager, executor::QemuExecutorState, modules::EmulatorModuleTuple, Emulator,
EmulatorExitHandler,
};
pub struct StatefulQemuExecutor<'a, H, OT, QT, S>
pub struct StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
where
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
S: State + HasExecutions,
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>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
inner: StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
}
impl<'a, H, OT, QT, S> Debug for StatefulQemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> Debug for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
where
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
S: State + HasExecutions,
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,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("QemuExecutor")
f.debug_struct("StatefulQemuExecutor")
.field("inner", &self.inner)
.finish()
}
}
impl<'a, H, OT, QT, S> StatefulQemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
where
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
S: State + HasExecutions,
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>,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
{
pub fn new<EM, OF, Z>(
hooks: &'a mut QemuHooks<QT, S>,
emulator: &'a mut Emulator<CM, EH, ET, S>,
harness_fn: &'a mut H,
observers: OT,
fuzzer: &mut Z,
@ -68,16 +77,16 @@ where
where
EM: EventFirer<State = S> + EventRestarter<State = S>,
OF: Feedback<S>,
S: State + HasExecutions + HasCorpus + HasSolutions,
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, QT, S>>,
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
EM,
OF,
OT,
Z,
>(hooks)?;
>(emulator)?;
let mut inner = StatefulInProcessExecutor::with_timeout(
harness_fn, qemu_state, observers, fuzzer, state, event_mgr, timeout,
@ -86,11 +95,11 @@ where
#[cfg(emulation_mode = "usermode")]
{
inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler::<
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
EM,
OF,
Z,
QT,
ET,
S,
> as *const c_void;
}
@ -98,7 +107,7 @@ where
#[cfg(emulation_mode = "systemmode")]
{
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>>,
StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>>,
EM,
OF,
Z,
@ -108,7 +117,9 @@ where
Ok(Self { inner })
}
pub fn inner(&self) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> {
pub fn inner(
&self,
) -> &StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>> {
&self.inner
}
@ -121,31 +132,22 @@ where
pub fn inner_mut(
&mut self,
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, QT, S>> {
) -> &mut StatefulInProcessExecutor<'a, H, OT, S, QemuExecutorState<'a, CM, EH, ET, S>> {
&mut self.inner
}
pub fn hooks(&self) -> &QemuHooks<QT, S> {
self.inner.exposed_executor_state().hooks()
}
pub fn hooks_mut(&mut self) -> &mut QemuHooks<QT, S> {
self.inner.exposed_executor_state_mut().hooks_mut()
}
pub fn emulator(&self) -> &Qemu {
self.inner.exposed_executor_state().qemu()
}
}
impl<'a, EM, H, OT, OF, QT, S, Z> Executor<EM, Z> for StatefulQemuExecutor<'a, H, OT, QT, S>
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, QT, S>) -> ExitKind,
S: State + HasExecutions + HasCorpus + HasSolutions,
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
S: Unpin + State + HasExecutions + HasCorpus + HasSolutions,
OT: ObserversTuple<S>,
OF: Feedback<S>,
QT: QemuHelperTuple<S> + Debug,
ET: EmulatorModuleTuple<S> + Debug,
Z: HasObjective<Objective = OF, State = S>,
{
fn run_target(
@ -155,16 +157,14 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
let qemu = Qemu::get().unwrap();
self.inner
.exposed_executor_state_mut()
.pre_exec::<Self, EM, OF, Z>(input, qemu);
.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,
qemu,
&mut *self.inner.inner.observers_mut(),
&mut exit_kind,
);
@ -172,32 +172,38 @@ where
}
}
impl<'a, H, OT, QT, S> UsesState for StatefulQemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> UsesState for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
where
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
type State = S;
}
impl<'a, H, OT, QT, S> UsesObservers for StatefulQemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> UsesObservers for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
where
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
H: FnMut(&S::Input, &mut QemuExecutorState<'a, CM, EH, ET, S>) -> ExitKind,
OT: ObserversTuple<S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
type Observers = OT;
}
impl<'a, H, OT, QT, S> HasObservers for StatefulQemuExecutor<'a, H, OT, QT, S>
impl<'a, CM, EH, H, OT, ET, S> HasObservers for StatefulQemuExecutor<'a, CM, EH, H, OT, ET, S>
where
H: FnMut(&S::Input, &mut QemuExecutorState<'a, QT, S>) -> ExitKind,
S: State + HasExecutions,
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>,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
#[inline]
fn observers(&self) -> RefIndexable<&Self::Observers, Self::Observers> {

File diff suppressed because it is too large Load Diff

View File

@ -38,11 +38,7 @@ pub use arch::*;
pub mod elf;
pub mod helpers;
pub use helpers::*;
pub mod hooks;
pub use hooks::*;
pub mod modules;
pub mod executor;
pub use executor::QemuExecutor;

View File

@ -12,37 +12,36 @@ use thread_local::ThreadLocal;
use crate::{
capstone,
helpers::{
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
modules::{
EmulatorModule, EmulatorModuleTuple, EmulatorModules, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks},
qemu::ArchExtras,
qemu::{ArchExtras, Hook},
Qemu,
};
pub trait CallTraceCollector: 'static {
fn on_call<QT, S>(
fn on_call<ET, S>(
&mut self,
hooks: &mut QemuHooks<QT, S>,
emulator_modules: &mut EmulatorModules<ET, S>,
state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>;
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>;
fn on_ret<QT, S>(
fn on_ret<ET, S>(
&mut self,
hooks: &mut QemuHooks<QT, S>,
emulator_modules: &mut EmulatorModules<ET, S>,
state: Option<&mut S>,
pc: GuestAddr,
ret_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>;
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>;
// Frowarded from the `QemuCallTracerHelper`
// Frowarded from the `CallTracerModule`
fn pre_exec<I>(&mut self, _qemu: Qemu, _input: &I)
where
I: Input,
@ -57,31 +56,31 @@ pub trait CallTraceCollector: 'static {
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
S: UsesInput,
S: Unpin + UsesInput,
{
}
}
pub trait CallTraceCollectorTuple: 'static + MatchFirstType {
fn on_call_all<QT, S>(
fn on_call_all<ET, S>(
&mut self,
hooks: &mut QemuHooks<QT, S>,
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>;
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>;
fn on_ret_all<QT, S>(
fn on_ret_all<ET, S>(
&mut self,
hooks: &mut QemuHooks<QT, S>,
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
ret_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>;
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>;
fn pre_exec_all<I>(&mut self, _qemu: Qemu, input: &I)
where
@ -95,31 +94,31 @@ pub trait CallTraceCollectorTuple: 'static + MatchFirstType {
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
S: UsesInput;
S: Unpin + UsesInput;
}
impl CallTraceCollectorTuple for () {
fn on_call_all<QT, S>(
fn on_call_all<ET, S>(
&mut self,
_hooks: &mut QemuHooks<QT, S>,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
_call_len: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
}
fn on_ret_all<QT, S>(
fn on_ret_all<ET, S>(
&mut self,
_hooks: &mut QemuHooks<QT, S>,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
_ret_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
}
@ -137,7 +136,7 @@ impl CallTraceCollectorTuple for () {
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
S: UsesInput,
S: Unpin + UsesInput,
{
}
}
@ -147,18 +146,18 @@ where
Head: CallTraceCollector,
Tail: CallTraceCollectorTuple,
{
fn on_call_all<QT, S>(
fn on_call_all<ET, S>(
&mut self,
hooks: &mut QemuHooks<QT, S>,
emulator_modules: &mut EmulatorModules<ET, S>,
mut state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
self.0.on_call(
hooks,
emulator_modules,
match state.as_mut() {
Some(s) => Some(*s),
None => None,
@ -166,21 +165,21 @@ where
pc,
call_len,
);
self.1.on_call_all(hooks, state, pc, call_len);
self.1.on_call_all(emulator_modules, state, pc, call_len);
}
fn on_ret_all<QT, S>(
fn on_ret_all<ET, S>(
&mut self,
hooks: &mut QemuHooks<QT, S>,
emulator_modules: &mut EmulatorModules<ET, S>,
mut state: Option<&mut S>,
pc: GuestAddr,
ret_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
self.0.on_ret(
hooks,
emulator_modules,
match state.as_mut() {
Some(s) => Some(*s),
None => None,
@ -188,7 +187,7 @@ where
pc,
ret_addr,
);
self.1.on_ret_all(hooks, state, pc, ret_addr);
self.1.on_ret_all(emulator_modules, state, pc, ret_addr);
}
fn pre_exec_all<I>(&mut self, qemu: Qemu, input: &I)
@ -207,7 +206,7 @@ where
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
S: UsesInput,
S: Unpin + UsesInput,
{
self.0.post_exec(qemu, input, observers, exit_kind);
self.1.post_exec_all(qemu, input, observers, exit_kind);
@ -215,7 +214,7 @@ where
}
#[derive(Debug)]
pub struct QemuCallTracerHelper<T>
pub struct CallTracerModule<T>
where
T: CallTraceCollectorTuple,
{
@ -224,9 +223,9 @@ where
collectors: Option<T>,
}
impl<T> QemuCallTracerHelper<T>
impl<T> CallTracerModule<T>
where
T: CallTraceCollectorTuple,
T: CallTraceCollectorTuple + Debug,
{
#[must_use]
pub fn new(filter: QemuInstrumentationAddressRangeFilter, collectors: T) -> Self {
@ -242,16 +241,19 @@ where
self.filter.allowed(addr)
}
fn on_ret<QT, S>(hooks: &mut QemuHooks<QT, S>, state: Option<&mut S>, pc: GuestAddr)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
fn on_ret<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
state: Option<&mut S>,
pc: GuestAddr,
) where
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let ret_addr: GuestAddr = hooks.qemu().read_return_address().unwrap();
let ret_addr: GuestAddr = emulator_modules.qemu().read_return_address().unwrap();
// log::info!("RET @ 0x{:#x}", ret_addr);
let mut collectors = if let Some(h) = hooks.helpers_mut().match_first_type_mut::<Self>() {
let mut collectors = if let Some(h) = emulator_modules.get_mut::<Self>() {
h.collectors.take()
} else {
return;
@ -262,24 +264,23 @@ where
collectors
.as_mut()
.unwrap()
.on_ret_all(hooks, state, pc, ret_addr);
hooks
.helpers_mut()
.match_first_type_mut::<Self>()
.on_ret_all(emulator_modules, state, pc, ret_addr);
emulator_modules
.get_mut::<Self>()
.unwrap()
.collectors = collectors;
}
fn gen_blocks_calls<QT, S>(
hooks: &mut QemuHooks<QT, S>,
fn gen_blocks_calls<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if let Some(h) = hooks.helpers_mut().match_first_type_mut::<Self>() {
if let Some(h) = emulator_modules.get_mut::<Self>() {
if !h.must_instrument(pc) {
return None;
}
@ -293,21 +294,24 @@ where
.unwrap();
}
let emu = hooks.qemu();
let qemu = emulator_modules.qemu();
if let Some(h) = hooks.helpers().match_first_type::<Self>() {
let mut call_addrs: Vec<(GuestAddr, usize)> = Vec::new();
let mut ret_addrs: Vec<GuestAddr> = Vec::new();
if let Some(h) = emulator_modules.modules().match_first_type::<Self>() {
#[allow(unused_mut)]
let mut code = {
#[cfg(emulation_mode = "usermode")]
unsafe {
std::slice::from_raw_parts(emu.g2h(pc), 512)
std::slice::from_raw_parts(qemu.g2h(pc), 512)
}
#[cfg(emulation_mode = "systemmode")]
&mut [0; 512]
};
#[cfg(emulation_mode = "systemmode")]
unsafe {
emu.read_mem(pc, code)
qemu.read_mem(pc, code)
}; // TODO handle faults
let mut iaddr = pc;
@ -322,39 +326,10 @@ where
match u32::from(detail.0) {
capstone::InsnGroupType::CS_GRP_CALL => {
let call_len = insn.bytes().len();
// TODO do not use a closure, find a more efficient way to pass call_len
let call_cb = Box::new(
move |hooks: &mut QemuHooks<QT, S>, state: Option<&mut S>, pc| {
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
let mut collectors = if let Some(h) =
hooks.helpers_mut().match_first_type_mut::<Self>()
{
h.collectors.take()
} else {
return;
};
if collectors.is_none() {
return; // TODO fix this, it can be None on races ret
}
collectors
.as_mut()
.unwrap()
.on_call_all(hooks, state, pc, call_len);
hooks
.helpers_mut()
.match_first_type_mut::<Self>()
.unwrap()
.collectors = collectors;
},
);
hooks.instruction_closure(insn.address() as GuestAddr, call_cb, false);
call_addrs.push((insn.address() as GuestAddr, call_len));
}
capstone::InsnGroupType::CS_GRP_RET => {
hooks.instruction_function(
insn.address() as GuestAddr,
Self::on_ret,
false,
);
ret_addrs.push(insn.address() as GuestAddr);
break 'disasm;
}
capstone::InsnGroupType::CS_GRP_INVALID
@ -371,20 +346,51 @@ where
#[cfg(emulation_mode = "usermode")]
unsafe {
code = std::slice::from_raw_parts(emu.g2h(iaddr), 512);
code = std::slice::from_raw_parts(qemu.g2h(iaddr), 512);
}
#[cfg(emulation_mode = "systemmode")]
unsafe {
emu.read_mem(pc, code);
qemu.read_mem(pc, code);
} // TODO handle faults
}
}
for (call_addr, call_len) in call_addrs {
// TODO do not use a closure, find a more efficient way to pass call_len
let call_cb = Box::new(
move |emulator_modules: &mut EmulatorModules<ET, S>, state: Option<&mut S>, pc| {
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
let mut collectors =
if let Some(h) = emulator_modules.get_mut::<Self>() {
h.collectors.take()
} else {
return;
};
if collectors.is_none() {
return; // TODO fix this, it can be None on races ret
}
collectors
.as_mut()
.unwrap()
.on_call_all(emulator_modules, state, pc, call_len);
emulator_modules
.get_mut::<Self>()
.unwrap()
.collectors = collectors;
},
);
emulator_modules.instruction_closure(call_addr, call_cb, false);
}
for ret_addr in ret_addrs {
emulator_modules.instruction_function(ret_addr, Self::on_ret, false);
}
None
}
}
impl<T> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCallTracerHelper<T>
impl<T> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for CallTracerModule<T>
where
T: CallTraceCollectorTuple,
{
@ -397,39 +403,48 @@ where
}
}
impl<S, T> QemuHelper<S> for QemuCallTracerHelper<T>
impl<S, T> EmulatorModule<S> for CallTracerModule<T>
where
S: UsesInput,
S: Unpin + UsesInput,
T: CallTraceCollectorTuple + Debug,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.blocks(
Hook::Function(Self::gen_blocks_calls::<QT, S>),
emulator_modules.blocks(
Hook::Function(Self::gen_blocks_calls::<ET, S>),
Hook::Empty,
Hook::Empty,
);
}
fn pre_exec(&mut self, qemu: Qemu, input: &S::Input) {
self.collectors.as_mut().unwrap().pre_exec_all(qemu, input);
fn pre_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, input: &S::Input)
where
ET: EmulatorModuleTuple<S>,
{
self.collectors
.as_mut()
.unwrap()
.pre_exec_all(emulator_modules.qemu(), input);
}
fn post_exec<OT>(
fn post_exec<OT, ET>(
&mut self,
qemu: Qemu,
emulator_modules: &mut EmulatorModules<ET, S>,
input: &S::Input,
observers: &mut OT,
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>,
{
self.collectors
.as_mut()
.unwrap()
.post_exec_all(qemu, input, observers, exit_kind);
self.collectors.as_mut().unwrap().post_exec_all(
emulator_modules.qemu(),
input,
observers,
exit_kind,
);
}
}
@ -464,29 +479,29 @@ where
'a: 'static,
{
#[allow(clippy::unnecessary_cast)]
fn on_call<QT, S>(
fn on_call<ET, S>(
&mut self,
_hooks: &mut QemuHooks<QT, S>,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
self.callstack_hash ^= pc as u64 + call_len as u64;
}
#[allow(clippy::unnecessary_cast)]
fn on_ret<QT, S>(
fn on_ret<ET, S>(
&mut self,
_hooks: &mut QemuHooks<QT, S>,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
ret_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
self.callstack_hash ^= ret_addr as u64;
}
@ -506,7 +521,7 @@ where
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
S: UsesInput,
S: Unpin + UsesInput,
{
let observer = observers
.get_mut(&self.observer_handle)
@ -553,15 +568,15 @@ impl FullBacktraceCollector {
impl CallTraceCollector for FullBacktraceCollector {
#[allow(clippy::unnecessary_cast)]
fn on_call<QT, S>(
fn on_call<ET, S>(
&mut self,
_hooks: &mut QemuHooks<QT, S>,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
call_len: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
// TODO handle Thumb
unsafe {
@ -570,15 +585,15 @@ impl CallTraceCollector for FullBacktraceCollector {
}
#[allow(clippy::unnecessary_cast)]
fn on_ret<QT, S>(
fn on_ret<ET, S>(
&mut self,
_hooks: &mut QemuHooks<QT, S>,
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_pc: GuestAddr,
ret_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let v = &mut *CALLSTACKS.as_mut().unwrap().get_or_default().get();

View File

@ -14,11 +14,12 @@ use serde::{Deserialize, Serialize};
#[cfg(emulation_mode = "usermode")]
use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu};
use crate::{
helpers::{
hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
emu::EmulatorModules,
modules::{
hash_me, EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks},
qemu::Hook,
};
#[cfg_attr(
@ -44,11 +45,11 @@ impl QemuCmpsMapMetadata {
libafl_bolts::impl_serdeany!(QemuCmpsMapMetadata);
#[derive(Debug)]
pub struct QemuCmpLogHelper {
pub struct CmpLogModule {
filter: QemuInstrumentationAddressRangeFilter,
}
impl QemuCmpLogHelper {
impl CmpLogModule {
#[must_use]
pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { filter }
@ -60,13 +61,13 @@ impl QemuCmpLogHelper {
}
}
impl Default for QemuCmpLogHelper {
impl Default for CmpLogModule {
fn default() -> Self {
Self::new(QemuInstrumentationAddressRangeFilter::None)
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmpLogHelper {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for CmpLogModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter
}
@ -76,16 +77,16 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmp
}
}
impl<S> QemuHelper<S> for QemuCmpLogHelper
impl<S> EmulatorModule<S> for CmpLogModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput + HasMetadata,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.cmps(
Hook::Function(gen_unique_cmp_ids::<QT, S>),
emulator_modules.cmps(
Hook::Function(gen_unique_cmp_ids::<ET, S>),
Hook::Raw(trace_cmp1_cmplog),
Hook::Raw(trace_cmp2_cmplog),
Hook::Raw(trace_cmp4_cmplog),
@ -95,11 +96,11 @@ where
}
#[derive(Debug)]
pub struct QemuCmpLogChildHelper {
pub struct CmpLogChildModule {
filter: QemuInstrumentationAddressRangeFilter,
}
impl QemuCmpLogChildHelper {
impl CmpLogChildModule {
#[must_use]
pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { filter }
@ -111,25 +112,24 @@ impl QemuCmpLogChildHelper {
}
}
impl Default for QemuCmpLogChildHelper {
impl Default for CmpLogChildModule {
fn default() -> Self {
Self::new(QemuInstrumentationAddressRangeFilter::None)
}
}
impl<S> QemuHelper<S> for QemuCmpLogChildHelper
impl<S> EmulatorModule<S> for CmpLogChildModule
where
S: UsesInput,
S: HasMetadata,
S: Unpin + UsesInput + HasMetadata,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.cmps(
Hook::Function(gen_hashed_cmp_ids::<QT, S>),
emulator_modules.cmps(
Hook::Function(gen_hashed_cmp_ids::<ET, S>),
Hook::Raw(trace_cmp1_cmplog),
Hook::Raw(trace_cmp2_cmplog),
Hook::Raw(trace_cmp4_cmplog),
@ -138,18 +138,17 @@ where
}
}
pub fn gen_unique_cmp_ids<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_unique_cmp_ids<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
) -> Option<u64>
where
S: HasMetadata,
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput + HasMetadata,
{
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogHelper>() {
if let Some(h) = emulator_modules.get::<CmpLogModule>() {
if !h.must_instrument(pc) {
return None;
}
@ -170,18 +169,17 @@ where
}))
}
pub fn gen_hashed_cmp_ids<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_hashed_cmp_ids<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_size: usize,
) -> Option<u64>
where
S: HasMetadata,
S: UsesInput,
QT: QemuHelperTuple<S>,
S: HasMetadata + Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if let Some(h) = hooks.match_helper_mut::<QemuCmpLogChildHelper>() {
if let Some(h) = emulator_modules.get::<CmpLogChildModule>() {
if !h.must_instrument(pc) {
return None;
}
@ -215,13 +213,13 @@ pub extern "C" fn trace_cmp8_cmplog(_: *const (), id: u64, v0: u64, v1: u64) {
#[cfg(emulation_mode = "usermode")]
#[derive(Debug)]
pub struct QemuCmpLogRoutinesHelper {
pub struct CmpLogRoutinesModule {
filter: QemuInstrumentationAddressRangeFilter,
cs: Capstone,
}
#[cfg(emulation_mode = "usermode")]
impl QemuCmpLogRoutinesHelper {
impl CmpLogRoutinesModule {
#[must_use]
pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self {
@ -262,16 +260,16 @@ impl QemuCmpLogRoutinesHelper {
}
}
fn gen_blocks_calls<QT, S>(
hooks: &mut QemuHooks<QT, S>,
fn gen_blocks_calls<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if let Some(h) = hooks.helpers_mut().match_first_type_mut::<Self>() {
if let Some(h) = emulator_modules.get_mut::<Self>() {
if !h.must_instrument(pc) {
return None;
}
@ -285,9 +283,9 @@ impl QemuCmpLogRoutinesHelper {
.unwrap();
}
let qemu = hooks.qemu();
let qemu = emulator_modules.qemu();
if let Some(h) = hooks.helpers().match_first_type::<Self>() {
if let Some(h) = emulator_modules.get::<Self>() {
#[allow(unused_mut)]
let mut code = {
#[cfg(emulation_mode = "usermode")]
@ -314,7 +312,12 @@ impl QemuCmpLogRoutinesHelper {
match u32::from(detail.0) {
capstone::InsnGroupType::CS_GRP_CALL => {
let k = (hash_me(pc.into())) & (CMPLOG_MAP_W as u64 - 1);
qemu.set_hook(k, insn.address() as GuestAddr, Self::on_call, false);
qemu.hooks().add_instruction_hooks(
k,
insn.address() as GuestAddr,
Self::on_call,
false,
);
}
capstone::InsnGroupType::CS_GRP_RET
| capstone::InsnGroupType::CS_GRP_INVALID
@ -345,7 +348,7 @@ impl QemuCmpLogRoutinesHelper {
}
#[cfg(emulation_mode = "usermode")]
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmpLogRoutinesHelper {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for CmpLogRoutinesModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter
}
@ -356,16 +359,16 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmp
}
#[cfg(emulation_mode = "usermode")]
impl<S> QemuHelper<S> for QemuCmpLogRoutinesHelper
impl<S> EmulatorModule<S> for CmpLogRoutinesModule
where
S: UsesInput,
S: Unpin + UsesInput,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.blocks(
Hook::Function(Self::gen_blocks_calls::<QT, S>),
emulator_modules.blocks(
Hook::Function(Self::gen_blocks_calls::<ET, S>),
Hook::Empty,
Hook::Empty,
);

View File

@ -12,14 +12,14 @@ pub use libafl_targets::{
use serde::{Deserialize, Serialize};
#[cfg(emulation_mode = "systemmode")]
use crate::helpers::QemuInstrumentationPagingFilter;
use crate::modules::QemuInstrumentationPagingFilter;
use crate::{
helpers::{
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple,
emu::EmulatorModules,
modules::{
hash_me, EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks},
IsFilter,
qemu::Hook,
};
#[cfg_attr(
@ -46,21 +46,21 @@ libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata);
#[cfg(emulation_mode = "usermode")]
#[derive(Debug)]
pub struct QemuEdgeCoverageHelper {
pub struct EdgeCoverageModule {
address_filter: QemuInstrumentationAddressRangeFilter,
use_hitcounts: bool,
}
#[cfg(emulation_mode = "systemmode")]
#[derive(Debug)]
pub struct QemuEdgeCoverageHelper {
pub struct EdgeCoverageModule {
address_filter: QemuInstrumentationAddressRangeFilter,
paging_filter: QemuInstrumentationPagingFilter,
use_hitcounts: bool,
}
#[cfg(emulation_mode = "usermode")]
impl QemuEdgeCoverageHelper {
impl EdgeCoverageModule {
#[must_use]
pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self {
@ -84,7 +84,7 @@ impl QemuEdgeCoverageHelper {
}
#[cfg(emulation_mode = "systemmode")]
impl QemuEdgeCoverageHelper {
impl EdgeCoverageModule {
#[must_use]
pub fn new(
address_filter: QemuInstrumentationAddressRangeFilter,
@ -116,14 +116,14 @@ impl QemuEdgeCoverageHelper {
}
#[cfg(emulation_mode = "usermode")]
impl Default for QemuEdgeCoverageHelper {
impl Default for EdgeCoverageModule {
fn default() -> Self {
Self::new(QemuInstrumentationAddressRangeFilter::None)
}
}
#[cfg(emulation_mode = "systemmode")]
impl Default for QemuEdgeCoverageHelper {
impl Default for EdgeCoverageModule {
fn default() -> Self {
Self::new(
QemuInstrumentationAddressRangeFilter::None,
@ -132,7 +132,7 @@ impl Default for QemuEdgeCoverageHelper {
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuEdgeCoverageHelper {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for EdgeCoverageModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.address_filter
}
@ -143,7 +143,7 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuEdg
}
#[cfg(emulation_mode = "systemmode")]
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCoverageHelper {
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for EdgeCoverageModule {
fn filter(&self) -> &QemuInstrumentationPagingFilter {
&self.paging_filter
}
@ -153,20 +153,21 @@ impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCover
}
}
impl<S> QemuHelper<S> for QemuEdgeCoverageHelper
impl<S> EmulatorModule<S> for EdgeCoverageModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput + HasMetadata,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
if self.use_hitcounts {
// hooks.edges(
// Hook::Function(gen_unique_edge_ids::<QT, S>),
// Hook::Function(gen_unique_edge_ids::<ET, S>),
// Hook::Raw(trace_edge_hitcount),
// );
let hook_id = hooks.edges(Hook::Function(gen_unique_edge_ids::<QT, S>), Hook::Empty);
let hook_id =
emulator_modules.edges(Hook::Function(gen_unique_edge_ids::<ET, S>), Hook::Empty);
unsafe {
libafl_qemu_sys::libafl_qemu_edge_hook_set_jit(
hook_id.0,
@ -175,10 +176,11 @@ where
}
} else {
// hooks.edges(
// Hook::Function(gen_unique_edge_ids::<QT, S>),
// Hook::Function(gen_unique_edge_ids::<ET, S>),
// Hook::Raw(trace_edge_single),
// );
let hook_id = hooks.edges(Hook::Function(gen_unique_edge_ids::<QT, S>), Hook::Empty);
let hook_id =
emulator_modules.edges(Hook::Function(gen_unique_edge_ids::<ET, S>), Hook::Empty);
unsafe {
libafl_qemu_sys::libafl_qemu_edge_hook_set_jit(
hook_id.0,
@ -189,25 +191,25 @@ where
}
}
pub type QemuCollidingEdgeCoverageHelper = QemuEdgeCoverageChildHelper;
pub type CollidingEdgeCoverageModule = EdgeCoverageChildModule;
#[cfg(emulation_mode = "usermode")]
#[derive(Debug)]
pub struct QemuEdgeCoverageChildHelper {
pub struct EdgeCoverageChildModule {
address_filter: QemuInstrumentationAddressRangeFilter,
use_hitcounts: bool,
}
#[cfg(emulation_mode = "systemmode")]
#[derive(Debug)]
pub struct QemuEdgeCoverageChildHelper {
pub struct EdgeCoverageChildModule {
address_filter: QemuInstrumentationAddressRangeFilter,
paging_filter: QemuInstrumentationPagingFilter,
use_hitcounts: bool,
}
#[cfg(emulation_mode = "usermode")]
impl QemuEdgeCoverageChildHelper {
impl EdgeCoverageChildModule {
#[must_use]
pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self {
@ -231,7 +233,7 @@ impl QemuEdgeCoverageChildHelper {
}
#[cfg(emulation_mode = "systemmode")]
impl QemuEdgeCoverageChildHelper {
impl EdgeCoverageChildModule {
#[must_use]
pub fn new(
address_filter: QemuInstrumentationAddressRangeFilter,
@ -263,14 +265,14 @@ impl QemuEdgeCoverageChildHelper {
}
#[cfg(emulation_mode = "usermode")]
impl Default for QemuEdgeCoverageChildHelper {
impl Default for EdgeCoverageChildModule {
fn default() -> Self {
Self::new(QemuInstrumentationAddressRangeFilter::None)
}
}
#[cfg(emulation_mode = "systemmode")]
impl Default for QemuEdgeCoverageChildHelper {
impl Default for EdgeCoverageChildModule {
fn default() -> Self {
Self::new(
QemuInstrumentationAddressRangeFilter::None,
@ -279,9 +281,7 @@ impl Default for QemuEdgeCoverageChildHelper {
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
for QemuEdgeCoverageChildHelper
{
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for EdgeCoverageChildModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.address_filter
}
@ -292,7 +292,7 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
}
#[cfg(emulation_mode = "systemmode")]
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCoverageChildHelper {
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for EdgeCoverageChildModule {
fn filter(&self) -> &QemuInstrumentationPagingFilter {
&self.paging_filter
}
@ -302,24 +302,24 @@ impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCover
}
}
impl<S> QemuHelper<S> for QemuEdgeCoverageChildHelper
impl<S> EmulatorModule<S> for EdgeCoverageChildModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
if self.use_hitcounts {
hooks.edges(
Hook::Function(gen_hashed_edge_ids::<QT, S>),
emulator_modules.edges(
Hook::Function(gen_hashed_edge_ids::<ET, S>),
Hook::Raw(trace_edge_hitcount_ptr),
);
} else {
hooks.edges(
Hook::Function(gen_hashed_edge_ids::<QT, S>),
emulator_modules.edges(
Hook::Function(gen_hashed_edge_ids::<ET, S>),
Hook::Raw(trace_edge_single_ptr),
);
}
@ -328,7 +328,7 @@ where
#[cfg(emulation_mode = "usermode")]
#[derive(Debug)]
pub struct QemuEdgeCoverageClassicHelper {
pub struct EdgeCoverageClassicModule {
address_filter: QemuInstrumentationAddressRangeFilter,
use_hitcounts: bool,
use_jit: bool,
@ -336,7 +336,7 @@ pub struct QemuEdgeCoverageClassicHelper {
#[cfg(emulation_mode = "systemmode")]
#[derive(Debug)]
pub struct QemuEdgeCoverageClassicHelper {
pub struct EdgeCoverageClassicModule {
address_filter: QemuInstrumentationAddressRangeFilter,
paging_filter: QemuInstrumentationPagingFilter,
use_hitcounts: bool,
@ -344,7 +344,7 @@ pub struct QemuEdgeCoverageClassicHelper {
}
#[cfg(emulation_mode = "usermode")]
impl QemuEdgeCoverageClassicHelper {
impl EdgeCoverageClassicModule {
#[must_use]
pub fn new(address_filter: QemuInstrumentationAddressRangeFilter, use_jit: bool) -> Self {
Self {
@ -373,7 +373,7 @@ impl QemuEdgeCoverageClassicHelper {
}
#[cfg(emulation_mode = "systemmode")]
impl QemuEdgeCoverageClassicHelper {
impl EdgeCoverageClassicModule {
#[must_use]
pub fn new(
address_filter: QemuInstrumentationAddressRangeFilter,
@ -409,14 +409,14 @@ impl QemuEdgeCoverageClassicHelper {
}
#[cfg(emulation_mode = "usermode")]
impl Default for QemuEdgeCoverageClassicHelper {
impl Default for EdgeCoverageClassicModule {
fn default() -> Self {
Self::new(QemuInstrumentationAddressRangeFilter::None, false)
}
}
#[cfg(emulation_mode = "systemmode")]
impl Default for QemuEdgeCoverageClassicHelper {
impl Default for EdgeCoverageClassicModule {
fn default() -> Self {
Self::new(
QemuInstrumentationAddressRangeFilter::None,
@ -426,9 +426,7 @@ impl Default for QemuEdgeCoverageClassicHelper {
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
for QemuEdgeCoverageClassicHelper
{
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for EdgeCoverageClassicModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.address_filter
}
@ -439,7 +437,7 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
}
#[cfg(emulation_mode = "systemmode")]
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCoverageClassicHelper {
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for EdgeCoverageClassicModule {
fn filter(&self) -> &QemuInstrumentationPagingFilter {
&self.paging_filter
}
@ -450,20 +448,20 @@ impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCover
}
#[allow(clippy::collapsible_else_if)]
impl<S> QemuHelper<S> for QemuEdgeCoverageClassicHelper
impl<S> EmulatorModule<S> for EdgeCoverageClassicModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
if self.use_hitcounts {
if self.use_jit {
let hook_id = hooks.blocks(
Hook::Function(gen_hashed_block_ids::<QT, S>),
let hook_id = emulator_modules.blocks(
Hook::Function(gen_hashed_block_ids::<ET, S>),
Hook::Empty,
Hook::Empty,
);
@ -475,16 +473,16 @@ where
);
}
} else {
hooks.blocks(
Hook::Function(gen_hashed_block_ids::<QT, S>),
emulator_modules.blocks(
Hook::Function(gen_hashed_block_ids::<ET, S>),
Hook::Empty,
Hook::Raw(trace_block_transition_hitcount),
);
}
} else {
if self.use_jit {
let hook_id = hooks.blocks(
Hook::Function(gen_hashed_block_ids::<QT, S>),
let hook_id = emulator_modules.blocks(
Hook::Function(gen_hashed_block_ids::<ET, S>),
Hook::Empty,
Hook::Empty,
);
@ -496,8 +494,8 @@ where
);
}
} else {
hooks.blocks(
Hook::Function(gen_hashed_block_ids::<QT, S>),
emulator_modules.blocks(
Hook::Function(gen_hashed_block_ids::<ET, S>),
Hook::Empty,
Hook::Raw(trace_block_transition_single),
);
@ -508,17 +506,17 @@ where
thread_local!(static PREV_LOC : UnsafeCell<u64> = const { UnsafeCell::new(0) });
pub fn gen_unique_edge_ids<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_unique_edge_ids<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
S: UsesInput + HasMetadata,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput + HasMetadata,
{
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
if let Some(h) = emulator_modules.get::<EdgeCoverageModule>() {
#[cfg(emulation_mode = "usermode")]
{
if !h.must_instrument(src) && !h.must_instrument(dest) {
@ -528,7 +526,7 @@ where
#[cfg(emulation_mode = "systemmode")]
{
let paging_id = hooks
let paging_id = emulator_modules
.qemu()
.current_cpu()
.and_then(|cpu| cpu.current_paging_id());
@ -576,20 +574,17 @@ pub extern "C" fn trace_edge_single(_: *const (), id: u64) {
}
}
pub fn gen_hashed_edge_ids<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_hashed_edge_ids<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
src: GuestAddr,
dest: GuestAddr,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
if let Some(h) = hooks
.helpers()
.match_first_type::<QemuEdgeCoverageChildHelper>()
{
if let Some(h) = emulator_modules.get::<EdgeCoverageChildModule>() {
#[cfg(emulation_mode = "usermode")]
if !h.must_instrument(src) && !h.must_instrument(dest) {
return None;
@ -597,7 +592,7 @@ where
#[cfg(emulation_mode = "systemmode")]
{
let paging_id = hooks
let paging_id = emulator_modules
.qemu()
.current_cpu()
.and_then(|cpu| cpu.current_paging_id());
@ -626,35 +621,16 @@ pub extern "C" fn trace_edge_single_ptr(_: *const (), id: u64) {
}
}
/*
pub fn gen_addr_block_ids<QT, S>(
_hooks: &mut QemuHooks<QT, S>,
pub fn gen_hashed_block_ids<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
// GuestAddress is u32 for 32 bit guests
#[allow(clippy::unnecessary_cast)]
Some(pc as u64)
}
*/
pub fn gen_hashed_block_ids<QT, S>(
hooks: &mut QemuHooks<QT, S>,
_state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
{
if let Some(h) = hooks
.helpers()
.match_first_type::<QemuEdgeCoverageClassicHelper>()
{
if let Some(h) = emulator_modules.get::<EdgeCoverageClassicModule>() {
#[cfg(emulation_mode = "usermode")]
{
if !h.must_instrument(pc) {
@ -663,7 +639,7 @@ where
}
#[cfg(emulation_mode = "systemmode")]
{
let paging_id = hooks
let paging_id = emulator_modules
.qemu()
.current_cpu()
.and_then(|cpu| cpu.current_paging_id());

View File

@ -6,138 +6,193 @@ use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr};
use crate::{hooks::QemuHooks, Qemu};
use crate::Qemu;
#[cfg(emulation_mode = "usermode")]
pub mod usermode;
#[cfg(emulation_mode = "usermode")]
pub use usermode::*;
#[cfg(emulation_mode = "systemmode")]
pub mod systemmode;
#[cfg(emulation_mode = "systemmode")]
pub use systemmode::*;
pub mod edges;
pub use edges::QemuEdgeCoverageHelper;
pub use edges::EdgeCoverageModule;
#[cfg(not(cpu_target = "hexagon"))]
pub mod calls;
#[cfg(not(cpu_target = "hexagon"))]
pub use calls::QemuCallTracerHelper;
#[cfg(not(cpu_target = "hexagon"))]
pub mod drcov;
#[cfg(not(cpu_target = "hexagon"))]
pub use drcov::QemuDrCovHelper;
pub use calls::CallTracerModule;
#[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))]
pub mod cmplog;
#[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))]
pub use cmplog::QemuCmpLogHelper;
pub use cmplog::CmpLogModule;
#[cfg(all(emulation_mode = "usermode", feature = "injections"))]
pub mod injections;
#[cfg(all(emulation_mode = "usermode", feature = "injections"))]
pub use injections::QemuInjectionHelper;
use crate::emu::EmulatorModules;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub mod snapshot;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub use snapshot::IntervalSnapshotFilter;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub use snapshot::QemuSnapshotHelper;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub mod asan;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub use asan::{init_qemu_with_asan, QemuAsanHelper};
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub mod asan_guest;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
pub use asan_guest::{init_qemu_with_asan_guest, QemuAsanGuestHelper};
/// A helper for `libafl_qemu`.
/// A module for `libafl_qemu`.
// TODO remove 'static when specialization will be stable
pub trait QemuHelper<S>: 'static + Debug
pub trait EmulatorModule<S>: 'static + Debug
where
S: UsesInput,
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = true;
fn init_hooks<QT>(&self, _hooks: &QemuHooks<QT, S>)
/// Initialize the module, mostly used to install some hooks early.
fn init_module<ET>(&self, _emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
}
fn first_exec<QT>(&self, _hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
}
fn pre_exec(&mut self, _qemu: Qemu, _input: &S::Input) {}
fn pre_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _input: &S::Input)
where
ET: EmulatorModuleTuple<S>,
{
}
fn post_exec<OT>(
fn post_exec<OT, ET>(
&mut self,
_qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>,
{
}
}
pub trait QemuHelperTuple<S>: MatchFirstType + for<'a> SplitBorrowExtractFirstType<'a>
pub trait EmulatorModuleTuple<S>:
MatchFirstType + for<'a> SplitBorrowExtractFirstType<'a> + Unpin
where
S: UsesInput,
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool;
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<QT, S>)
fn init_modules_all<ET>(&self, _emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>;
ET: EmulatorModuleTuple<S>;
fn first_exec_all<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec_all<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>;
ET: EmulatorModuleTuple<S>;
fn pre_exec_all(&mut self, _qemu: Qemu, input: &S::Input);
fn post_exec_all<OT>(
fn pre_exec_all<ET>(
&mut self,
_qemu: Qemu,
input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
_emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
) where
OT: ObserversTuple<S>;
}
ET: EmulatorModuleTuple<S>;
impl<S> QemuHelperTuple<S> for ()
where
S: UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks_all<QT>(&self, _hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
}
fn first_exec_all<QT>(&self, _hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
}
fn pre_exec_all(&mut self, _qemu: Qemu, _input: &S::Input) {}
fn post_exec_all<OT>(
fn post_exec_all<OT, ET>(
&mut self,
_qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>;
}
impl<S> EmulatorModuleTuple<S> for ()
where
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_modules_all<ET>(&self, _emulator_modules: &mut EmulatorModules<ET, S>)
where
ET: EmulatorModuleTuple<S>,
{
}
fn first_exec_all<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
where
ET: EmulatorModuleTuple<S>,
{
}
fn pre_exec_all<ET>(
&mut self,
_emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
) where
ET: EmulatorModuleTuple<S>,
{
}
fn post_exec_all<OT, ET>(
&mut self,
_emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>,
{
}
}
impl<Head, Tail, S> EmulatorModuleTuple<S> for (Head, Tail)
where
Head: EmulatorModule<S> + Unpin,
Tail: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
fn init_modules_all<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where
ET: EmulatorModuleTuple<S>,
{
self.0.init_module(emulator_modules);
self.1.init_modules_all(emulator_modules);
}
fn first_exec_all<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
ET: EmulatorModuleTuple<S>,
{
self.0.first_exec(emulator_modules);
self.1.first_exec_all(emulator_modules);
}
fn pre_exec_all<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, input: &S::Input)
where
ET: EmulatorModuleTuple<S>,
{
self.0.pre_exec(emulator_modules, input);
self.1.pre_exec_all(emulator_modules, input);
}
fn post_exec_all<OT, ET>(
&mut self,
emulator_modules: &mut EmulatorModules<ET, S>,
input: &S::Input,
observers: &mut OT,
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>,
{
self.0
.post_exec(emulator_modules, input, observers, exit_kind);
self.1
.post_exec_all(emulator_modules, input, observers, exit_kind);
}
}
impl HasInstrumentationFilter<()> for () {
@ -164,49 +219,6 @@ where
}
}
impl<Head, Tail, S> QemuHelperTuple<S> for (Head, Tail)
where
Head: QemuHelper<S>,
Tail: QemuHelperTuple<S>,
S: UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
fn init_hooks_all<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
self.0.init_hooks(hooks);
self.1.init_hooks_all(hooks);
}
fn first_exec_all<QT>(&self, hooks: &QemuHooks<QT, S>)
where
QT: QemuHelperTuple<S>,
{
self.0.first_exec(hooks);
self.1.first_exec_all(hooks);
}
fn pre_exec_all(&mut self, qemu: Qemu, input: &S::Input) {
self.0.pre_exec(qemu, input);
self.1.pre_exec_all(qemu, input);
}
fn post_exec_all<OT>(
&mut self,
qemu: Qemu,
input: &S::Input,
observers: &mut OT,
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
{
self.0.post_exec(qemu, input, observers, exit_kind);
self.1.post_exec_all(qemu, input, observers, exit_kind);
}
}
#[derive(Debug, Clone)]
pub enum QemuFilterList<T: IsFilter + Debug + Clone> {
AllowList(T),
@ -271,19 +283,6 @@ where
}
}
#[cfg(emulation_mode = "usermode")]
pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
{
}
#[cfg(emulation_mode = "systemmode")]
pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
{
}
static mut EMPTY_ADDRESS_FILTER: UnsafeCell<QemuInstrumentationAddressRangeFilter> =
UnsafeCell::new(QemuFilterList::None);
static mut EMPTY_PAGING_FILTER: UnsafeCell<QemuInstrumentationPagingFilter> =
@ -309,25 +308,6 @@ impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for () {
}
}
#[cfg(emulation_mode = "systemmode")]
impl<Head> StdInstrumentationFilter for (Head, ()) where
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
{
}
#[cfg(emulation_mode = "usermode")]
impl<Head> StdInstrumentationFilter for (Head, ()) where
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
{
}
#[cfg(emulation_mode = "systemmode")]
impl StdInstrumentationFilter for () {}
#[cfg(emulation_mode = "usermode")]
impl StdInstrumentationFilter for () {}
pub trait IsFilter: Debug {
type FilterParameter;
@ -344,12 +324,6 @@ impl IsFilter for () {
pub trait IsAddressFilter: IsFilter<FilterParameter = GuestAddr> {}
#[cfg(emulation_mode = "systemmode")]
pub trait IsPagingFilter: IsFilter<FilterParameter = Option<GuestPhysAddr>> {}
#[cfg(emulation_mode = "systemmode")]
impl IsPagingFilter for QemuInstrumentationPagingFilter {}
impl IsAddressFilter for QemuInstrumentationAddressRangeFilter {}
#[must_use]

View File

@ -0,0 +1,24 @@
use libafl_qemu_sys::GuestPhysAddr;
use crate::modules::{
HasInstrumentationFilter, IsFilter, QemuInstrumentationAddressRangeFilter,
QemuInstrumentationPagingFilter,
};
pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
{
}
impl<Head> crate::modules::StdInstrumentationFilter for (Head, ()) where
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter>
{
}
impl StdInstrumentationFilter for () {}
pub trait IsPagingFilter: IsFilter<FilterParameter = Option<GuestPhysAddr>> {}
impl IsPagingFilter for QemuInstrumentationPagingFilter {}

View File

@ -3,7 +3,7 @@
use std::{borrow::Cow, env, fs, path::PathBuf, sync::Mutex};
use hashbrown::{HashMap, HashSet};
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, HasMetadata};
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
use libc::{
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
};
@ -12,13 +12,12 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
use rangemap::RangeMap;
use crate::{
helpers::{
calls::FullBacktraceCollector, HasInstrumentationFilter, IsFilter, QemuHelper,
QemuHelperTuple, QemuInstrumentationAddressRangeFilter,
modules::{
calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule,
EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks},
qemu::{MemAccessInfo, QemuInitError, SyscallHookResult},
snapshot::QemuSnapshotHelper,
qemu::{MemAccessInfo, QemuInitError},
sys::TCGTemp,
GuestAddr, Qemu, Regs,
};
@ -152,6 +151,11 @@ use std::pin::Pin;
use object::{Object, ObjectSection};
use crate::{
emu::EmulatorModules,
qemu::{Hook, QemuHooks, SyscallHookResult},
};
pub struct AsanGiovese {
pub alloc_tree: Mutex<IntervalTree<GuestAddr, AllocTreeItem>>,
pub saved_tree: IntervalTree<GuestAddr, AllocTreeItem>,
@ -205,7 +209,7 @@ impl AsanGiovese {
}
#[must_use]
fn new(emu: Qemu) -> Pin<Box<Self>> {
fn new(qemu_hooks: QemuHooks) -> Pin<Box<Self>> {
let res = Self {
alloc_tree: Mutex::new(IntervalTree::new()),
saved_tree: IntervalTree::new(),
@ -215,7 +219,7 @@ impl AsanGiovese {
snapshot_shadow: true, // By default, track the dirty shadow pages
};
let mut boxed = Box::pin(res);
emu.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall);
qemu_hooks.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall);
boxed
}
@ -713,7 +717,7 @@ pub fn init_qemu_with_asan(
}
let qemu = Qemu::init(args, env)?;
let rt = AsanGiovese::new(qemu);
let rt = AsanGiovese::new(qemu.hooks());
Ok((qemu, rt))
}
@ -725,10 +729,10 @@ pub enum QemuAsanOptions {
SnapshotDetectLeaks,
}
pub type QemuAsanChildHelper = QemuAsanHelper;
pub type AsanChildModule = AsanModule;
#[derive(Debug)]
pub struct QemuAsanHelper {
pub struct AsanModule {
enabled: bool,
detect_leaks: bool,
empty: bool,
@ -736,7 +740,7 @@ pub struct QemuAsanHelper {
filter: QemuInstrumentationAddressRangeFilter,
}
impl QemuAsanHelper {
impl AsanModule {
#[must_use]
pub fn default(rt: Pin<Box<AsanGiovese>>) -> Self {
Self::new(
@ -907,7 +911,7 @@ impl QemuAsanHelper {
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuAsanHelper {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for AsanModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter
}
@ -917,103 +921,107 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuAsa
}
}
impl<S> QemuHelper<S> for QemuAsanHelper
impl<S> EmulatorModule<S> for AsanModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput,
{
const HOOKS_DO_SIDE_EFFECTS: bool = false;
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.syscalls(Hook::Function(qasan_fake_syscall::<QT, S>));
emulator_modules.syscalls(Hook::Function(qasan_fake_syscall::<ET, S>));
if self.rt.error_callback.is_some() {
hooks.crash_function(oncrash_asan::<QT, S>);
emulator_modules.crash_function(oncrash_asan::<ET, S>);
}
}
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.reads(
Hook::Function(gen_readwrite_asan::<QT, S>),
Hook::Function(trace_read1_asan::<QT, S>),
Hook::Function(trace_read2_asan::<QT, S>),
Hook::Function(trace_read4_asan::<QT, S>),
Hook::Function(trace_read8_asan::<QT, S>),
Hook::Function(trace_read_n_asan::<QT, S>),
emulator_modules.reads(
Hook::Function(gen_readwrite_asan::<ET, S>),
Hook::Function(trace_read1_asan::<ET, S>),
Hook::Function(trace_read2_asan::<ET, S>),
Hook::Function(trace_read4_asan::<ET, S>),
Hook::Function(trace_read8_asan::<ET, S>),
Hook::Function(trace_read_n_asan::<ET, S>),
);
if hooks.match_helper::<QemuSnapshotHelper>().is_none() {
hooks.writes(
Hook::Function(gen_readwrite_asan::<QT, S>),
Hook::Function(trace_write1_asan::<QT, S>),
Hook::Function(trace_write2_asan::<QT, S>),
Hook::Function(trace_write4_asan::<QT, S>),
Hook::Function(trace_write8_asan::<QT, S>),
Hook::Function(trace_write_n_asan::<QT, S>),
if emulator_modules.get::<SnapshotModule>().is_none() {
emulator_modules.writes(
Hook::Function(gen_readwrite_asan::<ET, S>),
Hook::Function(trace_write1_asan::<ET, S>),
Hook::Function(trace_write2_asan::<ET, S>),
Hook::Function(trace_write4_asan::<ET, S>),
Hook::Function(trace_write8_asan::<ET, S>),
Hook::Function(trace_write_n_asan::<ET, S>),
);
} else {
// track writes for both helpers as opt
hooks.writes(
Hook::Function(gen_write_asan_snapshot::<QT, S>),
Hook::Function(trace_write1_asan_snapshot::<QT, S>),
Hook::Function(trace_write2_asan_snapshot::<QT, S>),
Hook::Function(trace_write4_asan_snapshot::<QT, S>),
Hook::Function(trace_write8_asan_snapshot::<QT, S>),
Hook::Function(trace_write_n_asan_snapshot::<QT, S>),
// track writes for both modules as opt
emulator_modules.writes(
Hook::Function(gen_write_asan_snapshot::<ET, S>),
Hook::Function(trace_write1_asan_snapshot::<ET, S>),
Hook::Function(trace_write2_asan_snapshot::<ET, S>),
Hook::Function(trace_write4_asan_snapshot::<ET, S>),
Hook::Function(trace_write8_asan_snapshot::<ET, S>),
Hook::Function(trace_write_n_asan_snapshot::<ET, S>),
);
}
}
fn pre_exec(&mut self, qemu: Qemu, _input: &S::Input) {
fn pre_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _input: &S::Input)
where
ET: EmulatorModuleTuple<S>,
{
if self.empty {
self.rt.snapshot(qemu);
self.rt.snapshot(emulator_modules.qemu());
self.empty = false;
}
}
fn post_exec<OT>(
fn post_exec<OT, ET>(
&mut self,
qemu: Qemu,
emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
_observers: &mut OT,
exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>,
{
if self.reset(qemu) == AsanRollback::HasLeaks {
if self.reset(emulator_modules.qemu()) == AsanRollback::HasLeaks {
*exit_kind = ExitKind::Crash;
}
}
}
pub fn oncrash_asan<QT, S>(hooks: &mut QemuHooks<QT, S>, target_sig: i32)
pub fn oncrash_asan<ET, S>(emulator_modules: &mut EmulatorModules<ET, S>, target_sig: i32)
where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();
h.rt.report(qemu, pc, AsanError::Signal(target_sig));
}
pub fn gen_readwrite_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_readwrite_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_addr: *mut TCGTemp,
_info: MemAccessInfo,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
if h.must_instrument(pc) {
Some(pc.into())
} else {
@ -1021,160 +1029,160 @@ where
}
}
pub fn trace_read1_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_read1_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_1(qemu, id as GuestAddr, addr);
}
pub fn trace_read2_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_read2_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_2(qemu, id as GuestAddr, addr);
}
pub fn trace_read4_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_read4_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_4(qemu, id as GuestAddr, addr);
}
pub fn trace_read8_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_read8_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_8(qemu, id as GuestAddr, addr);
}
pub fn trace_read_n_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_read_n_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
size: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_n(qemu, id as GuestAddr, addr, size);
}
pub fn trace_write1_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write1_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_1(qemu, id as GuestAddr, addr);
}
pub fn trace_write2_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write2_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_2(qemu, id as GuestAddr, addr);
}
pub fn trace_write4_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write4_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_4(qemu, id as GuestAddr, addr);
}
pub fn trace_write8_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write8_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_8(qemu, id as GuestAddr, addr);
}
pub fn trace_write_n_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write_n_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
size: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_n(qemu, id as GuestAddr, addr, size);
}
pub fn gen_write_asan_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_write_asan_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_addr: *mut TCGTemp,
_info: MemAccessInfo,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
if h.must_instrument(pc) {
Some(pc.into())
} else {
@ -1182,100 +1190,110 @@ where
}
}
pub fn trace_write1_asan_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write1_asan_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if id != 0 {
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_1(qemu, id as GuestAddr, addr);
}
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, 1);
}
pub fn trace_write2_asan_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write2_asan_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if id != 0 {
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_2(qemu, id as GuestAddr, addr);
}
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, 2);
}
pub fn trace_write4_asan_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write4_asan_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if id != 0 {
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_4(qemu, id as GuestAddr, addr);
}
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, 4);
}
pub fn trace_write8_asan_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write8_asan_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if id != 0 {
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.write_8(qemu, id as GuestAddr, addr);
}
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, 8);
}
pub fn trace_write_n_asan_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write_n_asan_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
addr: GuestAddr,
size: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if id != 0 {
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
h.read_n(qemu, id as GuestAddr, addr, size);
}
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, size);
}
#[allow(clippy::too_many_arguments)]
pub fn qasan_fake_syscall<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn qasan_fake_syscall<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
sys_num: i32,
a0: GuestAddr,
@ -1288,12 +1306,12 @@ pub fn qasan_fake_syscall<QT, S>(
_a7: GuestAddr,
) -> SyscallHookResult
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if sys_num == QASAN_FAKESYS_NR {
let qemu = *hooks.qemu();
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
let qemu = emulator_modules.qemu();
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
QasanAction::CheckLoad => {
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();

View File

@ -7,17 +7,17 @@ use std::{
path::PathBuf,
};
use libafl::{inputs::UsesInput, HasMetadata};
use libafl::inputs::UsesInput;
#[cfg(not(feature = "clippy"))]
use crate::sys::libafl_tcg_gen_asan;
use crate::{
helpers::{
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
emu::EmulatorModules,
modules::{
EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks},
qemu::{MemAccessInfo, Qemu, QemuInitError},
qemu::{Hook, MemAccessInfo, Qemu, QemuInitError},
sys::TCGTemp,
GuestAddr, MapInfo,
};
@ -121,13 +121,13 @@ impl From<&MapInfo> for QemuAsanGuestMapping {
}
#[derive(Debug)]
pub struct QemuAsanGuestHelper {
pub struct AsanGuestModule {
filter: QemuInstrumentationAddressRangeFilter,
mappings: Vec<QemuAsanGuestMapping>,
}
#[cfg(any(cpu_target = "aarch64", cpu_target = "x86_64", feature = "clippy"))]
impl QemuAsanGuestHelper {
impl AsanGuestModule {
const HIGH_SHADOW_START: GuestAddr = 0x02008fff7000;
const HIGH_SHADOW_END: GuestAddr = 0x10007fff7fff;
const LOW_SHADOW_START: GuestAddr = 0x00007fff8000;
@ -140,14 +140,14 @@ impl QemuAsanGuestHelper {
cpu_target = "mips",
cpu_target = "ppc"
))]
impl QemuAsanGuestHelper {
impl AsanGuestModule {
const HIGH_SHADOW_START: GuestAddr = 0x28000000;
const HIGH_SHADOW_END: GuestAddr = 0x3fffffff;
const LOW_SHADOW_START: GuestAddr = 0x20000000;
const LOW_SHADOW_END: GuestAddr = 0x23ffffff;
}
impl QemuAsanGuestHelper {
impl AsanGuestModule {
#[must_use]
pub fn default(emu: &Qemu, asan: String) -> Self {
Self::new(emu, asan, QemuInstrumentationAddressRangeFilter::None)
@ -197,7 +197,7 @@ impl QemuAsanGuestHelper {
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuAsanGuestHelper {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for AsanGuestModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter
}
@ -207,18 +207,20 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuAsa
}
}
fn gen_readwrite_guest_asan<QT, S>(
hooks: &mut QemuHooks<QT, S>,
fn gen_readwrite_guest_asan<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
addr: *mut TCGTemp,
info: MemAccessInfo,
) -> Option<u64>
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let h = hooks.match_helper_mut::<QemuAsanGuestHelper>().unwrap();
let h = emulator_modules
.get_mut::<AsanGuestModule>()
.unwrap();
if !h.must_instrument(pc) {
return None;
}
@ -244,55 +246,56 @@ where
#[allow(unused_variables)]
unsafe fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize) {}
fn guest_trace_error_asan<QT, S>(
_hooks: &mut QemuHooks<QT, S>,
fn guest_trace_error_asan<ET, S>(
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_id: u64,
_addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
panic!("I really shouldn't be here");
}
fn guest_trace_error_n_asan<QT, S>(
_hooks: &mut QemuHooks<QT, S>,
fn guest_trace_error_n_asan<ET, S>(
_emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_id: u64,
_addr: GuestAddr,
_n: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
panic!("I really shouldn't be here either");
}
impl<S> QemuHelper<S> for QemuAsanGuestHelper
impl<S> EmulatorModule<S> for AsanGuestModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
hooks.reads(
Hook::Function(gen_readwrite_guest_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_n_asan::<QT, S>),
emulator_modules.reads(
Hook::Function(gen_readwrite_guest_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_n_asan::<ET, S>),
);
hooks.writes(
Hook::Function(gen_readwrite_guest_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_asan::<QT, S>),
Hook::Function(guest_trace_error_n_asan::<QT, S>),
emulator_modules.writes(
Hook::Function(gen_readwrite_guest_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_asan::<ET, S>),
Hook::Function(guest_trace_error_n_asan::<ET, S>),
);
}
}

View File

@ -8,12 +8,12 @@ use rangemap::RangeMap;
use serde::{Deserialize, Serialize};
use crate::{
helpers::{
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
emu::EmulatorModules,
modules::{
EmulatorModule, EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks},
Qemu,
qemu::Hook,
};
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
@ -25,21 +25,21 @@ static DRCOV_LENGTHS: Mutex<Option<HashMap<GuestAddr, GuestUsize>>> = Mutex::new
allow(clippy::unsafe_derive_deserialize)
)] // for SerdeAny
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct QemuDrCovMetadata {
pub struct DrCovMetadata {
pub current_id: u64,
}
impl QemuDrCovMetadata {
impl DrCovMetadata {
#[must_use]
pub fn new() -> Self {
Self { current_id: 0 }
}
}
libafl_bolts::impl_serdeany!(QemuDrCovMetadata);
libafl_bolts::impl_serdeany!(DrCovMetadata);
#[derive(Debug)]
pub struct QemuDrCovHelper {
pub struct DrCovModule {
filter: QemuInstrumentationAddressRangeFilter,
module_mapping: RangeMap<usize, (u16, String)>,
filename: PathBuf,
@ -47,12 +47,11 @@ pub struct QemuDrCovHelper {
drcov_len: usize,
}
impl QemuDrCovHelper {
impl DrCovModule {
#[must_use]
#[allow(clippy::let_underscore_untyped)]
pub fn new(
filter: QemuInstrumentationAddressRangeFilter,
module_mapping: RangeMap<usize, (u16, String)>,
filename: PathBuf,
full_trace: bool,
) -> Self {
@ -63,7 +62,7 @@ impl QemuDrCovHelper {
let _ = DRCOV_LENGTHS.lock().unwrap().insert(HashMap::new());
Self {
filter,
module_mapping,
module_mapping: RangeMap::new(),
filename,
full_trace,
drcov_len: 0,
@ -76,7 +75,7 @@ impl QemuDrCovHelper {
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuDrCovHelper {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for DrCovModule {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter
}
@ -86,31 +85,49 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuDrC
}
}
impl<S> QemuHelper<S> for QemuDrCovHelper
impl<S> EmulatorModule<S> for DrCovModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput + HasMetadata,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.blocks(
Hook::Function(gen_unique_block_ids::<QT, S>),
Hook::Function(gen_block_lengths::<QT, S>),
Hook::Function(exec_trace_block::<QT, S>),
emulator_modules.blocks(
Hook::Function(gen_unique_block_ids::<ET, S>),
Hook::Function(gen_block_lengths::<ET, S>),
Hook::Function(exec_trace_block::<ET, S>),
);
}
fn pre_exec(&mut self, _qemu: Qemu, _input: &S::Input) {}
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
ET: EmulatorModuleTuple<S>,
{
let qemu = emulator_modules.qemu();
fn post_exec<OT>(
for (i, (r, p)) in qemu
.mappings()
.filter_map(|m| {
m.path()
.map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string()))
.filter(|(_, p)| !p.is_empty())
})
.enumerate()
{
self.module_mapping.insert(r, (i as u16, p));
}
}
fn post_exec<OT, ET>(
&mut self,
_qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
) where
OT: ObserversTuple<S>,
ET: EmulatorModuleTuple<S>,
{
let lengths_opt = DRCOV_LENGTHS.lock().unwrap();
let lengths = lengths_opt.as_ref().unwrap();
@ -192,40 +209,34 @@ where
}
}
pub fn gen_unique_block_ids<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_unique_block_ids<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
state: Option<&mut S>,
pc: GuestAddr,
) -> Option<u64>
where
S: UsesInput + HasMetadata,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput + HasMetadata,
ET: EmulatorModuleTuple<S>,
{
let drcov_helper = hooks
.helpers()
.match_first_type::<QemuDrCovHelper>()
.unwrap();
if !drcov_helper.must_instrument(pc) {
let drcov_module = emulator_modules.get::<DrCovModule>().unwrap();
if !drcov_module.must_instrument(pc) {
return None;
}
let state = state.expect("The gen_unique_block_ids hook works only for in-process fuzzing");
if state
.metadata_map_mut()
.get_mut::<QemuDrCovMetadata>()
.get_mut::<DrCovMetadata>()
.is_none()
{
state.add_metadata(QemuDrCovMetadata::new());
state.add_metadata(DrCovMetadata::new());
}
let meta = state
.metadata_map_mut()
.get_mut::<QemuDrCovMetadata>()
.unwrap();
let meta = state.metadata_map_mut().get_mut::<DrCovMetadata>().unwrap();
match DRCOV_MAP.lock().unwrap().as_mut().unwrap().entry(pc) {
Entry::Occupied(e) => {
let id = *e.get();
if drcov_helper.full_trace {
if drcov_module.full_trace {
Some(id)
} else {
None
@ -235,7 +246,7 @@ where
let id = meta.current_id;
e.insert(id);
meta.current_id = id + 1;
if drcov_helper.full_trace {
if drcov_module.full_trace {
// GuestAddress is u32 for 32 bit guests
#[allow(clippy::unnecessary_cast)]
Some(id as u64)
@ -246,20 +257,17 @@ where
}
}
pub fn gen_block_lengths<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn gen_block_lengths<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
pc: GuestAddr,
block_length: GuestUsize,
) where
S: UsesInput + HasMetadata,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput + HasMetadata,
ET: EmulatorModuleTuple<S>,
{
let drcov_helper = hooks
.helpers()
.match_first_type::<QemuDrCovHelper>()
.unwrap();
if !drcov_helper.must_instrument(pc) {
let drcov_module = emulator_modules.get::<DrCovModule>().unwrap();
if !drcov_module.must_instrument(pc) {
return;
}
DRCOV_LENGTHS
@ -270,14 +278,16 @@ pub fn gen_block_lengths<QT, S>(
.insert(pc, block_length);
}
pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<QT, S>, _state: Option<&mut S>, id: u64)
where
QT: QemuHelperTuple<S>,
S: UsesInput + HasMetadata,
pub fn exec_trace_block<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
id: u64,
) where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput + HasMetadata,
{
if hooks
.helpers()
.match_first_type::<QemuDrCovHelper>()
if emulator_modules
.get::<DrCovModule>()
.unwrap()
.full_trace
{

View File

@ -21,9 +21,13 @@ use serde::{Deserialize, Serialize};
#[cfg(not(cpu_target = "hexagon"))]
use crate::SYS_execve;
use crate::{
elf::EasyElf, qemu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple,
QemuHooks, SyscallHookResult,
elf::EasyElf,
emu::EmulatorModules,
modules::{EmulatorModule, EmulatorModuleTuple},
qemu::{ArchExtras, Hook, SyscallHookResult},
CallingConvention, Qemu,
};
#[cfg(cpu_target = "hexagon")]
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
/// <https://github.com/qemu/qemu/blob/11be70677c70fdccd452a3233653949b79e97908/linux-user/hexagon/syscall_nr.h#L230>
@ -146,13 +150,13 @@ pub struct Match {
}
#[derive(Debug)]
pub struct QemuInjectionHelper {
pub struct InjectionModule {
pub tokens: Vec<String>,
definitions: HashMap<String, InjectionDefinition>,
matches_list: Vec<Matches>,
}
impl QemuInjectionHelper {
impl InjectionModule {
/// `configure_injections` is the main function to activate the injection
/// vulnerability detection feature.
pub fn from_yaml<P: AsRef<Path> + Display>(yaml_file: P) -> Result<Self, Error> {
@ -207,20 +211,20 @@ impl QemuInjectionHelper {
})
}
fn on_call_check<S: UsesInput, QT: QemuHelperTuple<S>>(
hooks: &mut QemuHooks<QT, S>,
id: usize,
parameter: u8,
) {
let qemu = hooks.qemu();
fn on_call_check<ET, S>(emulator_modules: &mut EmulatorModules<ET, S>, id: usize, parameter: u8)
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
let qemu = emulator_modules.qemu();
let reg: GuestAddr = qemu
.current_cpu()
.unwrap()
.read_function_argument(CallingConvention::Cdecl, parameter)
.unwrap_or_default();
let helper = hooks.helpers_mut().match_first_type_mut::<Self>().unwrap();
let matches = &helper.matches_list[id];
let module = emulator_modules.get_mut::<Self>().unwrap();
let matches = &module.matches_list[id];
//println!("reg value = {:x}", reg);
@ -252,22 +256,22 @@ impl QemuInjectionHelper {
}
}
impl<S> QemuHelper<S> for QemuInjectionHelper
impl<S> EmulatorModule<S> for InjectionModule
where
S: UsesInput,
S: Unpin + UsesInput,
{
fn init_hooks<QT>(&self, hooks: &QemuHooks<QT, S>)
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
hooks.syscalls(Hook::Function(syscall_hook::<QT, S>));
emulator_modules.syscalls(Hook::Function(syscall_hook::<ET, S>));
}
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
let qemu = *hooks.qemu();
let qemu = emulator_modules.qemu();
let mut libs: Vec<LibInfo> = Vec::new();
for region in qemu.mappings() {
@ -316,7 +320,7 @@ where
let param = func_definition.param;
for hook_addr in hook_addrs {
hooks.instruction(
emulator_modules.instructions(
hook_addr,
Hook::Closure(Box::new(move |hooks, _state, _guest_addr| {
Self::on_call_check(hooks, id, param);
@ -329,8 +333,8 @@ where
}
}
fn syscall_hook<QT, S>(
hooks: &mut QemuHooks<QT, S>, // our instantiated QemuHooks
fn syscall_hook<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>, // our instantiated QemuHooks
_state: Option<&mut S>,
syscall: i32, // syscall number
x0: GuestAddr, // registers ...
@ -343,15 +347,14 @@ fn syscall_hook<QT, S>(
_x7: GuestAddr,
) -> SyscallHookResult
where
QT: QemuHelperTuple<S>,
S: UsesInput,
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
log::trace!("syscall_hook {syscall} {SYS_execve}");
debug_assert!(i32::try_from(SYS_execve).is_ok());
if syscall == SYS_execve as i32 {
let _helper = hooks
.helpers_mut()
.match_first_type_mut::<QemuInjectionHelper>()
let _module = emulator_modules
.get_mut::<InjectionModule>()
.unwrap();
if x0 > 0 && x1 > 0 {
let c_array = x1 as *const *const c_char;

View File

@ -0,0 +1,40 @@
#[cfg(not(cpu_target = "hexagon"))]
pub mod drcov;
#[cfg(not(cpu_target = "hexagon"))]
pub use drcov::DrCovModule;
#[cfg(feature = "injections")]
pub mod injections;
#[cfg(feature = "injections")]
pub use injections::InjectionModule;
#[cfg(not(cpu_target = "hexagon"))]
pub mod snapshot;
#[cfg(not(cpu_target = "hexagon"))]
pub use snapshot::IntervalSnapshotFilter;
#[cfg(not(cpu_target = "hexagon"))]
pub use snapshot::SnapshotModule;
#[cfg(not(cpu_target = "hexagon"))]
pub mod asan;
#[cfg(not(cpu_target = "hexagon"))]
pub use asan::{init_qemu_with_asan, AsanModule};
#[cfg(not(cpu_target = "hexagon"))]
pub mod asan_guest;
#[cfg(not(cpu_target = "hexagon"))]
pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule};
use crate::modules::{HasInstrumentationFilter, QemuInstrumentationAddressRangeFilter};
pub trait StdInstrumentationFilter:
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
{
}
impl<Head> StdInstrumentationFilter for (Head, ()) where
Head: HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
{
}
impl StdInstrumentationFilter for () {}

View File

@ -1,7 +1,7 @@
use std::{cell::UnsafeCell, mem::MaybeUninit, sync::Mutex};
use hashbrown::{HashMap, HashSet};
use libafl::{inputs::UsesInput, HasMetadata};
use libafl::inputs::UsesInput;
use libafl_qemu_sys::{GuestAddr, MmapPerms};
use meminterval::{Interval, IntervalTree};
use thread_local::ThreadLocal;
@ -20,10 +20,9 @@ use crate::SYS_mmap2;
)))]
use crate::SYS_newfstatat;
use crate::{
asan::QemuAsanHelper,
helpers::{QemuHelper, QemuHelperTuple, Range},
hooks::{Hook, QemuHooks},
qemu::SyscallHookResult,
emu::EmulatorModules,
modules::{asan::AsanModule, EmulatorModule, EmulatorModuleTuple, Range},
qemu::{Hook, SyscallHookResult},
Qemu, SYS_brk, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap,
SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
};
@ -32,7 +31,7 @@ use crate::{
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1);
pub type StopExecutionCallback = Box<dyn FnMut(&mut QemuSnapshotHelper, &Qemu)>;
pub type StopExecutionCallback = Box<dyn FnMut(&mut SnapshotModule, &Qemu)>;
#[derive(Clone, Debug)]
pub struct SnapshotPageInfo {
@ -80,7 +79,7 @@ pub enum IntervalSnapshotFilter {
DenyList(Vec<Range<GuestAddr>>),
}
pub struct QemuSnapshotHelper {
pub struct SnapshotModule {
pub accesses: ThreadLocal<UnsafeCell<SnapshotAccessInfo>>,
pub maps: MappingInfo,
pub new_maps: Mutex<MappingInfo>,
@ -94,9 +93,9 @@ pub struct QemuSnapshotHelper {
pub interval_filter: Vec<IntervalSnapshotFilter>,
}
impl core::fmt::Debug for QemuSnapshotHelper {
impl core::fmt::Debug for SnapshotModule {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("QemuSnapshotHelper")
f.debug_struct("SnapshotModule")
.field("accesses", &self.accesses)
.field("new_maps", &self.new_maps)
.field("pages", &self.pages)
@ -108,7 +107,7 @@ impl core::fmt::Debug for QemuSnapshotHelper {
}
}
impl QemuSnapshotHelper {
impl SnapshotModule {
#[must_use]
pub fn new() -> Self {
Self {
@ -658,78 +657,85 @@ impl QemuSnapshotHelper {
}
}
impl Default for QemuSnapshotHelper {
impl Default for SnapshotModule {
fn default() -> Self {
Self::new()
}
}
impl<S> QemuHelper<S> for QemuSnapshotHelper
impl<S> EmulatorModule<S> for SnapshotModule
where
S: UsesInput + HasMetadata,
S: Unpin + UsesInput,
{
fn first_exec<QT>(&self, hooks: &QemuHooks<QT, S>)
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where
QT: QemuHelperTuple<S>,
ET: EmulatorModuleTuple<S>,
{
if hooks.match_helper::<QemuAsanHelper>().is_none() {
// The ASan helper, if present, will call the tracer hook for the snapshot helper as opt
hooks.writes(
if emulator_modules.get::<AsanModule>().is_none() {
// The ASan module, if present, will call the tracer hook for the snapshot helper as opt
emulator_modules.writes(
Hook::Empty,
Hook::Function(trace_write_snapshot::<QT, S, 1>),
Hook::Function(trace_write_snapshot::<QT, S, 2>),
Hook::Function(trace_write_snapshot::<QT, S, 4>),
Hook::Function(trace_write_snapshot::<QT, S, 8>),
Hook::Function(trace_write_n_snapshot::<QT, S>),
Hook::Function(trace_write_snapshot::<ET, S, 1>),
Hook::Function(trace_write_snapshot::<ET, S, 2>),
Hook::Function(trace_write_snapshot::<ET, S, 4>),
Hook::Function(trace_write_snapshot::<ET, S, 8>),
Hook::Function(trace_write_n_snapshot::<ET, S>),
);
}
if !self.accurate_unmap {
hooks.syscalls(Hook::Function(filter_mmap_snapshot::<QT, S>));
emulator_modules.syscalls(Hook::Function(filter_mmap_snapshot::<ET, S>));
}
hooks.after_syscalls(Hook::Function(trace_mmap_snapshot::<QT, S>));
emulator_modules.after_syscalls(Hook::Function(trace_mmap_snapshot::<ET, S>));
}
fn pre_exec(&mut self, qemu: Qemu, _input: &S::Input) {
fn pre_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _input: &S::Input)
where
ET: EmulatorModuleTuple<S>,
{
if self.empty {
self.snapshot(qemu);
self.snapshot(emulator_modules.qemu());
} else {
self.reset(qemu);
self.reset(emulator_modules.qemu());
}
}
}
pub fn trace_write_snapshot<QT, S, const SIZE: usize>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write_snapshot<ET, S, const SIZE: usize>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, SIZE);
}
pub fn trace_write_n_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_write_n_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
_id: u64,
addr: GuestAddr,
size: usize,
) where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(addr, size);
}
#[allow(clippy::too_many_arguments)]
#[allow(non_upper_case_globals)]
pub fn filter_mmap_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn filter_mmap_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
sys_num: i32,
a0: GuestAddr,
@ -742,11 +748,13 @@ pub fn filter_mmap_snapshot<QT, S>(
_a7: GuestAddr,
) -> SyscallHookResult
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
if i64::from(sys_num) == SYS_munmap {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) {
return SyscallHookResult::new(Some(0));
}
@ -754,10 +762,10 @@ where
SyscallHookResult::new(None)
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
#[allow(non_upper_case_globals)]
pub fn trace_mmap_snapshot<QT, S>(
hooks: &mut QemuHooks<QT, S>,
pub fn trace_mmap_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,
result: GuestAddr,
sys_num: i32,
@ -771,21 +779,27 @@ pub fn trace_mmap_snapshot<QT, S>(
_a7: GuestAddr,
) -> GuestAddr
where
S: UsesInput,
QT: QemuHelperTuple<S>,
S: Unpin + UsesInput,
ET: EmulatorModuleTuple<S>,
{
// NOT A COMPLETE LIST OF MEMORY EFFECTS
match i64::from(sys_num) {
SYS_read | SYS_pread64 => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a1, a2 as usize);
}
SYS_readlinkat => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a2, a3 as usize);
}
SYS_futex => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a0, a3 as usize);
}
#[cfg(not(any(
@ -796,27 +810,37 @@ where
)))]
SYS_newfstatat => {
if a2 != 0 {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a2, 4096); // stat is not greater than a page
}
}
#[cfg(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "i386"))]
SYS_fstatat64 => {
if a2 != 0 {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a2, 4096); // stat is not greater than a page
}
}
SYS_statfs | SYS_fstatfs | SYS_fstat => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a1, 4096); // stat is not greater than a page
}
SYS_getrandom => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.access(a0, a1 as usize);
}
SYS_brk => {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
if h.brk != result && result != 0 {
/* brk has changed. we change mapping from the snapshotted brk address to the new target_brk
* If no brk mapping has been made until now, change_mapped won't change anything and just create a new mapping.
@ -838,7 +862,9 @@ where
#[cfg(any(cpu_target = "arm", cpu_target = "mips"))]
if sys_const == SYS_mmap2 {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.add_mapped(result, a1 as usize, Some(prot));
}
}
@ -846,23 +872,31 @@ where
#[cfg(not(cpu_target = "arm"))]
if sys_const == SYS_mmap {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.add_mapped(result, a1 as usize, Some(prot));
}
}
if sys_const == SYS_mremap {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
// TODO get the old permissions from the removed mapping
h.remove_mapped(a0, a1 as usize);
h.add_mapped(result, a2 as usize, None);
} else if sys_const == SYS_mprotect {
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
h.change_mapped(a0, a1 as usize, Some(prot));
}
} else if sys_const == SYS_munmap {
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
let h = emulator_modules
.get_mut::<SnapshotModule>()
.unwrap();
if !h.accurate_unmap && !h.is_unmap_allowed(a0, a1 as usize) {
h.remove_mapped(a0, a1 as usize);
}

View File

@ -0,0 +1,967 @@
//! The high-level hooks
#![allow(clippy::type_complexity, clippy::missing_transmute_annotations)]
use core::{ffi::c_void, fmt::Debug, mem::transmute, ptr};
use libafl::{executors::hooks::inprocess::inprocess_get_state, inputs::UsesInput};
#[cfg(emulation_mode = "usermode")]
use libafl_qemu_sys::libafl_dump_core_hook;
use libafl_qemu_sys::{CPUArchStatePtr, FatPtr, GuestAddr, GuestUsize};
#[cfg(feature = "python")]
use pyo3::{pyclass, pymethods, FromPyObject};
use crate::{
emu::EmulatorModules,
modules::EmulatorModuleTuple,
qemu::{MemAccessInfo, Qemu},
sys::TCGTemp,
HookData, HookId,
};
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
// all kinds of hooks
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum HookRepr {
Function(*const c_void),
Closure(FatPtr),
Empty,
}
#[derive(Debug)]
pub struct HookState<const N: usize, H: HookId> {
id: H,
gen: HookRepr,
post_gen: HookRepr,
execs: [HookRepr; N],
}
impl<const N: usize, H: HookId> HookState<N, H> {
pub fn new(id: H, gen: HookRepr, post_gen: HookRepr, execs: [HookRepr; N]) -> Self {
Self {
id,
gen,
post_gen,
execs,
}
}
pub unsafe fn set_id(&mut self, id: H) {
self.id = id;
}
}
pub enum Hook<F, C, R: Clone> {
Function(F),
Closure(C),
Raw(R),
Empty,
}
#[repr(C)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "python", derive(FromPyObject))]
pub struct SyscallHookResult {
pub retval: GuestAddr,
pub skip_syscall: bool,
}
impl<F, C, R: Clone> Hook<F, C, R> {
pub fn is_empty(&self) -> bool {
matches!(self, Hook::Empty)
}
}
macro_rules! create_wrapper {
($name:ident, ($($param:ident : $param_type:ty),*)) => {
paste::paste! {
pub extern "C" fn [<func_ $name _hook_wrapper>]<ET, S>(hook: &mut c_void, $($param: $param_type),*)
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<c_void>(hook));
func(modules, inprocess_get_state::<S>(), $($param),*);
}
}
pub extern "C" fn [<closure_ $name _hook_wrapper>]<ET, S>(hook: &mut FatPtr, $($param: $param_type),*)
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
let func: &mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)> = transmute(hook);
func(modules, inprocess_get_state::<S>(), $($param),*);
}
}
}
};
($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => {
paste::paste! {
pub extern "C" fn [<func_ $name _hook_wrapper>]<ET, S>(hook: &mut c_void, $($param: $param_type),*) -> $ret_type
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<c_void>(hook));
func(modules, inprocess_get_state::<S>(), $($param),*)
}
}
pub extern "C" fn [<closure_ $name _hook_wrapper>]<ET, S>(hook: &mut FatPtr, $($param: $param_type),*) -> $ret_type
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
let func: &mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> $ret_type> = transmute(hook);
func(modules, inprocess_get_state::<S>(), $($param),*)
}
}
}
};
}
macro_rules! create_gen_wrapper {
($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty, $execs:literal, $hook_id:ident) => {
paste::paste! {
pub extern "C" fn [<$name _gen_hook_wrapper>]<ET, S>(hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*) -> $ret_type
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
match &mut hook.gen {
HookRepr::Function(ptr) => {
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type> =
transmute(*ptr);
func(modules, inprocess_get_state::<S>(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id)
}
HookRepr::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type>,
> = transmute(ptr);
func(modules, inprocess_get_state::<S>(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id)
}
_ => 0,
}
}
}
}
}
}
macro_rules! create_post_gen_wrapper {
($name:ident, ($($param:ident : $param_type:ty),*), $execs:literal, $hook_id:ident) => {
paste::paste! {
pub extern "C" fn [<$name _post_gen_hook_wrapper>]<ET, S>(hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*)
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
match &mut hook.post_gen {
HookRepr::Function(ptr) => {
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
transmute(*ptr);
func(modules, inprocess_get_state::<S>(), $($param),*);
}
HookRepr::Closure(ptr) => {
let func: &mut Box<
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
> = transmute(ptr);
func(modules, inprocess_get_state::<S>(), $($param),*);
}
_ => (),
}
}
}
}
}
}
macro_rules! create_exec_wrapper {
($name:ident, ($($param:ident : $param_type:ty),*), $execidx:literal, $execs:literal, $hook_id:ident) => {
paste::paste! {
pub extern "C" fn [<$name _ $execidx _exec_hook_wrapper>]<ET, S>(hook: &mut HookState<{ $execs }, $hook_id>, $($param: $param_type),*)
where
ET: EmulatorModuleTuple<S>,
S: Unpin + UsesInput,
{
unsafe {
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
match &mut hook.execs[$execidx] {
HookRepr::Function(ptr) => {
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) = transmute(*ptr);
func(modules, inprocess_get_state::<S>(), $($param),*);
}
HookRepr::Closure(ptr) => {
let func: &mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)> =
transmute(ptr);
func(modules, inprocess_get_state::<S>(), $($param),*);
}
_ => (),
}
}
}
}
}
}
macro_rules! create_hook_id {
($name:ident, $sys:ident, true) => {
paste::paste! {
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct [<$name HookId>](pub(crate) usize);
impl [<$name HookId>] {
#[must_use]
pub fn invalid() -> Self {
Self(0)
}
}
impl HookId for [<$name HookId>] {
fn remove(&self, invalidate_block: bool) -> bool {
unsafe { libafl_qemu_sys::$sys(self.0, invalidate_block.into()) != 0 }
}
}
}
};
($name:ident, $sys:ident, false) => {
paste::paste! {
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct [<$name HookId>](pub(crate) usize);
impl [<$name HookId>] {
#[must_use]
pub fn invalid() -> Self {
Self(0)
}
}
impl HookId for [<$name HookId>] {
fn remove(&self, _invalidate_block: bool) -> bool {
unsafe { libafl_qemu_sys::$sys(self.0) != 0 }
}
}
}
};
}
macro_rules! create_hook_types {
($name:ident, $fn_type:ty, $closure_type:ty, $raw_type:ty) => {
paste::paste! {
pub type [<$name HookFn>]<ET, S> = $fn_type;
pub type [<$name HookClosure>]<ET, S> = $closure_type;
pub type [<$name HookRaw>] = $raw_type;
pub type [<$name Hook>]<ET, S> = Hook<
[<$name HookFn>]<ET, S>,
[<$name HookClosure>]<ET, S>,
[<$name HookRaw>],
>;
}
};
}
#[cfg(emulation_mode = "usermode")]
create_hook_id!(PostSyscall, libafl_qemu_remove_post_syscall_hook, false);
#[cfg(emulation_mode = "usermode")]
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
// Instruction hook wrappers
create_hook_types!(
Instruction,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, GuestAddr),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, GuestAddr)>,
extern "C" fn(*const (), pc: GuestAddr)
);
create_hook_id!(Instruction, libafl_qemu_remove_instruction_hook, true);
create_wrapper!(instruction, (pc: GuestAddr));
// Backdoor hook wrappers
create_hook_types!(
Backdoor,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, GuestAddr)>,
extern "C" fn(*const (), cpu: CPUArchStatePtr, pc: GuestAddr)
);
create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true);
create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr));
// Pre-syscall hook wrappers
#[cfg(emulation_mode = "usermode")]
create_hook_types!(
PreSyscall,
fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
sys_num: i32,
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
a3: GuestAddr,
a4: GuestAddr,
a5: GuestAddr,
a6: GuestAddr,
a7: GuestAddr,
) -> SyscallHookResult,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&'a mut S>,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> SyscallHookResult,
>,
extern "C" fn(
*const (),
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> SyscallHookResult
);
#[cfg(emulation_mode = "usermode")]
create_hook_id!(PreSyscall, libafl_qemu_remove_pre_syscall_hook, false);
#[cfg(emulation_mode = "usermode")]
create_wrapper!(
pre_syscall,
(
sys_num: i32,
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
a3: GuestAddr,
a4: GuestAddr,
a5: GuestAddr,
a6: GuestAddr,
a7: GuestAddr
),
SyscallHookResult
);
// Post-syscall hook wrappers
#[cfg(emulation_mode = "usermode")]
create_hook_types!(
PostSyscall,
fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
res: GuestAddr,
sys_num: i32,
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
a3: GuestAddr,
a4: GuestAddr,
a5: GuestAddr,
a6: GuestAddr,
a7: GuestAddr,
) -> GuestAddr,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&mut S>,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr,
>,
extern "C" fn(
*const (),
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr
);
#[cfg(emulation_mode = "usermode")]
create_wrapper!(
post_syscall,
(
res: GuestAddr,
sys_num: i32,
a0: GuestAddr,
a1: GuestAddr,
a2: GuestAddr,
a3: GuestAddr,
a4: GuestAddr,
a5: GuestAddr,
a6: GuestAddr,
a7: GuestAddr
),
GuestAddr
);
// New thread hook wrappers
#[cfg(emulation_mode = "usermode")]
create_hook_types!(
NewThread,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, tid: u32) -> bool,
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u32) -> bool>,
extern "C" fn(*const (), tid: u32) -> bool
);
#[cfg(emulation_mode = "usermode")]
create_wrapper!(new_thread, (tid: u32), bool);
// Edge hook wrappers
create_hook_types!(
EdgeGen,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option<u64>,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&'a mut S>,
GuestAddr,
GuestAddr,
) -> Option<u64>,
>,
extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64
);
create_hook_types!(
EdgeExec,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64)>,
extern "C" fn(*const (), id: u64)
);
create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true);
create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId);
create_exec_wrapper!(edge, (id: u64), 0, 1, EdgeHookId);
// Block hook wrappers
create_hook_types!(
BlockGen,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr) -> Option<u64>,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&'a mut S>,
GuestAddr,
) -> Option<u64>,
>,
unsafe extern "C" fn(*const (), pc: GuestAddr) -> u64
);
create_hook_types!(
BlockPostGen,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&mut S>, GuestAddr, GuestUsize)>,
unsafe extern "C" fn(*const (), pc: GuestAddr, block_length: GuestUsize)
);
create_hook_types!(
BlockExec,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64)>,
unsafe extern "C" fn(*const (), id: u64)
);
create_hook_id!(Block, libafl_qemu_remove_block_hook, true);
create_gen_wrapper!(block, (addr: GuestAddr), u64, 1, BlockHookId);
create_post_gen_wrapper!(block, (addr: GuestAddr, len: GuestUsize), 1, BlockHookId);
create_exec_wrapper!(block, (id: u64), 0, 1, BlockHookId);
// Read hook wrappers
create_hook_types!(
ReadGen,
fn(
qemu_modules: &mut EmulatorModules<ET, S>,
Option<&mut S>,
pc: GuestAddr,
addr: *mut TCGTemp,
info: MemAccessInfo,
) -> Option<u64>,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&'a mut S>,
GuestAddr,
*mut TCGTemp,
MemAccessInfo,
) -> Option<u64>,
>,
unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64
);
create_hook_types!(
ReadExec,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr)>,
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr)
);
create_hook_types!(
ReadExecN,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
Box<
dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr, usize),
>,
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize)
);
create_hook_id!(Read, libafl_qemu_remove_read_hook, true);
create_gen_wrapper!(read, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, ReadHookId);
create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 0, 5, ReadHookId);
create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 1, 5, ReadHookId);
create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 2, 5, ReadHookId);
create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 3, 5, ReadHookId);
create_exec_wrapper!(
read,
(id: u64, addr: GuestAddr, size: usize),
4,
5,
ReadHookId
);
// Write hook wrappers
create_hook_types!(
WriteGen,
fn(
&mut EmulatorModules<ET, S>,
Option<&mut S>,
pc: GuestAddr,
addr: *mut TCGTemp,
info: MemAccessInfo,
) -> Option<u64>,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&'a mut S>,
GuestAddr,
*mut TCGTemp,
MemAccessInfo,
) -> Option<u64>,
>,
unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64
);
create_hook_types!(
WriteExec,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr)>,
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr)
);
create_hook_types!(
WriteExecN,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
Box<
dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr, usize),
>,
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize)
);
create_hook_id!(Write, libafl_qemu_remove_write_hook, true);
create_gen_wrapper!(write, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, WriteHookId);
create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 0, 5, WriteHookId);
create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 1, 5, WriteHookId);
create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 2, 5, WriteHookId);
create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 3, 5, WriteHookId);
create_exec_wrapper!(
write,
(id: u64, addr: GuestAddr, size: usize),
4,
5,
WriteHookId
);
// Cmp hook wrappers
create_hook_types!(
CmpGen,
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr, size: usize) -> Option<u64>,
Box<
dyn for<'a> FnMut(
&'a mut EmulatorModules<ET, S>,
Option<&'a mut S>,
GuestAddr,
usize,
) -> Option<u64>,
>,
unsafe extern "C" fn(*const (), pc: GuestAddr, size: usize) -> u64
);
pub type CmpExecHook<ET, S, SZ> = Hook<
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, v0: SZ, v1: SZ),
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, SZ, SZ)>,
unsafe extern "C" fn(*const (), id: u64, v0: SZ, v1: SZ),
>;
create_hook_id!(Cmp, libafl_qemu_remove_cmp_hook, true);
create_gen_wrapper!(cmp, (pc: GuestAddr, size: usize), u64, 4, CmpHookId);
create_exec_wrapper!(cmp, (id: u64, v0: u8, v1: u8), 0, 4, CmpHookId);
create_exec_wrapper!(cmp, (id: u64, v0: u16, v1: u16), 1, 4, CmpHookId);
create_exec_wrapper!(cmp, (id: u64, v0: u32, v1: u32), 2, 4, CmpHookId);
create_exec_wrapper!(cmp, (id: u64, v0: u64, v1: u64), 3, 4, CmpHookId);
// Crash hook wrappers
#[cfg(emulation_mode = "usermode")]
pub type CrashHookClosure<ET, S> = Box<dyn FnMut(&mut EmulatorModules<ET, S>, i32)>;
/// The thin wrapper around QEMU hooks.
/// It is considered unsafe to use it directly.
#[derive(Clone, Copy, Debug)]
pub struct QemuHooks {
_private: (),
}
impl QemuHooks {
/// Get a `QemuHooks` object.
/// Same as `QemuHooks::get`, but without checking whether `QemuHooks` have been correctly initialized.
///
/// # Safety
///
/// Should not be used out of Qemu itself.
/// Prefer `Qemu::get` for a safe version of this method.
#[must_use]
pub unsafe fn get_unchecked() -> Self {
QemuHooks { _private: () }
}
#[must_use]
pub fn get() -> Option<Self> {
// Use QEMU to check if hooks have been initialized.
Some(Qemu::get()?.hooks())
}
// TODO set T lifetime to be like Emulator
#[allow(clippy::missing_transmute_annotations)]
pub fn add_instruction_hooks<T: Into<HookData>>(
&self,
data: T,
addr: GuestAddr,
callback: extern "C" fn(T, GuestAddr),
invalidate_block: bool,
) -> InstructionHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, GuestAddr) = transmute(callback);
let num = libafl_qemu_sys::libafl_qemu_add_instruction_hooks(
addr.into(),
Some(callback),
data,
i32::from(invalidate_block),
);
InstructionHookId(num)
}
}
#[must_use]
pub fn remove_instruction_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize {
unsafe {
libafl_qemu_sys::libafl_qemu_remove_instruction_hooks_at(
addr.into(),
i32::from(invalidate_block),
)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_edge_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, GuestAddr) -> u64>,
exec: Option<unsafe extern "C" fn(T, u64)>,
) -> EdgeHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<unsafe extern "C" fn(u64, GuestAddr, GuestAddr) -> u64> =
transmute(gen);
let exec: Option<unsafe extern "C" fn(u64, u64)> = transmute(exec);
let num = libafl_qemu_sys::libafl_add_edge_hook(gen, exec, data);
EdgeHookId(num)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_block_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr) -> u64>,
post_gen: Option<unsafe extern "C" fn(T, GuestAddr, GuestUsize)>,
exec: Option<unsafe extern "C" fn(T, u64)>,
) -> BlockHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<unsafe extern "C" fn(u64, GuestAddr) -> u64> = transmute(gen);
let post_gen: Option<unsafe extern "C" fn(u64, GuestAddr, GuestUsize)> =
transmute(post_gen);
let exec: Option<unsafe extern "C" fn(u64, u64)> = transmute(exec);
let num = libafl_qemu_sys::libafl_add_block_hook(gen, post_gen, exec, data);
BlockHookId(num)
}
}
/// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions
///
/// `gen` gets passed the current programm counter, mutable access to a `TCGTemp` and information about the memory
/// access being performed.
/// The `u64` return value is an id that gets passed to the `exec` functions as their second argument.
///
/// `exec` hooks get invoked on every read performed by the guest
///
/// `exec1`-`exec8` special case accesses of width 1-8
///
/// If there is no specialized hook for a given read width, the `exec_n` will be
/// called and its last argument will specify the access width
#[allow(clippy::missing_transmute_annotations)]
pub fn add_read_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
) -> ReadHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<
unsafe extern "C" fn(
u64,
GuestAddr,
*mut TCGTemp,
libafl_qemu_sys::MemOpIdx,
) -> u64,
> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, usize)> =
transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_read_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,
);
ReadHookId(num)
}
}
// TODO add MemOp info
#[allow(clippy::missing_transmute_annotations)]
pub fn add_write_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
) -> WriteHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<
unsafe extern "C" fn(
u64,
GuestAddr,
*mut TCGTemp,
libafl_qemu_sys::MemOpIdx,
) -> u64,
> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, usize)> =
transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_write_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,
);
WriteHookId(num)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_cmp_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, usize) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, u8, u8)>,
exec2: Option<unsafe extern "C" fn(T, u64, u16, u16)>,
exec4: Option<unsafe extern "C" fn(T, u64, u32, u32)>,
exec8: Option<unsafe extern "C" fn(T, u64, u64, u64)>,
) -> CmpHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<unsafe extern "C" fn(u64, GuestAddr, usize) -> u64> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, u8, u8)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, u16, u16)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, u32, u32)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, u64, u64)> = transmute(exec8);
let num = libafl_qemu_sys::libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data);
CmpHookId(num)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_backdoor_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, CPUArchStatePtr, GuestAddr),
) -> BackdoorHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, CPUArchStatePtr, GuestAddr) = transmute(callback);
let num = libafl_qemu_sys::libafl_add_backdoor_hook(Some(callback), data);
BackdoorHookId(num)
}
}
}
#[cfg(emulation_mode = "usermode")]
impl QemuHooks {
#[allow(clippy::type_complexity)]
pub fn add_pre_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> SyscallHookResult,
) -> PreSyscallHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(
u64,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> libafl_qemu_sys::syshook_ret = transmute(callback);
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
PreSyscallHookId(num)
}
}
pub fn add_new_thread_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, tid: u32) -> bool,
) -> NewThreadHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, u32) -> bool = transmute(callback);
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
NewThreadHookId(num)
}
}
#[allow(clippy::type_complexity)]
pub fn add_post_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr,
) -> PostSyscallHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(
u64,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr = transmute(callback);
let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data);
PostSyscallHookId(num)
}
}
#[allow(clippy::type_complexity)]
#[allow(clippy::unused_self)]
pub(crate) fn set_crash_hook(self, callback: extern "C" fn(i32)) {
unsafe {
libafl_dump_core_hook = callback;
}
}
}
#[cfg(feature = "python")]
#[pymethods]
impl SyscallHookResult {
#[new]
#[must_use]
pub fn new(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
skip_syscall: false,
},
|v| Self {
retval: v,
skip_syscall: true,
},
)
}
}
#[cfg(not(feature = "python"))]
impl SyscallHookResult {
#[must_use]
pub fn new(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
skip_syscall: false,
},
|v| Self {
retval: v,
skip_syscall: true,
},
)
}
}

View File

@ -13,7 +13,6 @@ use std::{
ops::Range,
pin::Pin,
ptr,
ptr::{addr_of, null},
};
use libafl_bolts::os::unix_signals::Signal;
@ -26,12 +25,10 @@ use libafl_qemu_sys::{
libafl_qemu_cpu_index, libafl_qemu_current_cpu, libafl_qemu_gdb_reply, libafl_qemu_get_cpu,
libafl_qemu_num_cpus, libafl_qemu_num_regs, libafl_qemu_read_reg,
libafl_qemu_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint,
libafl_qemu_write_reg, CPUArchState, CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr,
GuestPhysAddr, GuestUsize, GuestVirtAddr, TCGTemp,
libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize,
GuestVirtAddr,
};
use num_traits::Num;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use strum::IntoEnumIterator;
use crate::{GuestAddrKind, GuestReg, Regs};
@ -47,49 +44,11 @@ mod systemmode;
#[allow(unused_imports)]
pub use systemmode::*;
pub const SKIP_EXEC_HOOK: u64 = u64::MAX;
mod hooks;
pub use hooks::*;
static mut QEMU_IS_INITIALIZED: bool = false;
macro_rules! create_hook_id {
($name:ident, $sys:ident, true) => {
paste::paste! {
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct [<$name HookId>](pub(crate) usize);
impl HookId for [<$name HookId>] {
fn remove(&self, invalidate_block: bool) -> bool {
unsafe { libafl_qemu_sys::$sys(self.0, invalidate_block.into()) != 0 }
}
}
}
};
($name:ident, $sys:ident, false) => {
paste::paste! {
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct [<$name HookId>](pub(crate) usize);
impl HookId for [<$name HookId>] {
fn remove(&self, _invalidate_block: bool) -> bool {
unsafe { libafl_qemu_sys::$sys(self.0) != 0 }
}
}
}
};
}
create_hook_id!(Instruction, libafl_qemu_remove_hook, true);
create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true);
create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true);
create_hook_id!(Block, libafl_qemu_remove_block_hook, true);
create_hook_id!(Read, libafl_qemu_remove_read_hook, true);
create_hook_id!(Write, libafl_qemu_remove_write_hook, true);
create_hook_id!(Cmp, libafl_qemu_remove_cmp_hook, true);
#[cfg(emulation_mode = "usermode")]
create_hook_id!(PreSyscall, libafl_qemu_remove_pre_syscall_hook, false);
#[cfg(emulation_mode = "usermode")]
create_hook_id!(PostSyscall, libafl_qemu_remove_post_syscall_hook, false);
#[cfg(emulation_mode = "usermode")]
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
#[derive(Debug)]
pub enum QemuInitError {
MultipleInstances,
@ -187,17 +146,8 @@ pub struct Qemu {
_private: (),
}
// syshook_ret
#[repr(C)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "python", derive(FromPyObject))]
pub struct SyscallHookResult {
pub retval: GuestAddr,
pub skip_syscall: bool,
}
#[derive(Debug, Clone)]
pub struct EmulatorMemoryChunk {
pub struct QemuMemoryChunk {
addr: GuestAddrKind,
size: GuestReg,
cpu: Option<CPU>,
@ -363,11 +313,6 @@ pub trait ArchExtras {
#[allow(clippy::unused_self)]
impl CPU {
#[must_use]
pub fn qemu(&self) -> Qemu {
unsafe { Qemu::get_unchecked() }
}
#[must_use]
#[allow(clippy::cast_sign_loss)]
pub fn index(&self) -> usize {
@ -421,7 +366,7 @@ impl CPU {
let val = GuestReg::to_le(val.into());
let success =
unsafe { libafl_qemu_write_reg(self.ptr, reg_id, addr_of!(val) as *const u8) };
unsafe { libafl_qemu_write_reg(self.ptr, reg_id, ptr::addr_of!(val) as *const u8) };
if success == 0 {
Err(QemuRWError {
kind: QemuRWErrorKind::Write,
@ -606,7 +551,7 @@ impl Qemu {
.map(|(k, v)| format!("{}={}\0", &k, &v))
.collect();
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect();
envp.push(null());
envp.push(ptr::null());
unsafe {
#[cfg(emulation_mode = "usermode")]
qemu_user_init(argc, argv.as_ptr(), envp.as_ptr());
@ -621,8 +566,14 @@ impl Qemu {
Ok(Qemu { _private: () })
}
#[must_use]
pub fn hooks(&self) -> QemuHooks {
unsafe { QemuHooks::get_unchecked() }
}
/// Get a QEMU object.
/// Same as `Qemu::get`, but without checking whether QEMU has been correctly initialized.
/// Since Qemu is a ZST, this operation is free.
///
/// # Safety
///
@ -644,7 +595,15 @@ impl Qemu {
}
}
fn post_run(&self) -> Result<QemuExitReason, QemuExitError> {
/// This function will run the emulator until the next breakpoint / sync exit, or until finish.
/// It is a low-level function and simply kicks QEMU.
/// # Safety
///
/// Should, in general, be safe to call.
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
self.run_inner();
let exit_reason = unsafe { libafl_get_exit_reason() };
if exit_reason.is_null() {
Err(QemuExitError::UnexpectedExit)
@ -809,193 +768,11 @@ impl Qemu {
}
}
// TODO set T lifetime to be like Emulator
#[allow(clippy::missing_transmute_annotations)]
pub fn set_hook<T: Into<HookData>>(
&self,
data: T,
addr: GuestAddr,
callback: extern "C" fn(T, GuestAddr),
invalidate_block: bool,
) -> InstructionHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, GuestAddr) = transmute(callback);
let num = libafl_qemu_sys::libafl_qemu_set_hook(
addr.into(),
Some(callback),
data,
i32::from(invalidate_block),
);
InstructionHookId(num)
}
}
#[must_use]
pub fn remove_hook(&self, id: impl HookId, invalidate_block: bool) -> bool {
id.remove(invalidate_block)
}
#[must_use]
pub fn remove_hooks_at(&self, addr: GuestAddr, invalidate_block: bool) -> usize {
unsafe {
libafl_qemu_sys::libafl_qemu_remove_hooks_at(addr.into(), i32::from(invalidate_block))
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_edge_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, GuestAddr) -> u64>,
exec: Option<unsafe extern "C" fn(T, u64)>,
) -> EdgeHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<unsafe extern "C" fn(u64, GuestAddr, GuestAddr) -> u64> =
transmute(gen);
let exec: Option<unsafe extern "C" fn(u64, u64)> = transmute(exec);
let num = libafl_qemu_sys::libafl_add_edge_hook(gen, exec, data);
EdgeHookId(num)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_block_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr) -> u64>,
post_gen: Option<unsafe extern "C" fn(T, GuestAddr, GuestUsize)>,
exec: Option<unsafe extern "C" fn(T, u64)>,
) -> BlockHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<unsafe extern "C" fn(u64, GuestAddr) -> u64> = transmute(gen);
let post_gen: Option<unsafe extern "C" fn(u64, GuestAddr, GuestUsize)> =
transmute(post_gen);
let exec: Option<unsafe extern "C" fn(u64, u64)> = transmute(exec);
let num = libafl_qemu_sys::libafl_add_block_hook(gen, post_gen, exec, data);
BlockHookId(num)
}
}
/// `data` can be used to pass data that can be accessed as the first argument in the `gen` and the `exec` functions
///
/// `gen` gets passed the current programm counter, mutable access to a `TCGTemp` and information about the memory
/// access being performed.
/// The `u64` return value is an id that gets passed to the `exec` functions as their second argument.
///
/// `exec` hooks get invoked on every read performed by the guest
///
/// `exec1`-`exec8` special case accesses of width 1-8
///
/// If there is no specialized hook for a given read width, the `exec_n` will be
/// called and its last argument will specify the access width
#[allow(clippy::missing_transmute_annotations)]
pub fn add_read_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
) -> ReadHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<
unsafe extern "C" fn(
u64,
GuestAddr,
*mut TCGTemp,
libafl_qemu_sys::MemOpIdx,
) -> u64,
> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, usize)> =
transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_read_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,
);
ReadHookId(num)
}
}
// TODO add MemOp info
#[allow(clippy::missing_transmute_annotations)]
pub fn add_write_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
) -> WriteHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<
unsafe extern "C" fn(
u64,
GuestAddr,
*mut TCGTemp,
libafl_qemu_sys::MemOpIdx,
) -> u64,
> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, usize)> =
transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_write_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,
);
WriteHookId(num)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_cmp_hooks<T: Into<HookData>>(
&self,
data: T,
gen: Option<unsafe extern "C" fn(T, GuestAddr, usize) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, u8, u8)>,
exec2: Option<unsafe extern "C" fn(T, u64, u16, u16)>,
exec4: Option<unsafe extern "C" fn(T, u64, u32, u32)>,
exec8: Option<unsafe extern "C" fn(T, u64, u64, u64)>,
) -> CmpHookId {
unsafe {
let data: u64 = data.into().0;
let gen: Option<unsafe extern "C" fn(u64, GuestAddr, usize) -> u64> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, u8, u8)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, u16, u16)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, u32, u32)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, u64, u64)> = transmute(exec8);
let num = libafl_qemu_sys::libafl_add_cmp_hook(gen, exec1, exec2, exec4, exec8, data);
CmpHookId(num)
}
}
#[allow(clippy::missing_transmute_annotations)]
pub fn add_backdoor_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, CPUArchStatePtr, GuestAddr),
) -> BackdoorHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, CPUArchStatePtr, GuestAddr) = transmute(callback);
let num = libafl_qemu_sys::libafl_add_backdoor_hook(Some(callback), data);
BackdoorHookId(num)
}
}
#[allow(clippy::type_complexity)]
pub fn add_gdb_cmd(&self, callback: Box<dyn FnMut(&Self, &str) -> bool>) {
unsafe {
@ -1095,7 +872,7 @@ impl PartialOrd for GuestAddrKind {
}
}
impl EmulatorMemoryChunk {
impl QemuMemoryChunk {
#[must_use]
pub fn addr(&self) -> GuestAddrKind {
self.addr
@ -1125,7 +902,7 @@ impl EmulatorMemoryChunk {
}
#[must_use]
pub fn get_slice(&self, range: &Range<GuestAddr>) -> Option<EmulatorMemoryChunk> {
pub fn get_slice(&self, range: &Range<GuestAddr>) -> Option<QemuMemoryChunk> {
let new_addr = self.addr + range.start;
let slice_size = range.clone().count();
@ -1142,7 +919,7 @@ impl EmulatorMemoryChunk {
/// Returns the number of bytes effectively written.
#[must_use]
pub fn write(&self, qemu: &Qemu, input: &[u8]) -> GuestReg {
pub fn write(&self, qemu: Qemu, input: &[u8]) -> GuestReg {
let max_len: usize = self.size.try_into().unwrap();
let input_sliced = if input.len() > max_len {
@ -1175,42 +952,6 @@ impl EmulatorMemoryChunk {
}
}
#[cfg(feature = "python")]
#[pymethods]
impl SyscallHookResult {
#[new]
#[must_use]
pub fn new(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
skip_syscall: false,
},
|v| Self {
retval: v,
skip_syscall: true,
},
)
}
}
#[cfg(not(feature = "python"))]
impl SyscallHookResult {
#[must_use]
pub fn new(value: Option<GuestAddr>) -> Self {
value.map_or(
Self {
retval: 0,
skip_syscall: false,
},
|v| Self {
retval: v,
skip_syscall: true,
},
)
}
}
#[cfg(feature = "python")]
pub mod pybind {
use pyo3::{exceptions::PyValueError, prelude::*};
@ -1298,8 +1039,12 @@ pub mod pybind {
unsafe {
let idx = PY_GENERIC_HOOKS.len();
PY_GENERIC_HOOKS.push((addr, hook));
self.qemu
.set_hook(idx as u64, addr, py_generic_hook_wrapper, true);
self.qemu.hooks().add_instruction_hooks(
idx as u64,
addr,
py_generic_hook_wrapper,
true,
);
}
}
@ -1307,7 +1052,7 @@ pub mod pybind {
unsafe {
PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
}
self.qemu.remove_hooks_at(addr, true)
self.qemu.hooks().remove_instruction_hooks_at(addr, true)
}
}
}

View File

@ -15,8 +15,8 @@ use libafl_qemu_sys::{
use num_traits::Zero;
use crate::{
EmulatorMemoryChunk, FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuExitError,
QemuExitReason, QemuSnapshotCheckResult, CPU,
FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuSnapshotCheckResult,
CPU,
};
pub(super) extern "C" fn qemu_cleanup_atexit() {
@ -200,17 +200,9 @@ impl Qemu {
);
}
/// This function will run the emulator until the next breakpoint / sync exit, or until finish.
/// It is a low-level function and simply kicks QEMU.
/// # Safety
///
/// Should, in general, be safe to call.
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
pub(super) unsafe fn run_inner(&self) {
vm_start();
qemu_main_loop();
self.post_run()
}
pub fn save_snapshot(&self, name: &str, sync: bool) {
@ -295,7 +287,7 @@ impl Qemu {
}
}
impl EmulatorMemoryChunk {
impl QemuMemoryChunk {
pub fn phys_iter(&self, qemu: Qemu) -> PhysMemoryIter {
PhysMemoryIter {
addr: self.addr,

View File

@ -4,19 +4,16 @@ use std::{
};
use libafl_qemu_sys::{
exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk,
libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk,
mmap_next_start, pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize,
IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
exec_path, free_self_maps, guest_base, libafl_force_dfl, libafl_get_brk, libafl_load_addr,
libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk, mmap_next_start,
pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize, IntervalTreeNode,
IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
};
use libc::c_int;
#[cfg(feature = "python")]
use pyo3::{pyclass, pymethods, IntoPy, PyObject, PyRef, PyRefMut, Python};
use crate::{
HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu, QemuExitError,
QemuExitReason, SyscallHookResult, CPU,
};
use crate::{Qemu, CPU};
#[cfg_attr(feature = "python", pyclass(unsendable))]
pub struct GuestMaps {
@ -135,15 +132,8 @@ impl Qemu {
}
}
/// This function will run the emulator until the next breakpoint, or until finish.
/// # Safety
///
/// Should, in general, be safe to call.
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
pub(super) unsafe fn run_inner(self) {
libafl_qemu_run();
self.post_run()
}
#[must_use]
@ -237,100 +227,6 @@ impl Qemu {
Err(format!("Failed to unmap {addr}"))
}
}
#[allow(clippy::type_complexity)]
pub fn add_pre_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> SyscallHookResult,
) -> PreSyscallHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(
u64,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> libafl_qemu_sys::syshook_ret = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
PreSyscallHookId(num)
}
}
#[allow(clippy::type_complexity)]
pub fn add_post_syscall_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(
T,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr,
) -> PostSyscallHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(
u64,
GuestAddr,
i32,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
GuestAddr,
) -> GuestAddr = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data);
PostSyscallHookId(num)
}
}
pub fn add_new_thread_hook<T: Into<HookData>>(
&self,
data: T,
callback: extern "C" fn(T, tid: u32) -> bool,
) -> NewThreadHookId {
unsafe {
let data: u64 = data.into().0;
let callback: extern "C" fn(u64, u32) -> bool = core::mem::transmute(callback);
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
NewThreadHookId(num)
}
}
#[allow(clippy::type_complexity)]
pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) {
unsafe {
libafl_dump_core_hook = callback;
}
}
}
#[cfg(feature = "python")]
@ -340,7 +236,7 @@ pub mod pybind {
exceptions::PyValueError, pymethods, types::PyInt, FromPyObject, PyObject, PyResult, Python,
};
use crate::{pybind::Qemu, SyscallHookResult};
use crate::{pybind::Qemu, qemu::hooks::SyscallHookResult};
static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
@ -438,6 +334,7 @@ pub mod pybind {
PY_SYSCALL_HOOK = Some(hook);
}
self.qemu
.hooks()
.add_pre_syscall_hook(0u64, py_syscall_hook_wrapper);
}
}

View File

@ -8,7 +8,9 @@ use libafl::state::{HasExecutions, State};
use crate::{
command::{CommandManager, IsCommand},
get_exit_arch_regs, EmulatorExitHandler, GuestReg, QemuHelperTuple, Regs, CPU,
get_exit_arch_regs,
modules::EmulatorModuleTuple,
EmulatorExitHandler, GuestReg, Regs, CPU,
};
#[derive(Debug, Clone, Enum)]
@ -24,30 +26,30 @@ pub enum ExitArgs {
}
#[derive(Debug)]
pub struct SyncExit<CM, E, QT, S>
pub struct SyncExit<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
command: Rc<dyn IsCommand<CM, E, QT, S>>,
command: Rc<dyn IsCommand<CM, EH, ET, S>>,
}
impl<CM, E, QT, S> SyncExit<CM, E, QT, S>
impl<CM, EH, ET, S> SyncExit<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
#[must_use]
pub fn new(command: Rc<dyn IsCommand<CM, E, QT, S>>) -> Self {
pub fn new(command: Rc<dyn IsCommand<CM, EH, ET, S>>) -> Self {
Self { command }
}
#[must_use]
pub fn command(&self) -> Rc<dyn IsCommand<CM, E, QT, S>> {
pub fn command(&self) -> Rc<dyn IsCommand<CM, EH, ET, S>> {
self.command.clone()
}
@ -62,12 +64,12 @@ where
}
}
impl<CM, E, QT, S> Display for SyncExit<CM, E, QT, S>
impl<CM, EH, ET, S> Display for SyncExit<CM, EH, ET, S>
where
CM: CommandManager<E, QT, S>,
E: EmulatorExitHandler<QT, S>,
QT: QemuHelperTuple<S>,
S: State + HasExecutions,
CM: CommandManager<EH, ET, S>,
EH: EmulatorExitHandler<ET, S>,
ET: EmulatorModuleTuple<S>,
S: Unpin + State + HasExecutions,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.command)

View File

@ -35,10 +35,14 @@ use libafl_bolts::{
tuples::{tuple_list, Handled, Merge},
AsSlice,
};
pub use libafl_qemu::qemu::Qemu;
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
use libafl_qemu::QemuCmpLogHelper;
use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks};
use libafl_qemu::modules::CmpLogModule;
pub use libafl_qemu::qemu::Qemu;
use libafl_qemu::{
command::NopCommandManager,
modules::edges::{self, EdgeCoverageModule},
Emulator, NopEmulatorExitHandler, QemuExecutor,
};
use libafl_targets::{edges_map_mut_ptr, CmpLogObserver};
use typed_builder::TypedBuilder;
@ -119,7 +123,7 @@ where
{
/// Run the fuzzer
#[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn run(&mut self, qemu: &Qemu) {
pub fn run(&mut self, qemu: Qemu) {
let conf = match self.configuration.as_ref() {
Some(name) => EventConfig::from_name(name),
None => EventConfig::AlwaysUnique,
@ -222,19 +226,26 @@ where
};
if self.use_cmplog.unwrap_or(false) {
let mut hooks = QemuHooks::new(
*qemu,
let modules = {
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
tuple_list!(
QemuEdgeCoverageHelper::default(),
QemuCmpLogHelper::default(),
),
{
tuple_list!(EdgeCoverageModule::default(), CmpLogModule::default(),)
}
#[cfg(any(feature = "mips", feature = "hexagon"))]
tuple_list!(QemuEdgeCoverageHelper::default()),
);
{
tuple_list!(EdgeCoverageModule::default())
}
};
let mut emulator = Emulator::new_with_qemu(
qemu,
modules,
NopEmulatorExitHandler,
NopCommandManager,
)?;
let executor = QemuExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
@ -334,11 +345,17 @@ where
}
}
} else {
let mut hooks =
QemuHooks::new(*qemu, tuple_list!(QemuEdgeCoverageHelper::default()));
let tools = tuple_list!(EdgeCoverageModule::default());
let mut emulator = Emulator::new_with_qemu(
qemu,
tools,
NopEmulatorExitHandler,
NopCommandManager,
)?;
let mut executor = QemuExecutor::new(
&mut hooks,
&mut emulator,
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
@ -520,7 +537,7 @@ pub mod pybind {
.tokens_file(self.tokens_file.clone())
.iterations(self.iterations)
.build()
.run(&qemu.qemu);
.run(qemu.qemu);
}
}

View File

@ -22,8 +22,7 @@ fn criterion_benchmark(c: &mut Criterion) {
});*/
c.bench_function("ahash", |b| {
b.iter(|| {
let mut hasher =
black_box(ahash::RandomState::with_seeds(123, 456, 789, 123).build_hasher());
let mut hasher = ahash::RandomState::with_seeds(123, 456, 789, 123).build_hasher();
hasher.write(black_box(&bench_vec));
black_box(hasher.finish());
});