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:
parent
f5e47c33fb
commit
c96ea616fe
@ -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,
|
||||
|
@ -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(),
|
||||
// QemuAsanHelper::default(asan),
|
||||
//QemuSnapshotHelper::new()
|
||||
),
|
||||
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,
|
||||
|
@ -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),*);
|
||||
|
@ -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,
|
||||
|
@ -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}",
|
||||
]
|
||||
|
@ -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),*);
|
||||
|
@ -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(
|
||||
let emulator_modules = tuple_list!(DrCovModule::new(
|
||||
QemuInstrumentationAddressRangeFilter::None,
|
||||
cov_path,
|
||||
false,
|
||||
));
|
||||
|
||||
let mut emulator = Emulator::new_with_qemu(
|
||||
qemu,
|
||||
tuple_list!(QemuDrCovHelper::new(
|
||||
QemuInstrumentationAddressRangeFilter::None,
|
||||
rangemap,
|
||||
coverage,
|
||||
false,
|
||||
)),
|
||||
);
|
||||
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());
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,13 @@ use libafl_bolts::{
|
||||
tuples::{tuple_list, Merge},
|
||||
};
|
||||
use libafl_qemu::{
|
||||
cmplog::CmpLogObserver,
|
||||
edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||
helpers::QemuHelperTuple,
|
||||
Qemu, QemuExecutor, QemuHooks,
|
||||
command::NopCommandManager,
|
||||
modules::{
|
||||
cmplog::CmpLogObserver,
|
||||
edges::{edges_map_mut_ptr, EDGES_MAP_SIZE_IN_USE, MAX_EDGES_FOUND},
|
||||
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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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, ¤t_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,
|
||||
);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)?;
|
||||
{
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
@ -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
1240
libafl_qemu/src/emu/hooks.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 exit_handler = emu.exit_handler.borrow();
|
||||
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()`."
|
||||
|
@ -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]) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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
@ -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;
|
||||
|
@ -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();
|
@ -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,
|
||||
);
|
@ -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());
|
@ -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]
|
24
libafl_qemu/src/modules/systemmode/mod.rs
Normal file
24
libafl_qemu/src/modules/systemmode/mod.rs
Normal 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 {}
|
@ -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();
|
@ -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>),
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
@ -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;
|
40
libafl_qemu/src/modules/usermode/mod.rs
Normal file
40
libafl_qemu/src/modules/usermode/mod.rs
Normal 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 () {}
|
@ -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);
|
||||
}
|
967
libafl_qemu/src/qemu/hooks.rs
Normal file
967
libafl_qemu/src/qemu/hooks.rs
Normal 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,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user