Qemu helpers & hooks refactoring (#2267)

* Helper is now called Module.

* Emulator now contains hooks state.

* Emulator is managed by QemuExecutor.

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

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ macro_rules! assert_unique_feature {
() => {}; () => {};
($first:tt $(,$rest:tt)*) => { ($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")); compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)* )*
assert_unique_feature!($($rest),*); assert_unique_feature!($($rest),*);

View File

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

View File

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

View File

@ -5,7 +5,7 @@ macro_rules! assert_unique_feature {
() => {}; () => {};
($first:tt $(,$rest:tt)*) => { ($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")); compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)* )*
assert_unique_feature!($($rest),*); assert_unique_feature!($($rest),*);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -125,7 +125,7 @@ where
} }
self.inner.hooks.pre_exec_all(state, input); self.inner.hooks.pre_exec_all(state, input);
let ret = (self.harness_fn.borrow_mut())(input, &mut self.exposed_executor_state); let ret = self.harness_fn.borrow_mut()(input, &mut self.exposed_executor_state);
self.inner.hooks.post_exec_all(state, input); self.inner.hooks.post_exec_all(state, input);
self.inner.leave_target(fuzzer, state, mgr, input); self.inner.leave_target(fuzzer, state, mgr, input);

View File

@ -95,7 +95,13 @@ pub fn generate(
) -> Result<Bindings, BindgenError> { ) -> Result<Bindings, BindgenError> {
let wrapper_h = build_dir.join("wrapper.h"); 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() let bindings = bindgen::Builder::default()
.derive_debug(true) .derive_debug(true)

View File

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

View File

@ -25,6 +25,9 @@ mod build;
pub use build::build; pub use build::build;
#[rustversion::nightly]
use crate::build::QEMU_REVISION;
const LLVM_VERSION_MAX: i32 = 33; const LLVM_VERSION_MAX: i32 = 33;
static mut CARGO_RPATH: Option<Vec<String>> = None; static mut CARGO_RPATH: Option<Vec<String>> = None;
@ -257,7 +260,7 @@ pub fn store_generated_content_if_different(
file_to_update: &Path, file_to_update: &Path,
fresh_content: &[u8], fresh_content: &[u8],
content_file_to_update: Option<Vec<u8>>, content_file_to_update: Option<Vec<u8>>,
first_line_prefix: Option<&str>, first_line_prefixes: Vec<&str>,
force_regeneration: bool, force_regeneration: bool,
) { ) {
let mut must_rewrite_file = true; let mut must_rewrite_file = true;
@ -299,7 +302,12 @@ pub fn store_generated_content_if_different(
}; };
if must_rewrite_file { 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"); 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] #[rustversion::nightly]
pub fn maybe_generate_stub_bindings( pub fn maybe_generate_stub_bindings(
cpu_target: &str, 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" { if env::var("CARGO_CFG_DOC").is_ok() && cpu_target == "x86_64" && emulation_mode == "usermode" {
let current_rustc_version = let current_rustc_version =
rustc_version::version().expect("Could not get 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 // We only try to store the stub if the current rustc version is strictly bigger than the one used to generate
// the versioned stub. // the versioned stub or the qemu hash differs.
let (try_generate, force_regeneration, stub_content): (bool, bool, Option<Vec<u8>>) = let (try_generate, force_regeneration, stub_content) =
if let Ok(stub_file) = File::open(stub_bindings_file) { parse_stub(stub_bindings_file, &current_rustc_version);
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)
};
let header = format!("/* {current_rustc_version} */"); let header = format!("/* {current_rustc_version} */");
@ -381,7 +415,10 @@ pub fn maybe_generate_stub_bindings(
.expect("Could not read generated bindings file") .expect("Could not read generated bindings file")
.as_slice(), .as_slice(),
stub_content, stub_content,
Some(header.as_str()), vec![
header.as_str(),
format!("/* qemu git hash: {QEMU_REVISION} */").as_str(),
],
force_regeneration, force_regeneration,
); );
} }

View File

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

View File

@ -2,9 +2,11 @@ use core::{slice::from_raw_parts, str::from_utf8_unchecked};
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
use paste::paste; use paste::paste;
#[cfg(feature = "python")]
use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python};
use strum_macros::EnumIter; 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! { extern_c_checked! {
pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32; 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, 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 { impl From<libafl_mapinfo> for MapInfo {
fn from(map_info: libafl_mapinfo) -> Self { fn from(map_info: libafl_mapinfo) -> Self {
let path: Option<String> = if map_info.path.is_null() { let path: Option<String> = if map_info.path.is_null() {

View File

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

View File

@ -1,4 +1,5 @@
/* 1.81.0-nightly */ /* 1.81.0-nightly */
/* qemu git hash: 712661c8200804c0bb0750f237048c6c3da2d863 */
/* automatically generated by rust-bindgen 0.69.4 */ /* automatically generated by rust-bindgen 0.69.4 */
pub const _STDINT_H: u32 = 1; 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 __STDC_ISO_10646__: u32 = 201706;
pub const __GNU_LIBRARY__: u32 = 6; pub const __GNU_LIBRARY__: u32 = 6;
pub const __GLIBC__: u32 = 2; 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 _SYS_CDEFS_H: u32 = 1;
pub const __glibc_c99_flexarr_available: u32 = 1; pub const __glibc_c99_flexarr_available: u32 = 1;
pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0; 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_WCHAR_H: u32 = 1;
pub const _BITS_STDINT_INTN_H: u32 = 1; pub const _BITS_STDINT_INTN_H: u32 = 1;
pub const _BITS_STDINT_UINTN_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 INT8_MIN: i32 = -128;
pub const INT16_MIN: i32 = -32768; pub const INT16_MIN: i32 = -32768;
pub const INT32_MIN: i32 = -2147483648; pub const INT32_MIN: i32 = -2147483648;

View File

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

View File

@ -16,20 +16,21 @@ use libafl_bolts::AsSlice;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
use crate::QemuInstrumentationPagingFilter; use crate::modules::QemuInstrumentationPagingFilter;
use crate::{ use crate::{
command::parser::{ command::parser::{
EndCommandParser, InputPhysCommandParser, InputVirtCommandParser, LoadCommandParser, EndCommandParser, InputPhysCommandParser, InputVirtCommandParser, LoadCommandParser,
NativeCommandParser, SaveCommandParser, StartPhysCommandParser, StartVirtCommandParser, NativeCommandParser, SaveCommandParser, StartPhysCommandParser, StartVirtCommandParser,
VaddrFilterAllowRangeCommandParser, VersionCommandParser, VaddrFilterAllowRangeCommandParser, VersionCommandParser,
}, },
executor::QemuExecutorState,
get_exit_arch_regs, get_exit_arch_regs,
modules::{
EmulatorModuleTuple, HasInstrumentationFilter, IsFilter,
QemuInstrumentationAddressRangeFilter, StdInstrumentationFilter,
},
sync_exit::ExitArgs, sync_exit::ExitArgs,
Emulator, EmulatorExitHandler, EmulatorMemoryChunk, ExitHandlerError, ExitHandlerResult, Emulator, EmulatorExitHandler, ExitHandlerError, ExitHandlerResult, GuestReg, InputLocation,
GuestReg, HasInstrumentationFilter, InputLocation, IsFilter, IsSnapshotManager, Qemu, IsSnapshotManager, Qemu, QemuMemoryChunk, QemuRWError, Regs, StdEmulatorExitHandler, CPU,
QemuHelperTuple, QemuInstrumentationAddressRangeFilter, QemuRWError, Regs,
StdEmulatorExitHandler, StdInstrumentationFilter, CPU,
}; };
pub mod parser; 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 { macro_rules! define_std_command_manager {
($name:ident, [$($native_command_parser:ident),+]) => { ($name:ident, [$($native_command_parser:ident),+]) => {
pub struct $name<QT, S, SM> pub struct $name<ET, S, SM>
where where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
native_command_parsers: 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 where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -78,7 +79,7 @@ macro_rules! define_std_command_manager {
dyn NativeCommandParser< dyn NativeCommandParser<
Self, Self,
StdEmulatorExitHandler<SM>, StdEmulatorExitHandler<SM>,
QT, ET,
S, S,
>, >,
>),*] >),*]
@ -87,7 +88,7 @@ macro_rules! define_std_command_manager {
let mut parsers: HashMap< let mut parsers: HashMap<
GuestReg, GuestReg,
Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, QT, S>>, Box<dyn NativeCommandParser<Self, StdEmulatorExitHandler<SM>, ET, S>>,
> = HashMap::new(); > = HashMap::new();
for parser in native_parsers { 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 where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
fn parse( fn parse(
&self, &self,
qemu: Qemu, 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 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])?; 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 where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, 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 where
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, 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!( define_std_command_manager!(
StdCommandManager, StdCommandManager,
[ [
@ -167,13 +181,13 @@ define_std_command_manager!(
] ]
); );
pub trait CommandManager<E, QT, S>: Sized pub trait CommandManager<EH, ET, S>: Sized
where where
E: EmulatorExitHandler<QT, S>, EH: EmulatorExitHandler<ET, S>,
QT: QemuHelperTuple<S>, ET: EmulatorModuleTuple<S>,
S: State + HasExecutions, 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)] #[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 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 where
CM: CommandManager<E, QT, S>, CM: CommandManager<EH, ET, S>,
E: EmulatorExitHandler<QT, S>, EH: EmulatorExitHandler<ET, S>,
QT: QemuHelperTuple<S>, ET: EmulatorModuleTuple<S>,
S: State + HasExecutions, 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 /// 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. /// the QEMU VM to run the command.
@ -202,11 +216,10 @@ where
/// - `InnerHandlerResult`: How the high-level handler should behave /// - `InnerHandlerResult`: How the high-level handler should behave
fn run( fn run(
&self, &self,
emu: &Emulator<CM, E, QT, S>, emu: &mut Emulator<CM, EH, ET, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
input: &S::Input, input: &S::Input,
ret_reg: Option<Regs>, ret_reg: Option<Regs>,
) -> Result<Option<ExitHandlerResult<CM, E, QT, S>>, ExitHandlerError>; ) -> Result<Option<ExitHandlerResult<CM, EH, ET, S>>, ExitHandlerError>;
} }
#[cfg(emulation_mode = "systemmode")] #[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)] #[derive(Debug, Clone)]
pub struct SaveCommand; 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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -244,27 +287,25 @@ where
fn run( fn run(
&self, &self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
#[cfg(emulation_mode = "systemmode")] qemu_executor_state: &mut QemuExecutorState<QT, S>,
#[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState<
QT,
S,
>,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 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 let emu_exit_handler = emu.exit_handler().borrow_mut();
.set_snapshot_id(snapshot_id)
.map_err(|_| ExitHandlerError::MultipleSnapshotDefinition)?; 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")] #[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(); let mut allowed_paging_ids = HashSet::new();
@ -273,7 +314,7 @@ where
let paging_filter = let paging_filter =
HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut( HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut(
qemu_helpers, emulator_modules,
); );
*paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids); *paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids);
@ -286,11 +327,11 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LoadCommand; 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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -300,11 +341,10 @@ where
fn run( fn run(
&self, &self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 qemu = emu.qemu();
let emu_exit_handler = emu.exit_handler().borrow_mut(); let emu_exit_handler = emu.exit_handler().borrow_mut();
@ -328,15 +368,15 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InputCommand { pub struct InputCommand {
location: EmulatorMemoryChunk, location: QemuMemoryChunk,
cpu: CPU, 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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -346,11 +386,10 @@ where
fn run( fn run(
&self, &self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
input: &S::Input, input: &S::Input,
ret_reg: Option<Regs>, 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 qemu = emu.qemu();
@ -366,14 +405,14 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct StartCommand { 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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -383,11 +422,10 @@ where
fn run( fn run(
&self, &self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
input: &S::Input, input: &S::Input,
ret_reg: Option<Regs>, 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 emu_exit_handler = emu.exit_handler().borrow_mut();
let qemu = emu.qemu(); let qemu = emu.qemu();
@ -420,11 +458,11 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EndCommand(Option<ExitKind>); 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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -434,11 +472,10 @@ where
fn run( fn run(
&self, &self,
emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 emu_exit_handler = emu.exit_handler().borrow_mut();
@ -462,11 +499,11 @@ where
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct VersionCommand(u64); 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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -476,11 +513,10 @@ where
fn run( fn run(
&self, &self,
_emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, _emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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; let guest_version = self.0;
@ -503,11 +539,11 @@ where
} }
#[cfg(emulation_mode = "systemmode")] #[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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -517,16 +553,15 @@ where
fn run( fn run(
&self, &self,
_emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 = let paging_filter =
HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut(qemu_helpers); HasInstrumentationFilter::<QemuInstrumentationPagingFilter>::filter_mut(qemu_modules);
*paging_filter = self.filter.clone(); *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 where
CM: CommandManager<StdEmulatorExitHandler<SM>, QT, S>, CM: CommandManager<StdEmulatorExitHandler<SM>, ET, S>,
QT: QemuHelperTuple<S> + StdInstrumentationFilter + Debug, ET: EmulatorModuleTuple<S> + StdInstrumentationFilter + Debug,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
S::Input: HasTargetBytes, S::Input: HasTargetBytes,
SM: IsSnapshotManager, SM: IsSnapshotManager,
{ {
@ -549,17 +584,16 @@ where
#[allow(clippy::type_complexity)] // TODO: refactor with correct type. #[allow(clippy::type_complexity)] // TODO: refactor with correct type.
fn run( fn run(
&self, &self,
_emu: &Emulator<CM, StdEmulatorExitHandler<SM>, QT, S>, _emu: &mut Emulator<CM, StdEmulatorExitHandler<SM>, ET, S>,
qemu_executor_state: &mut QemuExecutorState<QT, S>,
_input: &S::Input, _input: &S::Input,
_ret_reg: Option<Regs>, _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 = let addr_range_filter =
HasInstrumentationFilter::<QemuInstrumentationAddressRangeFilter>::filter_mut( HasInstrumentationFilter::<QemuInstrumentationAddressRangeFilter>::filter_mut(
qemu_helpers, qemu_modules,
); );
*addr_range_filter = self.filter.clone(); *addr_range_filter = self.filter.clone();
@ -639,7 +673,7 @@ impl Display for PagingFilterCommand {
impl StartCommand { impl StartCommand {
#[must_use] #[must_use]
pub fn new(input_location: EmulatorMemoryChunk) -> Self { pub fn new(input_location: QemuMemoryChunk) -> Self {
Self { input_location } Self { input_location }
} }
} }
@ -653,7 +687,7 @@ impl EndCommand {
impl InputCommand { impl InputCommand {
#[must_use] #[must_use]
pub fn new(location: EmulatorMemoryChunk, cpu: CPU) -> Self { pub fn new(location: QemuMemoryChunk, cpu: CPU) -> Self {
Self { location, cpu } Self { location, cpu }
} }
} }

View File

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

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -6,138 +6,193 @@ use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType}; use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr}; 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 mod edges;
pub use edges::QemuEdgeCoverageHelper; pub use edges::EdgeCoverageModule;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub mod calls; pub mod calls;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub use calls::QemuCallTracerHelper; pub use calls::CallTracerModule;
#[cfg(not(cpu_target = "hexagon"))]
pub mod drcov;
#[cfg(not(cpu_target = "hexagon"))]
pub use drcov::QemuDrCovHelper;
#[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))] #[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))]
pub mod cmplog; pub mod cmplog;
#[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))] #[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))]
pub use cmplog::QemuCmpLogHelper; pub use cmplog::CmpLogModule;
#[cfg(all(emulation_mode = "usermode", feature = "injections"))] use crate::emu::EmulatorModules;
pub mod injections;
#[cfg(all(emulation_mode = "usermode", feature = "injections"))]
pub use injections::QemuInjectionHelper;
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))] /// A module for `libafl_qemu`.
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`.
// TODO remove 'static when specialization will be stable // TODO remove 'static when specialization will be stable
pub trait QemuHelper<S>: 'static + Debug pub trait EmulatorModule<S>: 'static + Debug
where where
S: UsesInput, S: Unpin + UsesInput,
{ {
const HOOKS_DO_SIDE_EFFECTS: bool = true; 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 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 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, &mut self,
_qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input, _input: &S::Input,
_observers: &mut OT, _observers: &mut OT,
_exit_kind: &mut ExitKind, _exit_kind: &mut ExitKind,
) where ) where
OT: ObserversTuple<S>, 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 where
S: UsesInput, S: Unpin + UsesInput,
{ {
const HOOKS_DO_SIDE_EFFECTS: bool; 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 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 where
QT: QemuHelperTuple<S>; ET: EmulatorModuleTuple<S>;
fn pre_exec_all(&mut self, _qemu: Qemu, input: &S::Input); fn pre_exec_all<ET>(
fn post_exec_all<OT>(
&mut self, &mut self,
_qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, S>,
input: &S::Input, _input: &S::Input,
_observers: &mut OT,
_exit_kind: &mut ExitKind,
) where ) where
OT: ObserversTuple<S>; ET: EmulatorModuleTuple<S>;
}
impl<S> QemuHelperTuple<S> for () fn post_exec_all<OT, ET>(
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>(
&mut self, &mut self,
_qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, S>,
_input: &S::Input, _input: &S::Input,
_observers: &mut OT, _observers: &mut OT,
_exit_kind: &mut ExitKind, _exit_kind: &mut ExitKind,
) where ) where
OT: ObserversTuple<S>, 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 () { 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)] #[derive(Debug, Clone)]
pub enum QemuFilterList<T: IsFilter + Debug + Clone> { pub enum QemuFilterList<T: IsFilter + Debug + Clone> {
AllowList(T), 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> = static mut EMPTY_ADDRESS_FILTER: UnsafeCell<QemuInstrumentationAddressRangeFilter> =
UnsafeCell::new(QemuFilterList::None); UnsafeCell::new(QemuFilterList::None);
static mut EMPTY_PAGING_FILTER: UnsafeCell<QemuInstrumentationPagingFilter> = 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 { pub trait IsFilter: Debug {
type FilterParameter; type FilterParameter;
@ -344,12 +324,6 @@ impl IsFilter for () {
pub trait IsAddressFilter: IsFilter<FilterParameter = GuestAddr> {} 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 {} impl IsAddressFilter for QemuInstrumentationAddressRangeFilter {}
#[must_use] #[must_use]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,6 @@ use std::{
ops::Range, ops::Range,
pin::Pin, pin::Pin,
ptr, ptr,
ptr::{addr_of, null},
}; };
use libafl_bolts::os::unix_signals::Signal; 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_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_num_cpus, libafl_qemu_num_regs, libafl_qemu_read_reg,
libafl_qemu_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint, libafl_qemu_remove_breakpoint, libafl_qemu_set_breakpoint, libafl_qemu_trigger_breakpoint,
libafl_qemu_write_reg, CPUArchState, CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, libafl_qemu_write_reg, CPUArchState, CPUStatePtr, FatPtr, GuestAddr, GuestPhysAddr, GuestUsize,
GuestPhysAddr, GuestUsize, GuestVirtAddr, TCGTemp, GuestVirtAddr,
}; };
use num_traits::Num; use num_traits::Num;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::{GuestAddrKind, GuestReg, Regs}; use crate::{GuestAddrKind, GuestReg, Regs};
@ -47,49 +44,11 @@ mod systemmode;
#[allow(unused_imports)] #[allow(unused_imports)]
pub use systemmode::*; pub use systemmode::*;
pub const SKIP_EXEC_HOOK: u64 = u64::MAX; mod hooks;
pub use hooks::*;
static mut QEMU_IS_INITIALIZED: bool = false; 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)] #[derive(Debug)]
pub enum QemuInitError { pub enum QemuInitError {
MultipleInstances, MultipleInstances,
@ -187,17 +146,8 @@ pub struct Qemu {
_private: (), _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)] #[derive(Debug, Clone)]
pub struct EmulatorMemoryChunk { pub struct QemuMemoryChunk {
addr: GuestAddrKind, addr: GuestAddrKind,
size: GuestReg, size: GuestReg,
cpu: Option<CPU>, cpu: Option<CPU>,
@ -363,11 +313,6 @@ pub trait ArchExtras {
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
impl CPU { impl CPU {
#[must_use]
pub fn qemu(&self) -> Qemu {
unsafe { Qemu::get_unchecked() }
}
#[must_use] #[must_use]
#[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_sign_loss)]
pub fn index(&self) -> usize { pub fn index(&self) -> usize {
@ -421,7 +366,7 @@ impl CPU {
let val = GuestReg::to_le(val.into()); let val = GuestReg::to_le(val.into());
let success = 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 { if success == 0 {
Err(QemuRWError { Err(QemuRWError {
kind: QemuRWErrorKind::Write, kind: QemuRWErrorKind::Write,
@ -606,7 +551,7 @@ impl Qemu {
.map(|(k, v)| format!("{}={}\0", &k, &v)) .map(|(k, v)| format!("{}={}\0", &k, &v))
.collect(); .collect();
let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).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 { unsafe {
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
qemu_user_init(argc, argv.as_ptr(), envp.as_ptr()); qemu_user_init(argc, argv.as_ptr(), envp.as_ptr());
@ -621,8 +566,14 @@ impl Qemu {
Ok(Qemu { _private: () }) Ok(Qemu { _private: () })
} }
#[must_use]
pub fn hooks(&self) -> QemuHooks {
unsafe { QemuHooks::get_unchecked() }
}
/// Get a QEMU object. /// Get a QEMU object.
/// Same as `Qemu::get`, but without checking whether QEMU has been correctly initialized. /// Same as `Qemu::get`, but without checking whether QEMU has been correctly initialized.
/// Since Qemu is a ZST, this operation is free.
/// ///
/// # Safety /// # 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() }; let exit_reason = unsafe { libafl_get_exit_reason() };
if exit_reason.is_null() { if exit_reason.is_null() {
Err(QemuExitError::UnexpectedExit) 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] #[must_use]
pub fn remove_hook(&self, id: impl HookId, invalidate_block: bool) -> bool { pub fn remove_hook(&self, id: impl HookId, invalidate_block: bool) -> bool {
id.remove(invalidate_block) 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)] #[allow(clippy::type_complexity)]
pub fn add_gdb_cmd(&self, callback: Box<dyn FnMut(&Self, &str) -> bool>) { pub fn add_gdb_cmd(&self, callback: Box<dyn FnMut(&Self, &str) -> bool>) {
unsafe { unsafe {
@ -1095,7 +872,7 @@ impl PartialOrd for GuestAddrKind {
} }
} }
impl EmulatorMemoryChunk { impl QemuMemoryChunk {
#[must_use] #[must_use]
pub fn addr(&self) -> GuestAddrKind { pub fn addr(&self) -> GuestAddrKind {
self.addr self.addr
@ -1125,7 +902,7 @@ impl EmulatorMemoryChunk {
} }
#[must_use] #[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 new_addr = self.addr + range.start;
let slice_size = range.clone().count(); let slice_size = range.clone().count();
@ -1142,7 +919,7 @@ impl EmulatorMemoryChunk {
/// Returns the number of bytes effectively written. /// Returns the number of bytes effectively written.
#[must_use] #[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 max_len: usize = self.size.try_into().unwrap();
let input_sliced = if input.len() > max_len { 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")] #[cfg(feature = "python")]
pub mod pybind { pub mod pybind {
use pyo3::{exceptions::PyValueError, prelude::*}; use pyo3::{exceptions::PyValueError, prelude::*};
@ -1298,8 +1039,12 @@ pub mod pybind {
unsafe { unsafe {
let idx = PY_GENERIC_HOOKS.len(); let idx = PY_GENERIC_HOOKS.len();
PY_GENERIC_HOOKS.push((addr, hook)); PY_GENERIC_HOOKS.push((addr, hook));
self.qemu self.qemu.hooks().add_instruction_hooks(
.set_hook(idx as u64, addr, py_generic_hook_wrapper, true); idx as u64,
addr,
py_generic_hook_wrapper,
true,
);
} }
} }
@ -1307,7 +1052,7 @@ pub mod pybind {
unsafe { unsafe {
PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr); PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr);
} }
self.qemu.remove_hooks_at(addr, true) self.qemu.hooks().remove_instruction_hooks_at(addr, true)
} }
} }
} }

View File

@ -15,8 +15,8 @@ use libafl_qemu_sys::{
use num_traits::Zero; use num_traits::Zero;
use crate::{ use crate::{
EmulatorMemoryChunk, FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuExitError, FastSnapshotPtr, GuestAddrKind, MemAccessInfo, Qemu, QemuMemoryChunk, QemuSnapshotCheckResult,
QemuExitReason, QemuSnapshotCheckResult, CPU, CPU,
}; };
pub(super) extern "C" fn qemu_cleanup_atexit() { 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. pub(super) unsafe fn run_inner(&self) {
/// 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> {
vm_start(); vm_start();
qemu_main_loop(); qemu_main_loop();
self.post_run()
} }
pub fn save_snapshot(&self, name: &str, sync: bool) { 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 { pub fn phys_iter(&self, qemu: Qemu) -> PhysMemoryIter {
PhysMemoryIter { PhysMemoryIter {
addr: self.addr, addr: self.addr,

View File

@ -4,19 +4,16 @@ use std::{
}; };
use libafl_qemu_sys::{ use libafl_qemu_sys::{
exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk, exec_path, free_self_maps, guest_base, libafl_force_dfl, libafl_get_brk, libafl_load_addr,
libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk, libafl_maps_first, libafl_maps_next, libafl_qemu_run, libafl_set_brk, mmap_next_start,
mmap_next_start, pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize, pageflags_get_root, read_self_maps, strlen, GuestAddr, GuestUsize, IntervalTreeNode,
IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
}; };
use libc::c_int; use libc::c_int;
#[cfg(feature = "python")] #[cfg(feature = "python")]
use pyo3::{pyclass, pymethods, IntoPy, PyObject, PyRef, PyRefMut, Python}; use pyo3::{pyclass, pymethods, IntoPy, PyObject, PyRef, PyRefMut, Python};
use crate::{ use crate::{Qemu, CPU};
HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu, QemuExitError,
QemuExitReason, SyscallHookResult, CPU,
};
#[cfg_attr(feature = "python", pyclass(unsendable))] #[cfg_attr(feature = "python", pyclass(unsendable))]
pub struct GuestMaps { pub struct GuestMaps {
@ -135,15 +132,8 @@ impl Qemu {
} }
} }
/// This function will run the emulator until the next breakpoint, or until finish. pub(super) unsafe fn run_inner(self) {
/// # 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> {
libafl_qemu_run(); libafl_qemu_run();
self.post_run()
} }
#[must_use] #[must_use]
@ -237,100 +227,6 @@ impl Qemu {
Err(format!("Failed to unmap {addr}")) 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")] #[cfg(feature = "python")]
@ -340,7 +236,7 @@ pub mod pybind {
exceptions::PyValueError, pymethods, types::PyInt, FromPyObject, PyObject, PyResult, Python, 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; static mut PY_SYSCALL_HOOK: Option<PyObject> = None;
@ -438,6 +334,7 @@ pub mod pybind {
PY_SYSCALL_HOOK = Some(hook); PY_SYSCALL_HOOK = Some(hook);
} }
self.qemu self.qemu
.hooks()
.add_pre_syscall_hook(0u64, py_syscall_hook_wrapper); .add_pre_syscall_hook(0u64, py_syscall_hook_wrapper);
} }
} }

View File

@ -8,7 +8,9 @@ use libafl::state::{HasExecutions, State};
use crate::{ use crate::{
command::{CommandManager, IsCommand}, 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)] #[derive(Debug, Clone, Enum)]
@ -24,30 +26,30 @@ pub enum ExitArgs {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SyncExit<CM, E, QT, S> pub struct SyncExit<CM, EH, ET, S>
where where
CM: CommandManager<E, QT, S>, CM: CommandManager<EH, ET, S>,
E: EmulatorExitHandler<QT, S>, EH: EmulatorExitHandler<ET, S>,
QT: QemuHelperTuple<S>, ET: EmulatorModuleTuple<S>,
S: State + HasExecutions, 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 where
CM: CommandManager<E, QT, S>, CM: CommandManager<EH, ET, S>,
E: EmulatorExitHandler<QT, S>, EH: EmulatorExitHandler<ET, S>,
QT: QemuHelperTuple<S>, ET: EmulatorModuleTuple<S>,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
{ {
#[must_use] #[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 } Self { command }
} }
#[must_use] #[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() 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 where
CM: CommandManager<E, QT, S>, CM: CommandManager<EH, ET, S>,
E: EmulatorExitHandler<QT, S>, EH: EmulatorExitHandler<ET, S>,
QT: QemuHelperTuple<S>, ET: EmulatorModuleTuple<S>,
S: State + HasExecutions, S: Unpin + State + HasExecutions,
{ {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.command) write!(f, "{}", self.command)

View File

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

View File

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