Refactor of Qemu configuration (#2707)
* Qemu config refactoring. * QEMU error refactoring. * Single QEMU init function. * Light refactor of EmulatorModules. * Qemu is now a parameter to EmulatorModule callbacks and most function hooks. * EmulatorModules is initialized before QEMU is initialized. * refactor asan and asanguest modules to avoid custom init of QEMU and use the module interface instead. * asan fixed size accesses working with generics. * use pre_syscall_* and post_syscall_* everywhere for consistency. * adapt qemu_launcher example to fully work with Emulator, since Qemu must now be initialized by Emulator. * start writing Emulator / EmulatorBuilder / QemuConfig doc. * fix broken intel pt doc.
This commit is contained in:
parent
5a3cbc18a7
commit
7c8708d4b1
@ -176,7 +176,7 @@ fn fuzz(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let emulator = Emulator::empty()
|
let emulator = Emulator::empty()
|
||||||
.qemu_cli(args)
|
.qemu_parameters(args)
|
||||||
.modules(emulator_modules)
|
.modules(emulator_modules)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ use libafl_qemu::{
|
|||||||
GuestReg,
|
GuestReg,
|
||||||
//snapshot::QemuSnapshotHelper,
|
//snapshot::QemuSnapshotHelper,
|
||||||
MmapPerms,
|
MmapPerms,
|
||||||
Qemu,
|
|
||||||
QemuExecutor,
|
QemuExecutor,
|
||||||
QemuExitError,
|
QemuExitError,
|
||||||
QemuExitReason,
|
QemuExitReason,
|
||||||
@ -175,8 +174,33 @@ fn fuzz(
|
|||||||
env::remove_var("LD_LIBRARY_PATH");
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let qemu = Qemu::init(&args).expect("QEMU init failed");
|
|
||||||
// let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap();
|
// Create an observation channel using the coverage map
|
||||||
|
let mut edges_observer = unsafe {
|
||||||
|
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
|
||||||
|
"edges",
|
||||||
|
OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE),
|
||||||
|
&raw mut MAX_EDGES_FOUND,
|
||||||
|
))
|
||||||
|
.track_indices()
|
||||||
|
};
|
||||||
|
|
||||||
|
let modules = tuple_list!(
|
||||||
|
StdEdgeCoverageModule::builder()
|
||||||
|
.map_observer(edges_observer.as_mut())
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
CmpLogModule::default(),
|
||||||
|
// QemuAsanHelper::default(asan),
|
||||||
|
//QemuSnapshotHelper::new()
|
||||||
|
);
|
||||||
|
|
||||||
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(args)
|
||||||
|
.modules(modules)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
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)?;
|
||||||
@ -255,16 +279,6 @@ fn fuzz(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
|
||||||
let mut edges_observer = unsafe {
|
|
||||||
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
|
|
||||||
"edges",
|
|
||||||
OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_ALLOCATED_SIZE),
|
|
||||||
&raw mut MAX_EDGES_FOUND,
|
|
||||||
))
|
|
||||||
.track_indices()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
@ -364,18 +378,6 @@ fn fuzz(
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let modules = tuple_list!(
|
|
||||||
StdEdgeCoverageModule::builder()
|
|
||||||
.map_observer(edges_observer.as_mut())
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
CmpLogModule::default(),
|
|
||||||
// QemuAsanHelper::default(asan),
|
|
||||||
//QemuSnapshotHelper::new()
|
|
||||||
);
|
|
||||||
|
|
||||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
|
||||||
|
|
||||||
// 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(
|
||||||
emulator,
|
emulator,
|
||||||
|
@ -28,8 +28,8 @@ use libafl_bolts::{
|
|||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention,
|
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, CallingConvention,
|
||||||
Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitError, QemuExitReason,
|
Emulator, GuestAddr, GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuForkExecutor,
|
||||||
QemuForkExecutor, QemuShutdownCause, Regs,
|
QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR};
|
use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR};
|
||||||
|
|
||||||
@ -113,7 +113,31 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
log::debug!("ARGS: {:#?}", options.args);
|
log::debug!("ARGS: {:#?}", options.args);
|
||||||
|
|
||||||
env::remove_var("LD_LIBRARY_PATH");
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
let qemu = Qemu::init(&options.args).unwrap();
|
|
||||||
|
let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap();
|
||||||
|
let edges = edges_shmem.as_slice_mut();
|
||||||
|
unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() };
|
||||||
|
|
||||||
|
let mut edges_observer = unsafe {
|
||||||
|
HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr(
|
||||||
|
"edges",
|
||||||
|
NonNull::new(edges.as_mut_ptr())
|
||||||
|
.expect("The edge map pointer is null.")
|
||||||
|
.cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
|
||||||
|
.const_map_observer(edges_observer.as_mut())
|
||||||
|
.build()?);
|
||||||
|
|
||||||
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(options.args)
|
||||||
|
.modules(modules)
|
||||||
|
.build()?;
|
||||||
|
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).unwrap();
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
@ -139,8 +163,6 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
|
|
||||||
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
|
|
||||||
let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
|
||||||
|
|
||||||
let monitor = SimpleMonitor::with_user_monitor(|s| {
|
let monitor = SimpleMonitor::with_user_monitor(|s| {
|
||||||
println!("{s}");
|
println!("{s}");
|
||||||
});
|
});
|
||||||
@ -157,19 +179,6 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap();
|
|
||||||
let edges = edges_shmem.as_slice_mut();
|
|
||||||
unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() };
|
|
||||||
|
|
||||||
let mut edges_observer = unsafe {
|
|
||||||
HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr(
|
|
||||||
"edges",
|
|
||||||
NonNull::new(edges.as_mut_ptr())
|
|
||||||
.expect("The edge map pointer is null.")
|
|
||||||
.cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(),
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut feedback = MaxMapFeedback::new(&edges_observer);
|
let mut feedback = MaxMapFeedback::new(&edges_observer);
|
||||||
|
|
||||||
let mut objective = ();
|
let mut objective = ();
|
||||||
@ -222,12 +231,6 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
|
|
||||||
.const_map_observer(edges_observer.as_mut())
|
|
||||||
.build()?);
|
|
||||||
|
|
||||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
|
||||||
|
|
||||||
let mut executor = QemuForkExecutor::new(
|
let mut executor = QemuForkExecutor::new(
|
||||||
emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
|
@ -31,7 +31,7 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
modules::{drcov::DrCovModule, StdAddressFilter},
|
modules::{drcov::DrCovModule, StdAddressFilter},
|
||||||
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExecutor,
|
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor,
|
||||||
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,80 +123,93 @@ pub fn fuzz() {
|
|||||||
|
|
||||||
env::remove_var("LD_LIBRARY_PATH");
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
|
|
||||||
let qemu = Qemu::init(&options.args).unwrap();
|
|
||||||
|
|
||||||
let mut elf_buffer = Vec::new();
|
|
||||||
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
|
||||||
|
|
||||||
let test_one_input_ptr = elf
|
|
||||||
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
|
||||||
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
|
||||||
log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
|
||||||
|
|
||||||
qemu.entry_break(test_one_input_ptr);
|
|
||||||
|
|
||||||
for m in qemu.mappings() {
|
|
||||||
log::debug!(
|
|
||||||
"Mapping: 0x{:016x}-0x{:016x}, {}",
|
|
||||||
m.start(),
|
|
||||||
m.end(),
|
|
||||||
m.path().unwrap_or(&"<EMPTY>".to_string())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap();
|
|
||||||
log::debug!("Break at {pc:#x}");
|
|
||||||
|
|
||||||
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
|
||||||
log::debug!("Return address = {ret_addr:#x}");
|
|
||||||
|
|
||||||
qemu.set_breakpoint(ret_addr);
|
|
||||||
|
|
||||||
let input_addr = qemu
|
|
||||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
|
||||||
.unwrap();
|
|
||||||
log::debug!("Placing input at {input_addr:#x}");
|
|
||||||
|
|
||||||
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
|
||||||
|
|
||||||
let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> {
|
|
||||||
unsafe {
|
|
||||||
let _ = qemu.write_mem(input_addr, buf);
|
|
||||||
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;
|
|
||||||
qemu.write_reg(Regs::Sp, stack_ptr)?;
|
|
||||||
qemu.write_return_address(ret_addr)?;
|
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?;
|
|
||||||
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?;
|
|
||||||
|
|
||||||
match qemu.run() {
|
|
||||||
Ok(QemuExitReason::Breakpoint(_)) => {}
|
|
||||||
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
|
||||||
process::exit(0)
|
|
||||||
}
|
|
||||||
_ => panic!("Unexpected QEMU exit."),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut harness =
|
|
||||||
|_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let mut buf = target.as_slice();
|
|
||||||
let mut len = buf.len();
|
|
||||||
if len > MAX_INPUT_SIZE {
|
|
||||||
buf = &buf[0..MAX_INPUT_SIZE];
|
|
||||||
len = MAX_INPUT_SIZE;
|
|
||||||
}
|
|
||||||
let len = len as GuestReg;
|
|
||||||
reset(buf, len).unwrap();
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut run_client = |state: Option<_>,
|
let mut run_client = |state: Option<_>,
|
||||||
mut mgr: LlmpRestartingEventManager<_, _, _>,
|
mut mgr: LlmpRestartingEventManager<_, _, _>,
|
||||||
client_description: ClientDescription| {
|
client_description: ClientDescription| {
|
||||||
|
let mut cov_path = options.coverage_path.clone();
|
||||||
|
|
||||||
|
let emulator_modules = tuple_list!(DrCovModule::builder()
|
||||||
|
.filter(StdAddressFilter::default())
|
||||||
|
.filename(cov_path.clone())
|
||||||
|
.full_trace(false)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(options.args.clone())
|
||||||
|
.modules(emulator_modules)
|
||||||
|
.build()
|
||||||
|
.expect("QEMU initialization failed");
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
|
|
||||||
|
let test_one_input_ptr = elf
|
||||||
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
|
log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
|
qemu.entry_break(test_one_input_ptr);
|
||||||
|
|
||||||
|
for m in qemu.mappings() {
|
||||||
|
log::debug!(
|
||||||
|
"Mapping: 0x{:016x}-0x{:016x}, {}",
|
||||||
|
m.start(),
|
||||||
|
m.end(),
|
||||||
|
m.path().unwrap_or(&"<EMPTY>".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
|
log::debug!("Break at {pc:#x}");
|
||||||
|
|
||||||
|
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
||||||
|
log::debug!("Return address = {ret_addr:#x}");
|
||||||
|
|
||||||
|
qemu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
|
let input_addr = qemu
|
||||||
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
|
.unwrap();
|
||||||
|
log::debug!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
|
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
|
|
||||||
|
let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> {
|
||||||
|
unsafe {
|
||||||
|
let _ = qemu.write_mem(input_addr, buf);
|
||||||
|
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;
|
||||||
|
qemu.write_reg(Regs::Sp, stack_ptr)?;
|
||||||
|
qemu.write_return_address(ret_addr)?;
|
||||||
|
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?;
|
||||||
|
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?;
|
||||||
|
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
||||||
|
Signal::SigInterrupt,
|
||||||
|
))) => process::exit(0),
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut harness =
|
||||||
|
|_emulator: &mut Emulator<_, _, _, _, _>, _state: &mut _, input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let mut buf = target.as_slice();
|
||||||
|
let mut len = buf.len();
|
||||||
|
if len > MAX_INPUT_SIZE {
|
||||||
|
buf = &buf[0..MAX_INPUT_SIZE];
|
||||||
|
len = MAX_INPUT_SIZE;
|
||||||
|
}
|
||||||
|
let len = len as GuestReg;
|
||||||
|
reset(buf, len).unwrap();
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
let core_id = client_description.core_id();
|
let core_id = client_description.core_id();
|
||||||
let core_idx = options
|
let core_idx = options
|
||||||
.cores
|
.cores
|
||||||
@ -232,23 +245,11 @@ 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 mut cov_path = options.coverage_path.clone();
|
|
||||||
let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap();
|
let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap();
|
||||||
let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap();
|
let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap();
|
||||||
let core = core_id.0;
|
let core = core_id.0;
|
||||||
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
|
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
|
||||||
|
|
||||||
let emulator_modules = tuple_list!(DrCovModule::builder()
|
|
||||||
.filter(StdAddressFilter::default())
|
|
||||||
.filename(cov_path)
|
|
||||||
.full_trace(false)
|
|
||||||
.build());
|
|
||||||
|
|
||||||
let emulator = Emulator::empty()
|
|
||||||
.qemu(qemu)
|
|
||||||
.modules(emulator_modules)
|
|
||||||
.build()?;
|
|
||||||
|
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
emulator,
|
emulator,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
|
@ -11,14 +11,8 @@ use libafl::{
|
|||||||
use libafl_bolts::{rands::StdRand, tuples::tuple_list};
|
use libafl_bolts::{rands::StdRand, tuples::tuple_list};
|
||||||
#[cfg(feature = "injections")]
|
#[cfg(feature = "injections")]
|
||||||
use libafl_qemu::modules::injections::InjectionModule;
|
use libafl_qemu::modules::injections::InjectionModule;
|
||||||
use libafl_qemu::{
|
use libafl_qemu::modules::{
|
||||||
modules::{
|
asan::AsanModule, asan_guest::AsanGuestModule, cmplog::CmpLogModule, DrCovModule,
|
||||||
asan::{init_qemu_with_asan, AsanModule},
|
|
||||||
asan_guest::{init_qemu_with_asan_guest, AsanGuestModule},
|
|
||||||
cmplog::CmpLogModule,
|
|
||||||
DrCovModule,
|
|
||||||
},
|
|
||||||
Qemu,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -80,18 +74,6 @@ impl Client<'_> {
|
|||||||
Err(Error::empty_optional("Multiple ASAN modes configured"))?;
|
Err(Error::empty_optional("Multiple ASAN modes configured"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (qemu, mut asan, mut asan_lib) = {
|
|
||||||
if is_asan {
|
|
||||||
let (emu, asan) = init_qemu_with_asan(&mut args, &mut env)?;
|
|
||||||
(emu, Some(asan), None)
|
|
||||||
} else if is_asan_guest {
|
|
||||||
let (emu, asan_lib) = init_qemu_with_asan_guest(&mut args, &mut env)?;
|
|
||||||
(emu, None, Some(asan_lib))
|
|
||||||
} else {
|
|
||||||
(Qemu::init(&args)?, None, None)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "injections"))]
|
#[cfg(not(feature = "injections"))]
|
||||||
let injection_module = None;
|
let injection_module = None;
|
||||||
|
|
||||||
@ -111,8 +93,6 @@ impl Client<'_> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let harness = Harness::init(qemu).expect("Error setting up harness.");
|
|
||||||
|
|
||||||
let is_cmplog = self.options.is_cmplog_core(core_id);
|
let is_cmplog = self.options.is_cmplog_core(core_id);
|
||||||
|
|
||||||
let extra_tokens = injection_module
|
let extra_tokens = injection_module
|
||||||
@ -122,8 +102,6 @@ impl Client<'_> {
|
|||||||
|
|
||||||
let instance_builder = Instance::builder()
|
let instance_builder = Instance::builder()
|
||||||
.options(self.options)
|
.options(self.options)
|
||||||
.qemu(qemu)
|
|
||||||
.harness(harness)
|
|
||||||
.mgr(mgr)
|
.mgr(mgr)
|
||||||
.client_description(client_description)
|
.client_description(client_description)
|
||||||
.extra_tokens(extra_tokens);
|
.extra_tokens(extra_tokens);
|
||||||
@ -136,77 +114,79 @@ impl Client<'_> {
|
|||||||
.filename(drcov.clone())
|
.filename(drcov.clone())
|
||||||
.full_trace(true)
|
.full_trace(true)
|
||||||
.build();
|
.build();
|
||||||
instance_builder.build().run(tuple_list!(drcov), state)
|
instance_builder
|
||||||
|
.build()
|
||||||
|
.run(args, tuple_list!(drcov), state)
|
||||||
} else if is_asan && is_cmplog {
|
} else if is_asan && is_cmplog {
|
||||||
if let Some(injection_module) = injection_module {
|
if let Some(injection_module) = injection_module {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
|
args,
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
CmpLogModule::default(),
|
CmpLogModule::default(),
|
||||||
AsanModule::default(asan.take().unwrap()),
|
AsanModule::default(&env),
|
||||||
injection_module,
|
injection_module,
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
tuple_list!(
|
args,
|
||||||
CmpLogModule::default(),
|
tuple_list!(CmpLogModule::default(), AsanModule::default(&env),),
|
||||||
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_module) = injection_module {
|
if let Some(injection_module) = injection_module {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
|
args,
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
CmpLogModule::default(),
|
CmpLogModule::default(),
|
||||||
AsanGuestModule::default(qemu, &asan_lib.take().unwrap()),
|
AsanGuestModule::default(&env),
|
||||||
injection_module
|
injection_module
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
tuple_list!(
|
args,
|
||||||
CmpLogModule::default(),
|
tuple_list!(CmpLogModule::default(), AsanGuestModule::default(&env),),
|
||||||
AsanGuestModule::default(qemu, &asan_lib.take().unwrap()),
|
|
||||||
),
|
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if is_asan {
|
} else if is_asan {
|
||||||
if let Some(injection_module) = injection_module {
|
if let Some(injection_module) = injection_module {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
tuple_list!(AsanModule::default(asan.take().unwrap()), injection_module),
|
args,
|
||||||
|
tuple_list!(AsanModule::default(&env), injection_module),
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
instance_builder.build().run(
|
instance_builder
|
||||||
tuple_list!(AsanModule::default(asan.take().unwrap()),),
|
.build()
|
||||||
state,
|
.run(args, tuple_list!(AsanModule::default(&env),), state)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else if is_asan_guest {
|
} else if is_asan_guest {
|
||||||
let modules = tuple_list!(AsanGuestModule::default(qemu, &asan_lib.take().unwrap()));
|
instance_builder
|
||||||
instance_builder.build().run(modules, state)
|
.build()
|
||||||
|
.run(args, tuple_list!(AsanGuestModule::default(&env)), state)
|
||||||
} else if is_cmplog {
|
} else if is_cmplog {
|
||||||
if let Some(injection_module) = injection_module {
|
if let Some(injection_module) = injection_module {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
|
args,
|
||||||
tuple_list!(CmpLogModule::default(), injection_module),
|
tuple_list!(CmpLogModule::default(), injection_module),
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
instance_builder
|
instance_builder
|
||||||
.build()
|
.build()
|
||||||
.run(tuple_list!(CmpLogModule::default()), state)
|
.run(args, tuple_list!(CmpLogModule::default()), state)
|
||||||
}
|
}
|
||||||
} else if let Some(injection_module) = injection_module {
|
} else if let Some(injection_module) = injection_module {
|
||||||
instance_builder
|
instance_builder
|
||||||
.build()
|
.build()
|
||||||
.run(tuple_list!(injection_module), state)
|
.run(args, tuple_list!(injection_module), state)
|
||||||
} else {
|
} else {
|
||||||
instance_builder.build().run(tuple_list!(), state)
|
instance_builder.build().run(args, tuple_list!(), state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,13 @@ use libafl_bolts::shmem::StdShMemProvider;
|
|||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
ownedref::OwnedMutSlice,
|
ownedref::OwnedMutSlice,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
tuples::{tuple_list, Merge, Prepend},
|
tuples::{tuple_list, MatchFirstType, Merge, Prepend},
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
modules::{
|
modules::{
|
||||||
cmplog::CmpLogObserver, EmulatorModuleTuple, StdAddressFilter, StdEdgeCoverageModule,
|
cmplog::CmpLogObserver, edges::EdgeCoverageFullVariant, EdgeCoverageModule, EmulatorModule,
|
||||||
|
EmulatorModuleTuple, NopPageFilter, StdAddressFilter, StdEdgeCoverageModule,
|
||||||
},
|
},
|
||||||
Emulator, GuestAddr, Qemu, QemuExecutor,
|
Emulator, GuestAddr, Qemu, QemuExecutor,
|
||||||
};
|
};
|
||||||
@ -61,9 +62,6 @@ pub type ClientMgr<M> =
|
|||||||
pub struct Instance<'a, M: Monitor> {
|
pub struct Instance<'a, M: Monitor> {
|
||||||
options: &'a FuzzerOptions,
|
options: &'a FuzzerOptions,
|
||||||
/// The harness. We create it before forking, then `take()` it inside the client.
|
/// The harness. We create it before forking, then `take()` it inside the client.
|
||||||
#[builder(setter(strip_option))]
|
|
||||||
harness: Option<Harness>,
|
|
||||||
qemu: Qemu,
|
|
||||||
mgr: ClientMgr<M>,
|
mgr: ClientMgr<M>,
|
||||||
client_description: ClientDescription,
|
client_description: ClientDescription,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
@ -106,7 +104,12 @@ impl<M: Monitor> Instance<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub fn run<ET>(&mut self, modules: ET, state: Option<ClientState>) -> Result<(), Error>
|
pub fn run<ET>(
|
||||||
|
&mut self,
|
||||||
|
args: Vec<String>,
|
||||||
|
modules: ET,
|
||||||
|
state: Option<ClientState>,
|
||||||
|
) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<ClientState> + Debug,
|
ET: EmulatorModuleTuple<ClientState> + Debug,
|
||||||
{
|
{
|
||||||
@ -122,10 +125,21 @@ impl<M: Monitor> Instance<'_, M> {
|
|||||||
|
|
||||||
let edge_coverage_module = StdEdgeCoverageModule::builder()
|
let edge_coverage_module = StdEdgeCoverageModule::builder()
|
||||||
.map_observer(edges_observer.as_mut())
|
.map_observer(edges_observer.as_mut())
|
||||||
.address_filter(self.coverage_filter(self.qemu)?)
|
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let modules = modules.prepend(edge_coverage_module);
|
let modules = modules.prepend(edge_coverage_module);
|
||||||
|
let mut emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(args)
|
||||||
|
.modules(modules)
|
||||||
|
.build()?;
|
||||||
|
let harness = Harness::init(emulator.qemu()).expect("Error setting up harness.");
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
|
// update address filter after qemu has been initialized
|
||||||
|
<EdgeCoverageModule<StdAddressFilter, NopPageFilter, EdgeCoverageFullVariant, false, 0> as EmulatorModule<ClientState>>::update_address_filter(emulator.modules_mut()
|
||||||
|
.modules_mut()
|
||||||
|
.match_first_type_mut::<EdgeCoverageModule<StdAddressFilter, NopPageFilter, EdgeCoverageFullVariant, false, 0>>()
|
||||||
|
.expect("Could not find back the edge module"), qemu, self.coverage_filter(qemu)?);
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
@ -198,10 +212,6 @@ impl<M: Monitor> Instance<'_, M> {
|
|||||||
|
|
||||||
state.add_metadata(tokens);
|
state.add_metadata(tokens);
|
||||||
|
|
||||||
let harness = self
|
|
||||||
.harness
|
|
||||||
.take()
|
|
||||||
.expect("The harness can never be None here!");
|
|
||||||
harness.post_fork();
|
harness.post_fork();
|
||||||
|
|
||||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>,
|
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>,
|
||||||
@ -211,8 +221,6 @@ impl<M: Monitor> Instance<'_, 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 emulator = Emulator::empty().qemu(self.qemu).modules(modules).build()?;
|
|
||||||
|
|
||||||
if let Some(rerun_input) = &self.options.rerun_input {
|
if let Some(rerun_input) = &self.options.rerun_input {
|
||||||
// TODO: We might want to support non-bytes inputs at some point?
|
// TODO: We might want to support non-bytes inputs at some point?
|
||||||
let bytes = fs::read(rerun_input)
|
let bytes = fs::read(rerun_input)
|
||||||
|
@ -107,14 +107,13 @@ pub fn fuzz() {
|
|||||||
|
|
||||||
// Initialize QEMU Emulator
|
// Initialize QEMU Emulator
|
||||||
let emu = Emulator::builder()
|
let emu = Emulator::builder()
|
||||||
.qemu_cli(args)
|
.qemu_parameters(args)
|
||||||
.add_module(
|
.prepend_module(
|
||||||
StdEdgeCoverageModule::builder()
|
StdEdgeCoverageModule::builder()
|
||||||
.map_observer(edges_observer.as_mut())
|
.map_observer(edges_observer.as_mut())
|
||||||
.build()?,
|
.build()?,
|
||||||
)
|
)
|
||||||
.build()
|
.build()?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Set breakpoints of interest with corresponding commands.
|
// Set breakpoints of interest with corresponding commands.
|
||||||
emu.add_breakpoint(
|
emu.add_breakpoint(
|
||||||
|
@ -29,9 +29,9 @@ use libafl_bolts::{
|
|||||||
AsSlice,
|
AsSlice,
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
config, elf::EasyElf, executor::QemuExecutor, modules::edges::StdEdgeCoverageModuleBuilder,
|
config, config::QemuConfig, elf::EasyElf, executor::QemuExecutor,
|
||||||
Emulator, GuestPhysAddr, Qemu, QemuExitError, QemuExitReason, QemuRWError, QemuShutdownCause,
|
modules::edges::StdEdgeCoverageModuleBuilder, Emulator, GuestPhysAddr, QemuExitError,
|
||||||
Regs,
|
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
|
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
|
||||||
|
|
||||||
@ -93,8 +93,8 @@ pub fn fuzz() {
|
|||||||
.track_indices()
|
.track_indices()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize QEMU
|
// Create QEMU configuration
|
||||||
let qemu = Qemu::builder()
|
let qemu_config = QemuConfig::builder()
|
||||||
.machine("mps2-an385")
|
.machine("mps2-an385")
|
||||||
.monitor(config::Monitor::Null)
|
.monitor(config::Monitor::Null)
|
||||||
.kernel(format!("{target_dir}/example.elf"))
|
.kernel(format!("{target_dir}/example.elf"))
|
||||||
@ -107,18 +107,19 @@ pub fn fuzz() {
|
|||||||
.file(format!("{target_dir}/dummy.qcow2"))
|
.file(format!("{target_dir}/dummy.qcow2"))
|
||||||
.build()])
|
.build()])
|
||||||
.start_cpu(false)
|
.start_cpu(false)
|
||||||
.build()
|
.build();
|
||||||
.expect("Failed to initialized QEMU");
|
|
||||||
|
|
||||||
let emulator_modules = tuple_list!(StdEdgeCoverageModuleBuilder::default()
|
let emulator_modules = tuple_list!(StdEdgeCoverageModuleBuilder::default()
|
||||||
.map_observer(edges_observer.as_mut())
|
.map_observer(edges_observer.as_mut())
|
||||||
.build()?);
|
.build()?);
|
||||||
|
|
||||||
let emulator = Emulator::empty()
|
let emulator = Emulator::empty()
|
||||||
.qemu(qemu)
|
.qemu_parameters(qemu_config)
|
||||||
.modules(emulator_modules)
|
.modules(emulator_modules)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
qemu.set_breakpoint(main_addr);
|
qemu.set_breakpoint(main_addr);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -63,7 +63,7 @@ pub fn fuzz() {
|
|||||||
.build()?);
|
.build()?);
|
||||||
|
|
||||||
let emu = Emulator::builder()
|
let emu = Emulator::builder()
|
||||||
.qemu_cli(args)
|
.qemu_parameters(args)
|
||||||
.modules(modules)
|
.modules(modules)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
@ -67,14 +67,9 @@ pub fn fuzz() {
|
|||||||
CmpLogModule::default(),
|
CmpLogModule::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// let driver = StdEmulatorDriver::builder()
|
|
||||||
// .print_commands(true)
|
|
||||||
// .build();
|
|
||||||
|
|
||||||
let emu = Emulator::builder()
|
let emu = Emulator::builder()
|
||||||
.qemu_cli(args)
|
.qemu_parameters(args)
|
||||||
.modules(modules)
|
.modules(modules)
|
||||||
// .driver(driver)
|
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let devices = emu.list_devices();
|
let devices = emu.list_devices();
|
||||||
|
@ -70,7 +70,7 @@ pub fn fuzz() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let emu = Emulator::builder()
|
let emu = Emulator::builder()
|
||||||
.qemu_cli(args)
|
.qemu_parameters(args)
|
||||||
.modules(modules)
|
.modules(modules)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use core::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::IndexMut,
|
ops::IndexMut,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
os::fd::AsRawFd,
|
os::fd::AsRawFd,
|
||||||
@ -19,14 +19,14 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
use libafl_bolts::core_affinity::CoreId;
|
use libafl_bolts::core_affinity::CoreId;
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
fs::{get_unique_std_input_file, InputFile},
|
fs::{get_unique_std_input_file, InputFile},
|
||||||
tuples::{Handle, MatchName, RefIndexable},
|
tuples::{Handle, MatchName, RefIndexable},
|
||||||
AsSlice,
|
AsSlice,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
use libc::STDIN_FILENO;
|
use libc::STDIN_FILENO;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use nix::{
|
use nix::{
|
||||||
@ -42,7 +42,7 @@ use nix::{
|
|||||||
},
|
},
|
||||||
unistd::Pid,
|
unistd::Pid,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
use super::HasTimeout;
|
use super::HasTimeout;
|
||||||
@ -181,7 +181,7 @@ where
|
|||||||
///
|
///
|
||||||
/// This configurator was primarly developed to be used in conjunction with
|
/// This configurator was primarly developed to be used in conjunction with
|
||||||
/// [`crate::executors::hooks::intel_pt::IntelPTHook`]
|
/// [`crate::executors::hooks::intel_pt::IntelPTHook`]
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, TypedBuilder)]
|
#[derive(Debug, Clone, PartialEq, Eq, TypedBuilder)]
|
||||||
pub struct PTraceCommandConfigurator {
|
pub struct PTraceCommandConfigurator {
|
||||||
#[builder(setter(into))]
|
#[builder(setter(into))]
|
||||||
@ -198,7 +198,7 @@ pub struct PTraceCommandConfigurator {
|
|||||||
timeout: u32,
|
timeout: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(all(feature = "intel_pt", target_os = "linux"))]
|
||||||
impl<I> CommandConfigurator<I, Pid> for PTraceCommandConfigurator
|
impl<I> CommandConfigurator<I, Pid> for PTraceCommandConfigurator
|
||||||
where
|
where
|
||||||
I: HasTargetBytes,
|
I: HasTargetBytes,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! The [`ValueBloomFeedback`] checks if a value has already been observed in a [`BloomFilter`] and returns `true` if the value is new, adding it to the bloom filter.
|
//! The [`ValueBloomFeedback`] checks if a value has already been observed in a [`BloomFilter`] and returns `true` if the value is new, adding it to the bloom filter.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use alloc::borrow::Cow;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use fastbloom::BloomFilter;
|
use fastbloom::BloomFilter;
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
|
@ -42,10 +42,10 @@ where
|
|||||||
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
|
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
|
||||||
EM: UsesState<State = S>, //delete me
|
EM: UsesState<State = S>, //delete me
|
||||||
{
|
{
|
||||||
#[expect(rustdoc::broken_intra_doc_links)]
|
|
||||||
/// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your
|
/// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your
|
||||||
/// own stage and you need to manage [`super::NestedStageRetryCountRestartHelper`] differently
|
/// own stage and you need to manage [`super::NestedStageRetryCountRestartHelper`] differently
|
||||||
/// see [`super::ConcolicTracingStage`]'s implementation as an example of usage.
|
/// see [`super::ConcolicTracingStage`]'s implementation as an example of usage.
|
||||||
|
#[allow(rustdoc::broken_intra_doc_links)]
|
||||||
pub fn trace(&mut self, fuzzer: &mut Z, state: &mut S, manager: &mut EM) -> Result<(), Error> {
|
pub fn trace(&mut self, fuzzer: &mut Z, state: &mut S, manager: &mut EM) -> Result<(), Error> {
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let input = state.current_input_cloned()?;
|
let input = state.current_input_cloned()?;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libafl_qemu"
|
name = "libafl_qemu"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
authors = [
|
||||||
|
"Andrea Fioraldi <andreafioraldi@gmail.com>",
|
||||||
|
"Romain Malmain <rmalmain@pm.me>",
|
||||||
|
]
|
||||||
description = "QEMU user backend library for LibAFL"
|
description = "QEMU user backend library for LibAFL"
|
||||||
documentation = "https://docs.rs/libafl_qemu"
|
documentation = "https://docs.rs/libafl_qemu"
|
||||||
repository = "https://github.com/AFLplusplus/LibAFL/"
|
repository = "https://github.com/AFLplusplus/LibAFL/"
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libafl_qemu_build"
|
name = "libafl_qemu_build"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
authors = [
|
||||||
|
"Andrea Fioraldi <andreafioraldi@gmail.com>",
|
||||||
|
"Romain Malmain <rmalmain@pm.me>",
|
||||||
|
]
|
||||||
description = "Builder for LibAFL QEMU"
|
description = "Builder for LibAFL QEMU"
|
||||||
documentation = "https://docs.rs/libafl_qemu_build"
|
documentation = "https://docs.rs/libafl_qemu_build"
|
||||||
repository = "https://github.com/AFLplusplus/LibAFL/"
|
repository = "https://github.com/AFLplusplus/LibAFL/"
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libafl_qemu_sys"
|
name = "libafl_qemu_sys"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
authors = [
|
||||||
|
"Andrea Fioraldi <andreafioraldi@gmail.com>",
|
||||||
|
"Romain Malmain <rmalmain@pm.me>",
|
||||||
|
]
|
||||||
description = "C to Rust bindings for the LibAFL QEMU bridge"
|
description = "C to Rust bindings for the LibAFL QEMU bridge"
|
||||||
documentation = "https://docs.rs/libafl_qemu_sys"
|
documentation = "https://docs.rs/libafl_qemu_sys"
|
||||||
repository = "https://github.com/AFLplusplus/LibAFL/"
|
repository = "https://github.com/AFLplusplus/LibAFL/"
|
||||||
|
@ -452,8 +452,8 @@ where
|
|||||||
// Unleash hooks if locked
|
// Unleash hooks if locked
|
||||||
if emu.driver_mut().unlock_hooks() {
|
if emu.driver_mut().unlock_hooks() {
|
||||||
// Prepare hooks
|
// Prepare hooks
|
||||||
emu.modules_mut().first_exec_all(state);
|
emu.modules_mut().first_exec_all(qemu, state);
|
||||||
emu.modules_mut().pre_exec_all(state, input);
|
emu.modules_mut().pre_exec_all(qemu, state, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto page filtering if option is enabled
|
// Auto page filtering if option is enabled
|
||||||
|
@ -1,30 +1,33 @@
|
|||||||
use std::{fmt::Debug, marker::PhantomData};
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
inputs::{HasTargetBytes, UsesInput},
|
inputs::{HasTargetBytes, UsesInput},
|
||||||
state::{HasExecutions, State},
|
state::{HasExecutions, State},
|
||||||
};
|
};
|
||||||
use libafl_bolts::tuples::{tuple_list, Prepend};
|
use libafl_bolts::tuples::{tuple_list, Append, Prepend};
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::FastSnapshotManager;
|
use crate::FastSnapshotManager;
|
||||||
use crate::{
|
use crate::{
|
||||||
command::{CommandManager, NopCommandManager, StdCommandManager},
|
command::{CommandManager, NopCommandManager, StdCommandManager},
|
||||||
config::QemuConfig,
|
config::QemuConfigBuilder,
|
||||||
modules::{EmulatorModule, EmulatorModuleTuple},
|
modules::{EmulatorModule, EmulatorModuleTuple},
|
||||||
Emulator, EmulatorHooks, NopEmulatorDriver, NopSnapshotManager, Qemu, QemuHooks, QemuInitError,
|
Emulator, NopEmulatorDriver, NopSnapshotManager, QemuInitError, QemuParams, StdEmulatorDriver,
|
||||||
StdEmulatorDriver, StdSnapshotManager,
|
StdSnapshotManager,
|
||||||
};
|
};
|
||||||
|
#[cfg(doc)]
|
||||||
|
use crate::{config::QemuConfig, Qemu};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
/// An [`Emulator`] Builder.
|
||||||
enum QemuBuilder {
|
///
|
||||||
Qemu(Qemu),
|
/// It is the most common way to create a new [`Emulator`].
|
||||||
QemuConfig(QemuConfig),
|
/// In addition to the main components of an [`Emulator`], it expects to receive a way to initialize [`Qemu`].
|
||||||
QemuString(Vec<String>),
|
/// It must be set through [`EmulatorBuilder::qemu_parameters`].
|
||||||
}
|
/// At the moment, there are two main ways to initialize QEMU:
|
||||||
|
/// - with a QEMU-compatible CLI. It will be given to QEMU as-is. The first argument should always be a path to the running binary, as expected by execve.
|
||||||
#[derive(Clone, Debug)]
|
/// - with an instance of [`QemuConfig`]. It is a more programmatic way to configure [`Qemu`]. It should be built using [`QemuConfigBuilder`].
|
||||||
pub struct EmulatorBuilder<CM, ED, ET, S, SM>
|
#[derive(Clone)]
|
||||||
|
pub struct EmulatorBuilder<CM, ED, ET, QP, S, SM>
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
@ -32,11 +35,19 @@ where
|
|||||||
driver: ED,
|
driver: ED,
|
||||||
snapshot_manager: SM,
|
snapshot_manager: SM,
|
||||||
command_manager: CM,
|
command_manager: CM,
|
||||||
qemu_builder: Option<QemuBuilder>,
|
qemu_parameters: Option<QP>,
|
||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> EmulatorBuilder<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager>
|
impl<S>
|
||||||
|
EmulatorBuilder<
|
||||||
|
NopCommandManager,
|
||||||
|
NopEmulatorDriver,
|
||||||
|
(),
|
||||||
|
QemuConfigBuilder,
|
||||||
|
S,
|
||||||
|
NopSnapshotManager,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
@ -47,14 +58,22 @@ where
|
|||||||
driver: NopEmulatorDriver,
|
driver: NopEmulatorDriver,
|
||||||
snapshot_manager: NopSnapshotManager,
|
snapshot_manager: NopSnapshotManager,
|
||||||
command_manager: NopCommandManager,
|
command_manager: NopCommandManager,
|
||||||
qemu_builder: None,
|
qemu_parameters: None,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
impl<S> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager>
|
impl<S>
|
||||||
|
EmulatorBuilder<
|
||||||
|
StdCommandManager<S>,
|
||||||
|
StdEmulatorDriver,
|
||||||
|
(),
|
||||||
|
QemuConfigBuilder,
|
||||||
|
S,
|
||||||
|
StdSnapshotManager,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
S: State + HasExecutions + Unpin,
|
S: State + HasExecutions + Unpin,
|
||||||
S::Input: HasTargetBytes,
|
S::Input: HasTargetBytes,
|
||||||
@ -67,14 +86,22 @@ where
|
|||||||
command_manager: StdCommandManager::default(),
|
command_manager: StdCommandManager::default(),
|
||||||
snapshot_manager: StdSnapshotManager::default(),
|
snapshot_manager: StdSnapshotManager::default(),
|
||||||
driver: StdEmulatorDriver::builder().build(),
|
driver: StdEmulatorDriver::builder().build(),
|
||||||
qemu_builder: None,
|
qemu_parameters: None,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
impl<S> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager>
|
impl<S>
|
||||||
|
EmulatorBuilder<
|
||||||
|
StdCommandManager<S>,
|
||||||
|
StdEmulatorDriver,
|
||||||
|
(),
|
||||||
|
QemuConfigBuilder,
|
||||||
|
S,
|
||||||
|
StdSnapshotManager,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
S: State + HasExecutions + Unpin,
|
S: State + HasExecutions + Unpin,
|
||||||
S::Input: HasTargetBytes,
|
S::Input: HasTargetBytes,
|
||||||
@ -87,12 +114,12 @@ where
|
|||||||
command_manager: StdCommandManager::default(),
|
command_manager: StdCommandManager::default(),
|
||||||
snapshot_manager: FastSnapshotManager::default(),
|
snapshot_manager: FastSnapshotManager::default(),
|
||||||
driver: StdEmulatorDriver::builder().build(),
|
driver: StdEmulatorDriver::builder().build(),
|
||||||
qemu_builder: None,
|
qemu_parameters: None,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<CM, ED, ET, S, SM> EmulatorBuilder<CM, ED, ET, S, SM>
|
impl<CM, ED, ET, QP, S, SM> EmulatorBuilder<CM, ED, ET, QP, S, SM>
|
||||||
where
|
where
|
||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
@ -101,90 +128,63 @@ where
|
|||||||
driver: ED,
|
driver: ED,
|
||||||
command_manager: CM,
|
command_manager: CM,
|
||||||
snapshot_manager: SM,
|
snapshot_manager: SM,
|
||||||
qemu_builder: Option<QemuBuilder>,
|
qemu_parameters: Option<QP>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
modules,
|
modules,
|
||||||
command_manager,
|
command_manager,
|
||||||
driver,
|
driver,
|
||||||
snapshot_manager,
|
snapshot_manager,
|
||||||
qemu_builder,
|
qemu_parameters,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<Emulator<CM, ED, ET, S, SM>, QemuInitError>
|
pub fn build<E>(self) -> Result<Emulator<CM, ED, ET, S, SM>, QemuInitError>
|
||||||
where
|
where
|
||||||
CM: CommandManager<ED, ET, S, SM>,
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
QP: TryInto<QemuParams, Error = E>,
|
||||||
|
QemuInitError: From<E>,
|
||||||
{
|
{
|
||||||
let qemu_builder = self.qemu_builder.ok_or(QemuInitError::EmptyArgs)?;
|
let qemu_params: QemuParams = self
|
||||||
|
.qemu_parameters
|
||||||
|
.ok_or(QemuInitError::NoParametersProvided)?
|
||||||
|
.try_into()?;
|
||||||
|
|
||||||
let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) };
|
Emulator::new(
|
||||||
|
qemu_params,
|
||||||
self.modules.pre_qemu_init_all(&mut emulator_hooks);
|
self.modules,
|
||||||
|
self.driver,
|
||||||
let qemu: Qemu = match qemu_builder {
|
self.snapshot_manager,
|
||||||
QemuBuilder::Qemu(qemu) => qemu,
|
self.command_manager,
|
||||||
QemuBuilder::QemuConfig(qemu_config) => {
|
)
|
||||||
let res: Result<Qemu, QemuInitError> = qemu_config.into();
|
|
||||||
res?
|
|
||||||
}
|
|
||||||
QemuBuilder::QemuString(qemu_string) => Qemu::init(&qemu_string)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
Ok(Emulator::new_with_qemu(
|
|
||||||
qemu,
|
|
||||||
emulator_hooks,
|
|
||||||
self.modules,
|
|
||||||
self.driver,
|
|
||||||
self.snapshot_manager,
|
|
||||||
self.command_manager,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CM, ED, ET, S, SM> EmulatorBuilder<CM, ED, ET, S, SM>
|
impl<CM, ED, ET, QP, S, SM> EmulatorBuilder<CM, ED, ET, QP, S, SM>
|
||||||
where
|
where
|
||||||
CM: CommandManager<ED, ET, S, SM>,
|
CM: CommandManager<ED, ET, S, SM>,
|
||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn qemu_config(self, qemu_config: QemuConfig) -> EmulatorBuilder<CM, ED, ET, S, SM> {
|
pub fn qemu_parameters<QP2>(
|
||||||
|
self,
|
||||||
|
qemu_parameters: QP2,
|
||||||
|
) -> EmulatorBuilder<CM, ED, ET, QP2, S, SM>
|
||||||
|
where
|
||||||
|
QP2: Into<QemuParams>,
|
||||||
|
{
|
||||||
EmulatorBuilder::new(
|
EmulatorBuilder::new(
|
||||||
self.modules,
|
self.modules,
|
||||||
self.driver,
|
self.driver,
|
||||||
self.command_manager,
|
self.command_manager,
|
||||||
self.snapshot_manager,
|
self.snapshot_manager,
|
||||||
Some(QemuBuilder::QemuConfig(qemu_config)),
|
Some(qemu_parameters),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
pub fn prepend_module<EM>(self, module: EM) -> EmulatorBuilder<CM, ED, (EM, ET), QP, S, SM>
|
||||||
pub fn qemu_cli(self, qemu_cli: Vec<String>) -> EmulatorBuilder<CM, ED, ET, S, SM> {
|
|
||||||
EmulatorBuilder::new(
|
|
||||||
self.modules,
|
|
||||||
self.driver,
|
|
||||||
self.command_manager,
|
|
||||||
self.snapshot_manager,
|
|
||||||
Some(QemuBuilder::QemuString(qemu_cli)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn qemu(self, qemu: Qemu) -> EmulatorBuilder<CM, ED, ET, S, SM> {
|
|
||||||
EmulatorBuilder::new(
|
|
||||||
self.modules,
|
|
||||||
self.driver,
|
|
||||||
self.command_manager,
|
|
||||||
self.snapshot_manager,
|
|
||||||
Some(QemuBuilder::Qemu(qemu)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_module<EM>(self, module: EM) -> EmulatorBuilder<CM, ED, (EM, ET), S, SM>
|
|
||||||
where
|
where
|
||||||
EM: EmulatorModule<S> + Unpin,
|
EM: EmulatorModule<S> + Unpin,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
@ -194,21 +194,38 @@ where
|
|||||||
self.driver,
|
self.driver,
|
||||||
self.command_manager,
|
self.command_manager,
|
||||||
self.snapshot_manager,
|
self.snapshot_manager,
|
||||||
self.qemu_builder,
|
self.qemu_parameters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn driver<ED2>(self, driver: ED2) -> EmulatorBuilder<CM, ED2, ET, S, SM> {
|
pub fn append_module<EM>(self, module: EM) -> EmulatorBuilder<CM, ED, (ET, EM), QP, S, SM>
|
||||||
|
where
|
||||||
|
EM: EmulatorModule<S> + Unpin,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
EmulatorBuilder::new(
|
||||||
|
self.modules.append(module),
|
||||||
|
self.driver,
|
||||||
|
self.command_manager,
|
||||||
|
self.snapshot_manager,
|
||||||
|
self.qemu_parameters,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn driver<ED2>(self, driver: ED2) -> EmulatorBuilder<CM, ED2, ET, QP, S, SM> {
|
||||||
EmulatorBuilder::new(
|
EmulatorBuilder::new(
|
||||||
self.modules,
|
self.modules,
|
||||||
driver,
|
driver,
|
||||||
self.command_manager,
|
self.command_manager,
|
||||||
self.snapshot_manager,
|
self.snapshot_manager,
|
||||||
self.qemu_builder,
|
self.qemu_parameters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command_manager<CM2>(self, command_manager: CM2) -> EmulatorBuilder<CM2, ED, ET, S, SM>
|
pub fn command_manager<CM2>(
|
||||||
|
self,
|
||||||
|
command_manager: CM2,
|
||||||
|
) -> EmulatorBuilder<CM2, ED, ET, QP, S, SM>
|
||||||
where
|
where
|
||||||
CM2: CommandManager<ED, ET, S, SM>,
|
CM2: CommandManager<ED, ET, S, SM>,
|
||||||
{
|
{
|
||||||
@ -217,30 +234,30 @@ where
|
|||||||
self.driver,
|
self.driver,
|
||||||
command_manager,
|
command_manager,
|
||||||
self.snapshot_manager,
|
self.snapshot_manager,
|
||||||
self.qemu_builder,
|
self.qemu_parameters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modules<ET2>(self, modules: ET2) -> EmulatorBuilder<CM, ED, ET2, S, SM> {
|
pub fn modules<ET2>(self, modules: ET2) -> EmulatorBuilder<CM, ED, ET2, QP, S, SM> {
|
||||||
EmulatorBuilder::new(
|
EmulatorBuilder::new(
|
||||||
modules,
|
modules,
|
||||||
self.driver,
|
self.driver,
|
||||||
self.command_manager,
|
self.command_manager,
|
||||||
self.snapshot_manager,
|
self.snapshot_manager,
|
||||||
self.qemu_builder,
|
self.qemu_parameters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapshot_manager<SM2>(
|
pub fn snapshot_manager<SM2>(
|
||||||
self,
|
self,
|
||||||
snapshot_manager: SM2,
|
snapshot_manager: SM2,
|
||||||
) -> EmulatorBuilder<CM, ED, ET, S, SM2> {
|
) -> EmulatorBuilder<CM, ED, ET, QP, S, SM2> {
|
||||||
EmulatorBuilder::new(
|
EmulatorBuilder::new(
|
||||||
self.modules,
|
self.modules,
|
||||||
self.driver,
|
self.driver,
|
||||||
self.command_manager,
|
self.command_manager,
|
||||||
snapshot_manager,
|
snapshot_manager,
|
||||||
self.qemu_builder,
|
self.qemu_parameters,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ where
|
|||||||
/// Just before calling user's harness for the first time.
|
/// Just before calling user's harness for the first time.
|
||||||
/// Called only once
|
/// Called only once
|
||||||
fn first_harness_exec(emulator: &mut Emulator<CM, Self, ET, S, SM>, state: &mut S) {
|
fn first_harness_exec(emulator: &mut Emulator<CM, Self, ET, S, SM>, state: &mut S) {
|
||||||
emulator.modules.first_exec_all(state);
|
emulator.modules.first_exec_all(emulator.qemu, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Just before calling user's harness
|
/// Just before calling user's harness
|
||||||
@ -63,7 +63,7 @@ where
|
|||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
) {
|
) {
|
||||||
emulator.modules.pre_exec_all(state, input);
|
emulator.modules.pre_exec_all(emulator.qemu, state, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Just after returning from user's harness
|
/// Just after returning from user's harness
|
||||||
@ -78,7 +78,7 @@ where
|
|||||||
{
|
{
|
||||||
emulator
|
emulator
|
||||||
.modules
|
.modules
|
||||||
.post_exec_all(state, input, observers, exit_kind);
|
.post_exec_all(emulator.qemu, state, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Just before entering QEMU
|
/// Just before entering QEMU
|
||||||
@ -169,7 +169,7 @@ where
|
|||||||
{
|
{
|
||||||
fn first_harness_exec(emulator: &mut Emulator<CM, Self, ET, S, SM>, state: &mut S) {
|
fn first_harness_exec(emulator: &mut Emulator<CM, Self, ET, S, SM>, state: &mut S) {
|
||||||
if !emulator.driver.hooks_locked {
|
if !emulator.driver.hooks_locked {
|
||||||
emulator.modules.first_exec_all(state);
|
emulator.modules.first_exec_all(emulator.qemu, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ where
|
|||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
) {
|
) {
|
||||||
if !emulator.driver.hooks_locked {
|
if !emulator.driver.hooks_locked {
|
||||||
emulator.modules.pre_exec_all(state, input);
|
emulator.modules.pre_exec_all(emulator.qemu, state, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
let input_location = { emulator.driver.input_location.get().cloned() };
|
let input_location = { emulator.driver.input_location.get().cloned() };
|
||||||
@ -206,7 +206,7 @@ where
|
|||||||
if !emulator.driver.hooks_locked {
|
if !emulator.driver.hooks_locked {
|
||||||
emulator
|
emulator
|
||||||
.modules
|
.modules
|
||||||
.post_exec_all(state, input, observers, exit_kind);
|
.post_exec_all(emulator.qemu, state, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
use std::{fmt::Debug, marker::PhantomData, mem::transmute, pin::Pin, ptr};
|
use std::{fmt::Debug, marker::PhantomData, mem::transmute, pin::Pin, ptr};
|
||||||
|
|
||||||
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
||||||
use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp};
|
use libafl_qemu_sys::{CPUStatePtr, FatPtr, GuestAddr, GuestUsize, TCGTemp};
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
use crate::qemu::{
|
use crate::qemu::{
|
||||||
closure_post_syscall_hook_wrapper, closure_pre_syscall_hook_wrapper,
|
closure_post_syscall_hook_wrapper, closure_pre_syscall_hook_wrapper,
|
||||||
func_post_syscall_hook_wrapper, func_pre_syscall_hook_wrapper, PostSyscallHook,
|
func_post_syscall_hook_wrapper, func_pre_syscall_hook_wrapper, PostSyscallHook,
|
||||||
PostSyscallHookId, PreSyscallHook, PreSyscallHookId, SyscallHookResult,
|
PostSyscallHookId, PreSyscallHook, PreSyscallHookId,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
use crate::qemu::{
|
use crate::qemu::{
|
||||||
@ -37,9 +37,15 @@ use crate::{
|
|||||||
ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook,
|
ReadExecNHook, ReadGenHook, ReadHookId, TcgHookState, WriteExecHook, WriteExecNHook,
|
||||||
WriteGenHook, WriteHookId,
|
WriteGenHook, WriteHookId,
|
||||||
},
|
},
|
||||||
CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, Qemu,
|
CpuPostRunHook, CpuPreRunHook, CpuRunHookId, HookState, MemAccessInfo, NewThreadHookFn, Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Get a C-compatible function pointer from the input hook.
|
||||||
|
/// If the hook was already a c function, nothing is done.
|
||||||
|
///
|
||||||
|
/// h: input hook
|
||||||
|
/// replacement: C-compatible function to call when C side should call the hook
|
||||||
|
/// fntype: type used to cast h into replacement
|
||||||
macro_rules! get_raw_hook {
|
macro_rules! get_raw_hook {
|
||||||
($h:expr, $replacement:expr, $fntype:ty) => {
|
($h:expr, $replacement:expr, $fntype:ty) => {
|
||||||
match $h {
|
match $h {
|
||||||
@ -74,6 +80,7 @@ where
|
|||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let emulator_modules = EmulatorModules::<ET, S>::emulator_modules_mut().unwrap();
|
let emulator_modules = EmulatorModules::<ET, S>::emulator_modules_mut().unwrap();
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
|
|
||||||
let crash_hooks_ptr = &raw mut emulator_modules.hooks.crash_hooks;
|
let crash_hooks_ptr = &raw mut emulator_modules.hooks.crash_hooks;
|
||||||
|
|
||||||
@ -81,12 +88,12 @@ where
|
|||||||
match crash_hook {
|
match crash_hook {
|
||||||
HookRepr::Function(ptr) => {
|
HookRepr::Function(ptr) => {
|
||||||
let func: CrashHookFn<ET, S> = transmute(*ptr);
|
let func: CrashHookFn<ET, S> = transmute(*ptr);
|
||||||
func(emulator_modules, target_sig);
|
func(qemu, emulator_modules, target_sig);
|
||||||
}
|
}
|
||||||
HookRepr::Closure(ptr) => {
|
HookRepr::Closure(ptr) => {
|
||||||
let func: &mut CrashHookClosure<ET, S> =
|
let func: &mut CrashHookClosure<ET, S> =
|
||||||
&mut *(ptr::from_mut::<FatPtr>(ptr) as *mut CrashHookClosure<ET, S>);
|
&mut *(ptr::from_mut::<FatPtr>(ptr) as *mut CrashHookClosure<ET, S>);
|
||||||
func(emulator_modules, target_sig);
|
func(qemu, emulator_modules, target_sig);
|
||||||
}
|
}
|
||||||
HookRepr::Empty => (),
|
HookRepr::Empty => (),
|
||||||
}
|
}
|
||||||
@ -100,7 +107,6 @@ pub struct EmulatorModules<ET, S>
|
|||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
qemu: Qemu,
|
|
||||||
modules: Pin<Box<ET>>,
|
modules: Pin<Box<ET>>,
|
||||||
hooks: EmulatorHooks<ET, S>,
|
hooks: EmulatorHooks<ET, S>,
|
||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
@ -170,6 +176,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn qemu_hooks(&self) -> QemuHooks {
|
||||||
|
self.qemu_hooks
|
||||||
|
}
|
||||||
|
|
||||||
pub fn instruction_closure(
|
pub fn instruction_closure(
|
||||||
&mut self,
|
&mut self,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
@ -679,10 +690,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn backdoor_function(
|
pub fn backdoor_function(&self, hook: BackdoorHookFn<ET, S>) -> BackdoorHookId {
|
||||||
&self,
|
|
||||||
hook: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUArchStatePtr, pc: GuestAddr),
|
|
||||||
) -> BackdoorHookId {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.qemu_hooks
|
self.qemu_hooks
|
||||||
.add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::<ET, S>)
|
.add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::<ET, S>)
|
||||||
@ -715,15 +723,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_creation_function(
|
pub fn thread_creation_function(&mut self, hook: NewThreadHookFn<ET, S>) -> NewThreadHookId {
|
||||||
&mut self,
|
|
||||||
hook: fn(
|
|
||||||
&mut EmulatorModules<ET, S>,
|
|
||||||
Option<&mut S>,
|
|
||||||
env: CPUArchStatePtr,
|
|
||||||
tid: u32,
|
|
||||||
) -> bool,
|
|
||||||
) -> NewThreadHookId {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
self.qemu_hooks
|
self.qemu_hooks
|
||||||
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<ET, S>)
|
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<ET, S>)
|
||||||
@ -767,10 +767,10 @@ where
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
{
|
{
|
||||||
pub fn syscalls(&mut self, hook: PreSyscallHook<ET, S>) -> Option<PreSyscallHookId> {
|
pub fn pre_syscalls(&mut self, hook: PreSyscallHook<ET, S>) -> Option<PreSyscallHookId> {
|
||||||
match hook {
|
match hook {
|
||||||
Hook::Function(f) => Some(self.syscalls_function(f)),
|
Hook::Function(f) => Some(self.pre_syscalls_function(f)),
|
||||||
Hook::Closure(c) => Some(self.syscalls_closure(c)),
|
Hook::Closure(c) => Some(self.pre_syscalls_closure(c)),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
Some(self.qemu_hooks.add_pre_syscall_hook(z, r))
|
Some(self.qemu_hooks.add_pre_syscall_hook(z, r))
|
||||||
@ -779,7 +779,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syscalls_function(&mut self, hook: PreSyscallHookFn<ET, S>) -> PreSyscallHookId {
|
pub fn pre_syscalls_function(&mut self, hook: PreSyscallHookFn<ET, S>) -> PreSyscallHookId {
|
||||||
// # Safety
|
// # Safety
|
||||||
// Will dereference the hook as [`FatPtr`].
|
// Will dereference the hook as [`FatPtr`].
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -788,7 +788,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syscalls_closure(&mut self, hook: PreSyscallHookClosure<ET, S>) -> PreSyscallHookId {
|
pub fn pre_syscalls_closure(&mut self, hook: PreSyscallHookClosure<ET, S>) -> PreSyscallHookId {
|
||||||
// # Safety
|
// # Safety
|
||||||
// Will dereference the hook as [`FatPtr`].
|
// Will dereference the hook as [`FatPtr`].
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -818,10 +818,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_syscalls(&mut self, hook: PostSyscallHook<ET, S>) -> Option<PostSyscallHookId> {
|
pub fn post_syscalls(&mut self, hook: PostSyscallHook<ET, S>) -> Option<PostSyscallHookId> {
|
||||||
match hook {
|
match hook {
|
||||||
Hook::Function(f) => Some(self.after_syscalls_function(f)),
|
Hook::Function(f) => Some(self.post_syscalls_function(f)),
|
||||||
Hook::Closure(c) => Some(self.after_syscalls_closure(c)),
|
Hook::Closure(c) => Some(self.post_syscalls_closure(c)),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
Some(self.qemu_hooks.add_post_syscall_hook(z, r))
|
Some(self.qemu_hooks.add_post_syscall_hook(z, r))
|
||||||
@ -830,7 +830,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_syscalls_function(&mut self, hook: PostSyscallHookFn<ET, S>) -> PostSyscallHookId {
|
pub fn post_syscalls_function(&mut self, hook: PostSyscallHookFn<ET, S>) -> PostSyscallHookId {
|
||||||
// # Safety
|
// # Safety
|
||||||
// Will dereference the hook as [`FatPtr`]. This should be ok.
|
// Will dereference the hook as [`FatPtr`]. This should be ok.
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -839,7 +839,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_syscalls_closure(
|
pub fn post_syscalls_closure(
|
||||||
&mut self,
|
&mut self,
|
||||||
hook: PostSyscallHookClosure<ET, S>,
|
hook: PostSyscallHookClosure<ET, S>,
|
||||||
) -> PostSyscallHookId {
|
) -> PostSyscallHookId {
|
||||||
@ -870,7 +870,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
|
pub fn crash_function(&mut self, hook: CrashHookFn<ET, S>) {
|
||||||
// # Safety
|
// # Safety
|
||||||
// Will cast the valid hook to a ptr.
|
// Will cast the valid hook to a ptr.
|
||||||
self.qemu_hooks.set_crash_hook(crash_hook_wrapper::<ET, S>);
|
self.qemu_hooks.set_crash_hook(crash_hook_wrapper::<ET, S>);
|
||||||
@ -959,7 +959,7 @@ where
|
|||||||
pub fn instruction_function(
|
pub fn instruction_function(
|
||||||
&mut self,
|
&mut self,
|
||||||
addr: GuestAddr,
|
addr: GuestAddr,
|
||||||
hook: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, GuestAddr),
|
hook: InstructionHookFn<ET, S>,
|
||||||
invalidate_block: bool,
|
invalidate_block: bool,
|
||||||
) -> InstructionHookId {
|
) -> InstructionHookId {
|
||||||
self.hooks
|
self.hooks
|
||||||
@ -1068,15 +1068,7 @@ where
|
|||||||
self.hooks.thread_creation(hook)
|
self.hooks.thread_creation(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_creation_function(
|
pub fn thread_creation_function(&mut self, hook: NewThreadHookFn<ET, S>) -> NewThreadHookId {
|
||||||
&mut self,
|
|
||||||
hook: fn(
|
|
||||||
&mut EmulatorModules<ET, S>,
|
|
||||||
Option<&mut S>,
|
|
||||||
env: CPUArchStatePtr,
|
|
||||||
tid: u32,
|
|
||||||
) -> bool,
|
|
||||||
) -> NewThreadHookId {
|
|
||||||
self.hooks.thread_creation_function(hook)
|
self.hooks.thread_creation_function(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1093,13 +1085,8 @@ where
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
pub(super) fn new(
|
pub(super) fn new(emulator_hooks: EmulatorHooks<ET, S>, modules: ET) -> Pin<Box<Self>> {
|
||||||
qemu: Qemu,
|
|
||||||
emulator_hooks: EmulatorHooks<ET, S>,
|
|
||||||
modules: ET,
|
|
||||||
) -> Pin<Box<Self>> {
|
|
||||||
let mut modules = Box::pin(Self {
|
let mut modules = Box::pin(Self {
|
||||||
qemu,
|
|
||||||
modules: Box::pin(modules),
|
modules: Box::pin(modules),
|
||||||
hooks: emulator_hooks,
|
hooks: emulator_hooks,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
@ -1122,35 +1109,40 @@ where
|
|||||||
modules
|
modules
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_qemu_init_all(&mut self) {
|
pub fn post_qemu_init_all(&mut self, qemu: Qemu) {
|
||||||
// We give access to EmulatorModuleTuple<S> during init, the compiler complains (for good reasons)
|
// We give access to EmulatorModuleTuple<S> during init, the compiler complains (for good reasons)
|
||||||
// TODO: We should find a way to be able to check for a module without giving full access to the tuple.
|
// TODO: We should find a way to be able to check for a module without giving full access to the tuple.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.modules_mut()
|
self.modules_mut()
|
||||||
.post_qemu_init_all(Self::emulator_modules_mut_unchecked());
|
.post_qemu_init_all(qemu, Self::emulator_modules_mut_unchecked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_exec_all(&mut self, state: &mut S) {
|
pub fn first_exec_all(&mut self, qemu: Qemu, state: &mut S) {
|
||||||
// # Safety
|
// # Safety
|
||||||
// We assume that the emulator was initialized correctly
|
// We assume that the emulator was initialized correctly
|
||||||
unsafe {
|
unsafe {
|
||||||
self.modules_mut()
|
self.modules_mut()
|
||||||
.first_exec_all(Self::emulator_modules_mut_unchecked(), state);
|
.first_exec_all(qemu, Self::emulator_modules_mut_unchecked(), state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) {
|
pub fn pre_exec_all(&mut self, qemu: Qemu, state: &mut S, input: &S::Input) {
|
||||||
// # Safety
|
// # Safety
|
||||||
// We assume that the emulator was initialized correctly
|
// We assume that the emulator was initialized correctly
|
||||||
unsafe {
|
unsafe {
|
||||||
self.modules_mut()
|
self.modules_mut().pre_exec_all(
|
||||||
.pre_exec_all(Self::emulator_modules_mut_unchecked(), state, input);
|
qemu,
|
||||||
|
Self::emulator_modules_mut_unchecked(),
|
||||||
|
state,
|
||||||
|
input,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_exec_all<OT>(
|
pub fn post_exec_all<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
@ -1160,6 +1152,7 @@ where
|
|||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
self.modules_mut().post_exec_all(
|
self.modules_mut().post_exec_all(
|
||||||
|
qemu,
|
||||||
Self::emulator_modules_mut_unchecked(),
|
Self::emulator_modules_mut_unchecked(),
|
||||||
state,
|
state,
|
||||||
input,
|
input,
|
||||||
@ -1191,16 +1184,15 @@ impl<ET, S> EmulatorModules<ET, S>
|
|||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
#[must_use]
|
|
||||||
pub fn qemu(&self) -> Qemu {
|
|
||||||
self.qemu
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn modules(&self) -> &ET {
|
pub fn modules(&self) -> &ET {
|
||||||
self.modules.as_ref().get_ref()
|
self.modules.as_ref().get_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hooks(&mut self) -> &EmulatorHooks<ET, S> {
|
||||||
|
&self.hooks
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hooks_mut(&mut self) -> &mut EmulatorHooks<ET, S> {
|
pub fn hooks_mut(&mut self) -> &mut EmulatorHooks<ET, S> {
|
||||||
&mut self.hooks
|
&mut self.hooks
|
||||||
}
|
}
|
||||||
@ -1213,107 +1205,53 @@ where
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
{
|
{
|
||||||
pub fn syscalls(&mut self, hook: PreSyscallHook<ET, S>) -> Option<PreSyscallHookId> {
|
pub fn pre_syscalls(&mut self, hook: PreSyscallHook<ET, S>) -> Option<PreSyscallHookId> {
|
||||||
self.hooks.syscalls(hook)
|
self.hooks.pre_syscalls(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Calls through to the, potentially unsafe, `syscalls_function`
|
/// Calls through to the, potentially unsafe, `syscalls_function`
|
||||||
#[expect(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub unsafe fn syscalls_function(
|
pub unsafe fn pre_syscalls_function(
|
||||||
&mut self,
|
&mut self,
|
||||||
hook: fn(
|
hook: PreSyscallHookFn<ET, S>,
|
||||||
&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,
|
|
||||||
) -> PreSyscallHookId {
|
) -> PreSyscallHookId {
|
||||||
self.hooks.syscalls_function(hook)
|
self.hooks.pre_syscalls_function(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Calls through to the, potentially unsafe, `syscalls_closure`
|
/// Calls through to the, potentially unsafe, `syscalls_closure`
|
||||||
#[expect(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub unsafe fn syscalls_closure(
|
pub unsafe fn pre_syscalls_closure(
|
||||||
&mut self,
|
&mut self,
|
||||||
hook: Box<
|
hook: PreSyscallHookClosure<ET, S>,
|
||||||
dyn for<'a> FnMut(
|
|
||||||
&'a mut EmulatorModules<ET, S>,
|
|
||||||
Option<&'a mut S>,
|
|
||||||
i32,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
) -> SyscallHookResult,
|
|
||||||
>,
|
|
||||||
) -> PreSyscallHookId {
|
) -> PreSyscallHookId {
|
||||||
self.hooks.syscalls_closure(hook)
|
self.hooks.pre_syscalls_closure(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_syscalls(&mut self, hook: PostSyscallHook<ET, S>) -> Option<PostSyscallHookId> {
|
pub fn post_syscalls(&mut self, hook: PostSyscallHook<ET, S>) -> Option<PostSyscallHookId> {
|
||||||
self.hooks.after_syscalls(hook)
|
self.hooks.post_syscalls(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Calls through to the, potentially unsafe, `after_syscalls_function`
|
/// Calls through to the, potentially unsafe, `after_syscalls_function`
|
||||||
#[expect(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub unsafe fn after_syscalls_function(
|
pub unsafe fn post_syscalls_function(
|
||||||
&mut self,
|
&mut self,
|
||||||
hook: fn(
|
hook: PostSyscallHookFn<ET, S>,
|
||||||
&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,
|
|
||||||
) -> PostSyscallHookId {
|
) -> PostSyscallHookId {
|
||||||
self.hooks.after_syscalls_function(hook)
|
self.hooks.post_syscalls_function(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn after_syscalls_closure(
|
pub fn post_syscalls_closure(
|
||||||
&mut self,
|
&mut self,
|
||||||
hook: Box<
|
hook: PostSyscallHookClosure<ET, S>,
|
||||||
dyn for<'a> FnMut(
|
|
||||||
&'a mut EmulatorModules<ET, S>,
|
|
||||||
Option<&mut S>,
|
|
||||||
GuestAddr,
|
|
||||||
i32,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
GuestAddr,
|
|
||||||
) -> GuestAddr,
|
|
||||||
>,
|
|
||||||
) -> PostSyscallHookId {
|
) -> PostSyscallHookId {
|
||||||
self.hooks.after_syscalls_closure(hook)
|
self.hooks.post_syscalls_closure(hook)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn crash_function(&mut self, hook: fn(&mut EmulatorModules<ET, S>, target_signal: i32)) {
|
pub fn crash_function(&mut self, hook: CrashHookFn<ET, S>) {
|
||||||
self.hooks.crash_function(hook);
|
self.hooks.crash_function(hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,12 +14,14 @@ use libafl::{
|
|||||||
};
|
};
|
||||||
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr};
|
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestUsize, GuestVirtAddr};
|
||||||
|
|
||||||
|
#[cfg(doc)]
|
||||||
|
use crate::modules::EmulatorModule;
|
||||||
use crate::{
|
use crate::{
|
||||||
breakpoint::{Breakpoint, BreakpointId},
|
breakpoint::{Breakpoint, BreakpointId},
|
||||||
command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
|
command::{CommandError, CommandManager, NopCommandManager, StdCommandManager},
|
||||||
modules::EmulatorModuleTuple,
|
modules::EmulatorModuleTuple,
|
||||||
sync_exit::SyncExit,
|
sync_exit::SyncExit,
|
||||||
Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk,
|
Qemu, QemuExitError, QemuExitReason, QemuHooks, QemuInitError, QemuMemoryChunk, QemuParams,
|
||||||
QemuShutdownCause, Regs, CPU,
|
QemuShutdownCause, Regs, CPU,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,6 +47,8 @@ mod systemmode;
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
pub use systemmode::*;
|
pub use systemmode::*;
|
||||||
|
|
||||||
|
use crate::config::QemuConfigBuilder;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum GuestAddrKind {
|
pub enum GuestAddrKind {
|
||||||
Physical(GuestPhysAddr),
|
Physical(GuestPhysAddr),
|
||||||
@ -118,6 +122,23 @@ pub struct InputLocation {
|
|||||||
ret_register: Option<Regs>,
|
ret_register: Option<Regs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The high-level interface to [`Qemu`].
|
||||||
|
///
|
||||||
|
/// It embeds multiple structures aiming at making QEMU usage easier:
|
||||||
|
///
|
||||||
|
/// - An [`IsSnapshotManager`] implementation, implementing the QEMU snapshot method to use.
|
||||||
|
/// - An [`EmulatorDriver`] implementation, responsible for handling the high-level control flow of QEMU runtime.
|
||||||
|
/// - A [`CommandManager`] implementation, handling the commands received from the target.
|
||||||
|
/// - [`EmulatorModules`], containing the [`EmulatorModule`] implementations' state.
|
||||||
|
///
|
||||||
|
/// Each of these fields can be set manually to finely tune how QEMU is getting handled.
|
||||||
|
/// It is highly encouraged to build [`Emulator`] using the associated [`EmulatorBuilder`].
|
||||||
|
/// There are two main functions to access the builder:
|
||||||
|
///
|
||||||
|
/// - [`Emulator::builder`] gives access to the standard [`EmulatorBuilder`], embedding all the standard components of an [`Emulator`].
|
||||||
|
/// - [`Emulator::empty`] gives access to an empty [`EmulatorBuilder`]. This is mostly useful to create a more custom [`Emulator`].
|
||||||
|
///
|
||||||
|
/// Please check the documentation of [`EmulatorBuilder`] for more details.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[expect(clippy::type_complexity)]
|
#[expect(clippy::type_complexity)]
|
||||||
pub struct Emulator<CM, ED, ET, S, SM>
|
pub struct Emulator<CM, ED, ET, S, SM>
|
||||||
@ -243,8 +264,14 @@ where
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn empty(
|
pub fn empty() -> EmulatorBuilder<
|
||||||
) -> EmulatorBuilder<NopCommandManager, NopEmulatorDriver, (), S, NopSnapshotManager> {
|
NopCommandManager,
|
||||||
|
NopEmulatorDriver,
|
||||||
|
(),
|
||||||
|
QemuConfigBuilder,
|
||||||
|
S,
|
||||||
|
NopSnapshotManager,
|
||||||
|
> {
|
||||||
EmulatorBuilder::empty()
|
EmulatorBuilder::empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,8 +282,14 @@ where
|
|||||||
S::Input: HasTargetBytes,
|
S::Input: HasTargetBytes,
|
||||||
{
|
{
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn builder(
|
pub fn builder() -> EmulatorBuilder<
|
||||||
) -> EmulatorBuilder<StdCommandManager<S>, StdEmulatorDriver, (), S, StdSnapshotManager> {
|
StdCommandManager<S>,
|
||||||
|
StdEmulatorDriver,
|
||||||
|
(),
|
||||||
|
QemuConfigBuilder,
|
||||||
|
S,
|
||||||
|
StdSnapshotManager,
|
||||||
|
> {
|
||||||
EmulatorBuilder::default()
|
EmulatorBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,24 +354,36 @@ where
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
pub fn new(
|
#[allow(clippy::must_use_candidate, clippy::similar_names)]
|
||||||
qemu_args: &[String],
|
pub fn new<T>(
|
||||||
|
qemu_params: T,
|
||||||
modules: ET,
|
modules: ET,
|
||||||
driver: ED,
|
driver: ED,
|
||||||
snapshot_manager: SM,
|
snapshot_manager: SM,
|
||||||
command_manager: CM,
|
command_manager: CM,
|
||||||
) -> Result<Self, QemuInitError> {
|
) -> Result<Self, QemuInitError>
|
||||||
let mut emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) };
|
where
|
||||||
|
T: Into<QemuParams>,
|
||||||
|
{
|
||||||
|
let mut qemu_params = qemu_params.into();
|
||||||
|
|
||||||
modules.pre_qemu_init_all(&mut emulator_hooks);
|
let emulator_hooks = unsafe { EmulatorHooks::new(QemuHooks::get_unchecked()) };
|
||||||
|
let mut emulator_modules = EmulatorModules::new(emulator_hooks, modules);
|
||||||
|
|
||||||
let qemu = Qemu::init(qemu_args)?;
|
// TODO: fix things there properly. The biggest issue being that it creates 2 mut ref to the module with the callback being called
|
||||||
|
unsafe {
|
||||||
|
emulator_modules.modules_mut().pre_qemu_init_all(
|
||||||
|
EmulatorModules::<ET, S>::emulator_modules_mut_unchecked(),
|
||||||
|
&mut qemu_params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let qemu = Qemu::init(qemu_params)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
Ok(Self::new_with_qemu(
|
Ok(Self::new_with_qemu(
|
||||||
qemu,
|
qemu,
|
||||||
emulator_hooks,
|
emulator_modules,
|
||||||
modules,
|
|
||||||
driver,
|
driver,
|
||||||
snapshot_manager,
|
snapshot_manager,
|
||||||
command_manager,
|
command_manager,
|
||||||
@ -351,17 +396,16 @@ where
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// pre-init qemu hooks should be run by then.
|
/// pre-init qemu hooks should be run before calling this.
|
||||||
pub(crate) unsafe fn new_with_qemu(
|
unsafe fn new_with_qemu(
|
||||||
qemu: Qemu,
|
qemu: Qemu,
|
||||||
emulator_hooks: EmulatorHooks<ET, S>,
|
emulator_modules: Pin<Box<EmulatorModules<ET, S>>>,
|
||||||
modules: ET,
|
|
||||||
driver: ED,
|
driver: ED,
|
||||||
snapshot_manager: SM,
|
snapshot_manager: SM,
|
||||||
command_manager: CM,
|
command_manager: CM,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut emulator = Emulator {
|
let mut emulator = Emulator {
|
||||||
modules: EmulatorModules::new(qemu, emulator_hooks, modules),
|
modules: emulator_modules,
|
||||||
command_manager,
|
command_manager,
|
||||||
snapshot_manager,
|
snapshot_manager,
|
||||||
driver,
|
driver,
|
||||||
@ -370,7 +414,7 @@ where
|
|||||||
qemu,
|
qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
emulator.modules.post_qemu_init_all();
|
emulator.modules.post_qemu_init_all(qemu);
|
||||||
|
|
||||||
emulator
|
emulator
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ use libc::siginfo_t;
|
|||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
use crate::EmulatorModules;
|
use crate::EmulatorModules;
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use crate::Qemu;
|
||||||
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver};
|
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver};
|
||||||
|
|
||||||
pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
pub struct QemuExecutor<'a, CM, ED, ET, H, OT, S, SM>
|
||||||
@ -183,12 +185,12 @@ where
|
|||||||
inner.inprocess_hooks_mut().crash_handler =
|
inner.inprocess_hooks_mut().crash_handler =
|
||||||
inproc_qemu_crash_handler::<ET, S> as *const c_void;
|
inproc_qemu_crash_handler::<ET, S> as *const c_void;
|
||||||
|
|
||||||
let handler = |emulator_modules: &mut EmulatorModules<ET, S>, host_sig| {
|
let handler = |qemu: Qemu, _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::<Self, EM, OF, Z>();
|
libafl::executors::inprocess::generic_inproc_crash_handler::<Self, EM, OF, Z>();
|
||||||
}
|
}
|
||||||
if let Some(cpu) = emulator_modules.qemu().current_cpu() {
|
if let Some(cpu) = qemu.current_cpu() {
|
||||||
eprint!("Context:\n{}", cpu.display_context());
|
eprint!("Context:\n{}", cpu.display_context());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -243,6 +243,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_ret<ET, S>(
|
fn on_ret<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: Option<&mut S>,
|
state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -250,7 +251,7 @@ where
|
|||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
let ret_addr: GuestAddr = emulator_modules.qemu().read_return_address().unwrap();
|
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
||||||
|
|
||||||
// log::info!("RET @ 0x{:#x}", ret_addr);
|
// log::info!("RET @ 0x{:#x}", ret_addr);
|
||||||
|
|
||||||
@ -271,6 +272,7 @@ where
|
|||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem in nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem in nightly
|
||||||
fn gen_blocks_calls<ET, S>(
|
fn gen_blocks_calls<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -293,8 +295,6 @@ where
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
|
|
||||||
let mut call_addrs: Vec<(GuestAddr, usize)> = Vec::new();
|
let mut call_addrs: Vec<(GuestAddr, usize)> = Vec::new();
|
||||||
let mut ret_addrs: Vec<GuestAddr> = Vec::new();
|
let mut ret_addrs: Vec<GuestAddr> = Vec::new();
|
||||||
|
|
||||||
@ -363,7 +363,10 @@ where
|
|||||||
for (call_addr, call_len) in call_addrs {
|
for (call_addr, call_len) in call_addrs {
|
||||||
// TODO do not use a closure, find a more efficient way to pass call_len
|
// TODO do not use a closure, find a more efficient way to pass call_len
|
||||||
let call_cb = Box::new(
|
let call_cb = Box::new(
|
||||||
move |emulator_modules: &mut EmulatorModules<ET, S>, state: Option<&mut S>, pc| {
|
move |_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
state: Option<&mut S>,
|
||||||
|
pc| {
|
||||||
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
|
// eprintln!("CALL @ 0x{:#x}", pc + call_len);
|
||||||
let mut collectors = if let Some(h) = emulator_modules.get_mut::<Self>() {
|
let mut collectors = if let Some(h) = emulator_modules.get_mut::<Self>() {
|
||||||
h.collectors.take()
|
h.collectors.take()
|
||||||
@ -400,7 +403,7 @@ where
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
type ModulePageFilter = NopPageFilter;
|
type ModulePageFilter = NopPageFilter;
|
||||||
|
|
||||||
fn post_qemu_init<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
@ -413,21 +416,20 @@ where
|
|||||||
|
|
||||||
fn pre_exec<ET>(
|
fn pre_exec<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
) where
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.collectors
|
self.collectors.as_mut().unwrap().pre_exec_all(qemu, input);
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.pre_exec_all(emulator_modules.qemu(), input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec<OT, ET>(
|
fn post_exec<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
@ -436,12 +438,10 @@ where
|
|||||||
OT: ObserversTuple<S::Input, S>,
|
OT: ObserversTuple<S::Input, S>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.collectors.as_mut().unwrap().post_exec_all(
|
self.collectors
|
||||||
emulator_modules.qemu(),
|
.as_mut()
|
||||||
input,
|
.unwrap()
|
||||||
observers,
|
.post_exec_all(qemu, input, observers, exit_kind);
|
||||||
exit_kind,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address_filter(&self) -> &Self::ModuleAddressFilter {
|
fn address_filter(&self) -> &Self::ModuleAddressFilter {
|
||||||
@ -553,6 +553,7 @@ pub struct FullBacktraceCollector {}
|
|||||||
impl FullBacktraceCollector {
|
impl FullBacktraceCollector {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// This accesses the global [`CALLSTACKS`] variable and may not be called concurrently.
|
/// This accesses the global [`CALLSTACKS`] variable and may not be called concurrently.
|
||||||
|
#[expect(rustdoc::private_intra_doc_links)]
|
||||||
pub unsafe fn new() -> Self {
|
pub unsafe fn new() -> Self {
|
||||||
let callstacks_ptr = &raw mut CALLSTACKS;
|
let callstacks_ptr = &raw mut CALLSTACKS;
|
||||||
unsafe { (*callstacks_ptr) = Some(ThreadLocal::new()) };
|
unsafe { (*callstacks_ptr) = Some(ThreadLocal::new()) };
|
||||||
|
@ -14,11 +14,12 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use crate::modules::{NopPageFilter, NOP_PAGE_FILTER};
|
use crate::modules::{NopPageFilter, NOP_PAGE_FILTER};
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
use crate::{capstone, qemu::ArchExtras, CallingConvention, Qemu};
|
use crate::{capstone, qemu::ArchExtras, CallingConvention};
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{hash_me, AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter},
|
modules::{hash_me, AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter},
|
||||||
qemu::Hook,
|
qemu::Hook,
|
||||||
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -74,8 +75,12 @@ where
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
type ModulePageFilter = NopPageFilter;
|
type ModulePageFilter = NopPageFilter;
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
emulator_modules.cmps(
|
emulator_modules.cmps(
|
||||||
@ -139,8 +144,12 @@ where
|
|||||||
|
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
emulator_modules.cmps(
|
emulator_modules.cmps(
|
||||||
@ -172,6 +181,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_unique_cmp_ids<ET, S>(
|
pub fn gen_unique_cmp_ids<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: Option<&mut S>,
|
state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -204,6 +214,7 @@ where
|
|||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
pub fn gen_hashed_cmp_ids<ET, S>(
|
pub fn gen_hashed_cmp_ids<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -298,6 +309,7 @@ impl CmpLogRoutinesModule {
|
|||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
fn gen_blocks_calls<ET, S>(
|
fn gen_blocks_calls<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -320,8 +332,6 @@ impl CmpLogRoutinesModule {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
|
|
||||||
if let Some(h) = emulator_modules.get::<Self>() {
|
if let Some(h) = emulator_modules.get::<Self>() {
|
||||||
#[allow(unused_mut)] // cfg dependent
|
#[allow(unused_mut)] // cfg dependent
|
||||||
let mut code = {
|
let mut code = {
|
||||||
@ -393,8 +403,12 @@ where
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
type ModulePageFilter = NopPageFilter;
|
type ModulePageFilter = NopPageFilter;
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
emulator_modules.blocks(
|
emulator_modules.blocks(
|
||||||
|
@ -13,6 +13,7 @@ use crate::{
|
|||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, NopAddressFilter},
|
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, NopAddressFilter},
|
||||||
qemu::Hook,
|
qemu::Hook,
|
||||||
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
|
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
|
||||||
@ -264,7 +265,7 @@ where
|
|||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
type ModulePageFilter = NopPageFilter;
|
type ModulePageFilter = NopPageFilter;
|
||||||
|
|
||||||
fn post_qemu_init<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
@ -276,15 +277,17 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if self.module_mapping.is_none() {
|
if self.module_mapping.is_none() {
|
||||||
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
|
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
|
||||||
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
|
|
||||||
let mut module_mapping: RangeMap<u64, (u16, String)> = RangeMap::new();
|
let mut module_mapping: RangeMap<u64, (u16, String)> = RangeMap::new();
|
||||||
|
|
||||||
#[expect(clippy::unnecessary_cast)] // for GuestAddr -> u64
|
#[expect(clippy::unnecessary_cast)] // for GuestAddr -> u64
|
||||||
@ -307,8 +310,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
assert!(
|
assert!(
|
||||||
@ -319,6 +326,7 @@ where
|
|||||||
|
|
||||||
fn post_exec<OT, ET>(
|
fn post_exec<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
@ -359,6 +367,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_unique_block_ids<ET, F, S>(
|
pub fn gen_unique_block_ids<ET, F, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: Option<&mut S>,
|
state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -409,6 +418,7 @@ where
|
|||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
pub fn gen_block_lengths<ET, F, S>(
|
pub fn gen_block_lengths<ET, F, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -432,6 +442,7 @@ pub fn gen_block_lengths<ET, F, S>(
|
|||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
pub fn exec_trace_block<ET, F, S>(
|
pub fn exec_trace_block<ET, F, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
|
@ -60,7 +60,7 @@ mod generators {
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
modules::{hash_me, AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter},
|
modules::{hash_me, AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter},
|
||||||
EmulatorModules,
|
EmulatorModules, Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn get_mask<const IS_CONST_MAP: bool, const MAP_SIZE: usize>() -> usize {
|
fn get_mask<const IS_CONST_MAP: bool, const MAP_SIZE: usize>() -> usize {
|
||||||
@ -77,7 +77,9 @@ mod generators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
pub fn gen_unique_edge_ids<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
pub fn gen_unique_edge_ids<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: Option<&mut S>,
|
state: Option<&mut S>,
|
||||||
src: GuestAddr,
|
src: GuestAddr,
|
||||||
@ -108,10 +110,7 @@ mod generators {
|
|||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
{
|
{
|
||||||
let paging_id = emulator_modules
|
let paging_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id());
|
||||||
.qemu()
|
|
||||||
.current_cpu()
|
|
||||||
.and_then(|cpu| cpu.current_paging_id());
|
|
||||||
|
|
||||||
if !module.must_instrument(src, paging_id)
|
if !module.must_instrument(src, paging_id)
|
||||||
&& !module.must_instrument(dest, paging_id)
|
&& !module.must_instrument(dest, paging_id)
|
||||||
@ -155,8 +154,10 @@ mod generators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
pub fn gen_hashed_edge_ids<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
pub fn gen_hashed_edge_ids<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
src: GuestAddr,
|
src: GuestAddr,
|
||||||
@ -179,10 +180,7 @@ mod generators {
|
|||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
{
|
{
|
||||||
let paging_id = emulator_modules
|
let paging_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id());
|
||||||
.qemu()
|
|
||||||
.current_cpu()
|
|
||||||
.and_then(|cpu| cpu.current_paging_id());
|
|
||||||
|
|
||||||
if !module.must_instrument(src, paging_id)
|
if !module.must_instrument(src, paging_id)
|
||||||
&& !module.must_instrument(dest, paging_id)
|
&& !module.must_instrument(dest, paging_id)
|
||||||
@ -210,8 +208,10 @@ mod generators {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::unnecessary_cast)]
|
#[expect(clippy::unnecessary_cast)]
|
||||||
|
#[allow(unused_variables)]
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
pub fn gen_hashed_block_ids<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
pub fn gen_hashed_block_ids<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -235,10 +235,7 @@ mod generators {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
{
|
{
|
||||||
let page_id = emulator_modules
|
let page_id = qemu.current_cpu().and_then(|cpu| cpu.current_paging_id());
|
||||||
.qemu()
|
|
||||||
.current_cpu()
|
|
||||||
.and_then(|cpu| cpu.current_paging_id());
|
|
||||||
|
|
||||||
if !module.must_instrument(pc, page_id) {
|
if !module.must_instrument(pc, page_id) {
|
||||||
return None;
|
return None;
|
||||||
|
@ -9,6 +9,7 @@ use libafl_qemu_sys::GuestPhysAddr;
|
|||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, PageFilter},
|
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, PageFilter},
|
||||||
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
@ -327,8 +328,12 @@ where
|
|||||||
type ModulePageFilter = PF;
|
type ModulePageFilter = PF;
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS;
|
const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS;
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if self.use_hitcounts {
|
if self.use_hitcounts {
|
||||||
|
@ -40,7 +40,7 @@ pub mod drcov;
|
|||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder};
|
pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder};
|
||||||
|
|
||||||
use crate::{emu::EmulatorModules, EmulatorHooks, Qemu};
|
use crate::{emu::EmulatorModules, Qemu, QemuParams};
|
||||||
|
|
||||||
/// A module for `libafl_qemu`.
|
/// A module for `libafl_qemu`.
|
||||||
// TODO remove 'static when specialization will be stable
|
// TODO remove 'static when specialization will be stable
|
||||||
@ -58,8 +58,14 @@ where
|
|||||||
/// Hook run **before** QEMU is initialized.
|
/// Hook run **before** QEMU is initialized.
|
||||||
/// This is always run when Emulator gets initialized, in any case.
|
/// This is always run when Emulator gets initialized, in any case.
|
||||||
/// Install here hooks that should be alive for the whole execution of the VM, even before QEMU gets initialized.
|
/// Install here hooks that should be alive for the whole execution of the VM, even before QEMU gets initialized.
|
||||||
fn pre_qemu_init<ET>(&self, _emulator_hooks: &mut EmulatorHooks<ET, S>)
|
///
|
||||||
where
|
/// It is also possible to edit QEMU parameters, just before QEMU gets initialized.
|
||||||
|
/// Thus, the module can modify options for QEMU just before it gets initialized.
|
||||||
|
fn pre_qemu_init<ET>(
|
||||||
|
&mut self,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_qemu_params: &mut QemuParams,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -67,7 +73,7 @@ where
|
|||||||
/// Hook run **after** QEMU is initialized.
|
/// Hook run **after** QEMU is initialized.
|
||||||
/// This is always run when Emulator gets initialized, in any case.
|
/// This is always run when Emulator gets initialized, in any case.
|
||||||
/// Install here hooks that should be alive for the whole execution of the VM, after QEMU gets initialized.
|
/// Install here hooks that should be alive for the whole execution of the VM, after QEMU gets initialized.
|
||||||
fn post_qemu_init<ET>(&self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
@ -77,8 +83,12 @@ where
|
|||||||
/// This call can be delayed to the point at which fuzzing is supposed to start.
|
/// This call can be delayed to the point at which fuzzing is supposed to start.
|
||||||
/// It is mostly used to avoid running hooks during VM initialization, either
|
/// It is mostly used to avoid running hooks during VM initialization, either
|
||||||
/// because it is useless or it would produce wrong results.
|
/// because it is useless or it would produce wrong results.
|
||||||
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -87,6 +97,7 @@ where
|
|||||||
/// On the first run, it is executed after [`Self::first_exec`].
|
/// On the first run, it is executed after [`Self::first_exec`].
|
||||||
fn pre_exec<ET>(
|
fn pre_exec<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
@ -98,6 +109,7 @@ where
|
|||||||
/// Run after a fuzzing run ends.
|
/// Run after a fuzzing run ends.
|
||||||
fn post_exec<OT, ET>(
|
fn post_exec<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
@ -146,20 +158,28 @@ where
|
|||||||
{
|
{
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool;
|
const HOOKS_DO_SIDE_EFFECTS: bool;
|
||||||
|
|
||||||
fn pre_qemu_init_all<ET>(&self, _emulator_hooks: &mut EmulatorHooks<ET, S>)
|
fn pre_qemu_init_all<ET>(
|
||||||
|
&mut self,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
qemu_params: &mut QemuParams,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<S>;
|
||||||
|
|
||||||
|
fn post_qemu_init_all<ET>(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>;
|
ET: EmulatorModuleTuple<S>;
|
||||||
|
|
||||||
fn post_qemu_init_all<ET>(&self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
fn first_exec_all<ET>(
|
||||||
where
|
&mut self,
|
||||||
ET: EmulatorModuleTuple<S>;
|
qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
fn first_exec_all<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, state: &mut S)
|
state: &mut S,
|
||||||
where
|
) where
|
||||||
ET: EmulatorModuleTuple<S>;
|
ET: EmulatorModuleTuple<S>;
|
||||||
|
|
||||||
fn pre_exec_all<ET>(
|
fn pre_exec_all<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
@ -168,6 +188,7 @@ where
|
|||||||
|
|
||||||
fn post_exec_all<OT, ET>(
|
fn post_exec_all<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
@ -195,30 +216,41 @@ where
|
|||||||
|
|
||||||
impl<S> EmulatorModuleTuple<S> for ()
|
impl<S> EmulatorModuleTuple<S> for ()
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
fn pre_qemu_init_all<ET>(&self, _emulator_hooks: &mut EmulatorHooks<ET, S>)
|
fn pre_qemu_init_all<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_qemu_params: &mut QemuParams,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_qemu_init_all<ET>(&self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init_all<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_exec_all<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec_all<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_all<ET>(
|
fn pre_exec_all<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
@ -229,6 +261,7 @@ where
|
|||||||
|
|
||||||
fn post_exec_all<OT, ET>(
|
fn post_exec_all<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
@ -258,44 +291,53 @@ where
|
|||||||
{
|
{
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
|
const HOOKS_DO_SIDE_EFFECTS: bool = Head::HOOKS_DO_SIDE_EFFECTS || Tail::HOOKS_DO_SIDE_EFFECTS;
|
||||||
|
|
||||||
fn pre_qemu_init_all<ET>(&self, emulator_hooks: &mut EmulatorHooks<ET, S>)
|
fn pre_qemu_init_all<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
qemu_params: &mut QemuParams,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.0.pre_qemu_init(emulator_hooks);
|
self.0.pre_qemu_init(emulator_modules, qemu_params);
|
||||||
self.1.pre_qemu_init_all(emulator_hooks);
|
self.1.pre_qemu_init_all(emulator_modules, qemu_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_qemu_init_all<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init_all<ET>(&mut self, qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.0.post_qemu_init(emulator_modules);
|
self.0.post_qemu_init(qemu, emulator_modules);
|
||||||
self.1.post_qemu_init_all(emulator_modules);
|
self.1.post_qemu_init_all(qemu, emulator_modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_exec_all<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, state: &mut S)
|
fn first_exec_all<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.0.first_exec(emulator_modules, state);
|
self.0.first_exec(qemu, emulator_modules, state);
|
||||||
self.1.first_exec_all(emulator_modules, state);
|
self.1.first_exec_all(qemu, emulator_modules, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_all<ET>(
|
fn pre_exec_all<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
) where
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.0.pre_exec(emulator_modules, state, input);
|
self.0.pre_exec(qemu, emulator_modules, state, input);
|
||||||
self.1.pre_exec_all(emulator_modules, state, input);
|
self.1.pre_exec_all(qemu, emulator_modules, state, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec_all<OT, ET>(
|
fn post_exec_all<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
@ -306,9 +348,9 @@ where
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
self.0
|
self.0
|
||||||
.post_exec(emulator_modules, state, input, observers, exit_kind);
|
.post_exec(qemu, emulator_modules, state, input, observers, exit_kind);
|
||||||
self.1
|
self.1
|
||||||
.post_exec_all(emulator_modules, state, input, observers, exit_kind);
|
.post_exec_all(qemu, emulator_modules, state, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn on_crash_all(&mut self) {
|
unsafe fn on_crash_all(&mut self) {
|
||||||
|
@ -16,9 +16,9 @@ use crate::{
|
|||||||
calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule,
|
calls::FullBacktraceCollector, snapshot::SnapshotModule, EmulatorModule,
|
||||||
EmulatorModuleTuple,
|
EmulatorModuleTuple,
|
||||||
},
|
},
|
||||||
qemu::{MemAccessInfo, QemuInitError},
|
qemu::MemAccessInfo,
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
Qemu, Regs,
|
Qemu, QemuParams, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO at some point, merge parts with libafl_frida
|
// TODO at some point, merge parts with libafl_frida
|
||||||
@ -176,8 +176,8 @@ impl core::fmt::Debug for AsanGiovese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsanGiovese {
|
impl AsanGiovese {
|
||||||
unsafe fn map_shadow() {
|
unsafe fn init(self: &mut Pin<Box<Self>>, qemu_hooks: QemuHooks) {
|
||||||
assert!(
|
assert_ne!(
|
||||||
libc::mmap(
|
libc::mmap(
|
||||||
HIGH_SHADOW_ADDR,
|
HIGH_SHADOW_ADDR,
|
||||||
HIGH_SHADOW_SIZE,
|
HIGH_SHADOW_SIZE,
|
||||||
@ -185,9 +185,10 @@ impl AsanGiovese {
|
|||||||
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
|
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
|
||||||
-1,
|
-1,
|
||||||
0
|
0
|
||||||
) != MAP_FAILED
|
),
|
||||||
|
MAP_FAILED
|
||||||
);
|
);
|
||||||
assert!(
|
assert_ne!(
|
||||||
libc::mmap(
|
libc::mmap(
|
||||||
LOW_SHADOW_ADDR,
|
LOW_SHADOW_ADDR,
|
||||||
LOW_SHADOW_SIZE,
|
LOW_SHADOW_SIZE,
|
||||||
@ -195,9 +196,10 @@ impl AsanGiovese {
|
|||||||
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
|
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
|
||||||
-1,
|
-1,
|
||||||
0
|
0
|
||||||
) != MAP_FAILED
|
),
|
||||||
|
MAP_FAILED
|
||||||
);
|
);
|
||||||
assert!(
|
assert_ne!(
|
||||||
libc::mmap(
|
libc::mmap(
|
||||||
GAP_SHADOW_ADDR,
|
GAP_SHADOW_ADDR,
|
||||||
GAP_SHADOW_SIZE,
|
GAP_SHADOW_SIZE,
|
||||||
@ -205,12 +207,15 @@ impl AsanGiovese {
|
|||||||
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
|
MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
|
||||||
-1,
|
-1,
|
||||||
0
|
0
|
||||||
) != MAP_FAILED
|
),
|
||||||
|
MAP_FAILED
|
||||||
);
|
);
|
||||||
|
|
||||||
|
qemu_hooks.add_pre_syscall_hook(self.as_mut(), Self::fake_syscall);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn new(qemu_hooks: QemuHooks) -> Pin<Box<Self>> {
|
fn new() -> 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(),
|
||||||
@ -219,9 +224,7 @@ impl AsanGiovese {
|
|||||||
saved_shadow: HashMap::default(),
|
saved_shadow: HashMap::default(),
|
||||||
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);
|
Box::pin(res)
|
||||||
qemu_hooks.add_pre_syscall_hook(boxed.as_mut(), Self::fake_syscall);
|
|
||||||
boxed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn fake_syscall(
|
extern "C" fn fake_syscall(
|
||||||
@ -255,7 +258,7 @@ impl AsanGiovese {
|
|||||||
Self::unpoison(qemu, a1, a2 as usize);
|
Self::unpoison(qemu, a1, a2 as usize);
|
||||||
}
|
}
|
||||||
QasanAction::IsPoison => {
|
QasanAction::IsPoison => {
|
||||||
if Self::is_invalid_access(qemu, a1, a2 as usize) {
|
if Self::is_invalid_access_n(qemu, a1, a2 as usize) {
|
||||||
r = 1;
|
r = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,51 +288,25 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_invalid_access_1(qemu: Qemu, addr: GuestAddr) -> bool {
|
pub fn is_invalid_access<const N: usize>(qemu: Qemu, addr: GuestAddr) -> bool {
|
||||||
unsafe {
|
const { assert!(N == 1 || N == 2 || N == 4 || N == 8) };
|
||||||
let h = qemu.g2h::<*const c_void>(addr) as isize;
|
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
|
||||||
let k = *shadow_addr as isize;
|
|
||||||
k != 0 && (h & 7).wrapping_add(1) > k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_invalid_access_2(qemu: Qemu, addr: GuestAddr) -> bool {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = qemu.g2h::<*const c_void>(addr) as isize;
|
let h = qemu.g2h::<*const c_void>(addr) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
let k = *shadow_addr as isize;
|
if N < 8 {
|
||||||
k != 0 && (h & 7).wrapping_add(2) > k
|
let k = *shadow_addr as isize;
|
||||||
}
|
k != 0 && (h & 7).wrapping_add(N as isize) > k
|
||||||
}
|
} else {
|
||||||
|
*shadow_addr != 0
|
||||||
#[inline]
|
}
|
||||||
#[must_use]
|
|
||||||
pub fn is_invalid_access_4(qemu: Qemu, addr: GuestAddr) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let h = qemu.g2h::<*const c_void>(addr) as isize;
|
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
|
||||||
let k = *shadow_addr as isize;
|
|
||||||
k != 0 && (h & 7).wrapping_add(4) > k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_invalid_access_8(qemu: Qemu, addr: GuestAddr) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let h = qemu.g2h::<*const c_void>(addr) as isize;
|
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
|
||||||
*shadow_addr != 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::cast_sign_loss)]
|
#[expect(clippy::cast_sign_loss)]
|
||||||
pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr, n: usize) -> bool {
|
pub fn is_invalid_access_n(qemu: Qemu, addr: GuestAddr, n: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return false;
|
return false;
|
||||||
@ -672,64 +649,6 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut ASAN_INITED: bool = false;
|
|
||||||
|
|
||||||
pub fn init_qemu_with_asan(
|
|
||||||
args: &mut Vec<String>,
|
|
||||||
env: &mut [(String, String)],
|
|
||||||
) -> Result<(Qemu, Pin<Box<AsanGiovese>>), QemuInitError> {
|
|
||||||
let current = env::current_exe().unwrap();
|
|
||||||
let asan_lib = fs::canonicalize(current)
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.join("libqasan.so");
|
|
||||||
let asan_lib = asan_lib
|
|
||||||
.to_str()
|
|
||||||
.expect("The path to the asan lib is invalid")
|
|
||||||
.to_string();
|
|
||||||
let add_asan =
|
|
||||||
|e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..];
|
|
||||||
|
|
||||||
// TODO: adapt since qemu does not take envp anymore as parameter
|
|
||||||
let mut added = false;
|
|
||||||
for (k, v) in &mut *env {
|
|
||||||
if k == "QEMU_SET_ENV" {
|
|
||||||
let mut new_v = vec![];
|
|
||||||
for e in v.split(',') {
|
|
||||||
if e.starts_with("LD_PRELOAD=") {
|
|
||||||
added = true;
|
|
||||||
new_v.push(add_asan(e));
|
|
||||||
} else {
|
|
||||||
new_v.push(e.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*v = new_v.join(",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i in 0..args.len() {
|
|
||||||
if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") {
|
|
||||||
added = true;
|
|
||||||
args[i + 1] = add_asan(&args[i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !added {
|
|
||||||
args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib);
|
|
||||||
args.insert(1, "-E".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
AsanGiovese::map_shadow();
|
|
||||||
ASAN_INITED = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let qemu = Qemu::init(args)?;
|
|
||||||
let rt = AsanGiovese::new(qemu.hooks());
|
|
||||||
|
|
||||||
Ok((qemu, rt))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum QemuAsanOptions {
|
pub enum QemuAsanOptions {
|
||||||
None,
|
None,
|
||||||
Snapshot,
|
Snapshot,
|
||||||
@ -741,6 +660,7 @@ pub type AsanChildModule = AsanModule;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AsanModule {
|
pub struct AsanModule {
|
||||||
|
env: Vec<(String, String)>,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
detect_leaks: bool,
|
detect_leaks: bool,
|
||||||
empty: bool,
|
empty: bool,
|
||||||
@ -750,25 +670,28 @@ pub struct AsanModule {
|
|||||||
|
|
||||||
impl AsanModule {
|
impl AsanModule {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn default(rt: Pin<Box<AsanGiovese>>) -> Self {
|
pub fn default(env: &[(String, String)]) -> Self {
|
||||||
Self::new(rt, StdAddressFilter::default(), &QemuAsanOptions::Snapshot)
|
Self::new(StdAddressFilter::default(), &QemuAsanOptions::Snapshot, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mut rt: Pin<Box<AsanGiovese>>,
|
|
||||||
filter: StdAddressFilter,
|
filter: StdAddressFilter,
|
||||||
options: &QemuAsanOptions,
|
options: &QemuAsanOptions,
|
||||||
|
env: &[(String, String)],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_qemu_with_asan(...) instead of just Qemu::init(...)");
|
|
||||||
let (snapshot, detect_leaks) = match options {
|
let (snapshot, detect_leaks) = match options {
|
||||||
QemuAsanOptions::None => (false, false),
|
QemuAsanOptions::None => (false, false),
|
||||||
QemuAsanOptions::Snapshot => (true, false),
|
QemuAsanOptions::Snapshot => (true, false),
|
||||||
QemuAsanOptions::DetectLeaks => (false, true),
|
QemuAsanOptions::DetectLeaks => (false, true),
|
||||||
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
|
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut rt = AsanGiovese::new();
|
||||||
rt.set_snapshot_shadow(snapshot);
|
rt.set_snapshot_shadow(snapshot);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
env: env.to_vec(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
detect_leaks,
|
detect_leaks,
|
||||||
empty: true,
|
empty: true,
|
||||||
@ -779,21 +702,24 @@ impl AsanModule {
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_error_callback(
|
pub fn with_error_callback(
|
||||||
mut rt: Pin<Box<AsanGiovese>>,
|
|
||||||
filter: StdAddressFilter,
|
filter: StdAddressFilter,
|
||||||
error_callback: AsanErrorCallback,
|
error_callback: AsanErrorCallback,
|
||||||
options: &QemuAsanOptions,
|
options: &QemuAsanOptions,
|
||||||
|
env: &[(String, String)],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_qemu_with_asan(...) instead of just Qemu::init(...)");
|
|
||||||
let (snapshot, detect_leaks) = match options {
|
let (snapshot, detect_leaks) = match options {
|
||||||
QemuAsanOptions::None => (false, false),
|
QemuAsanOptions::None => (false, false),
|
||||||
QemuAsanOptions::Snapshot => (true, false),
|
QemuAsanOptions::Snapshot => (true, false),
|
||||||
QemuAsanOptions::DetectLeaks => (false, true),
|
QemuAsanOptions::DetectLeaks => (false, true),
|
||||||
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
|
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut rt = AsanGiovese::new();
|
||||||
rt.set_snapshot_shadow(snapshot);
|
rt.set_snapshot_shadow(snapshot);
|
||||||
rt.set_error_callback(error_callback);
|
rt.set_error_callback(error_callback);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
env: env.to_vec(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
detect_leaks,
|
detect_leaks,
|
||||||
empty: true,
|
empty: true,
|
||||||
@ -806,15 +732,15 @@ impl AsanModule {
|
|||||||
/// The `ASan` error report accesses [`FullBacktraceCollector`]
|
/// The `ASan` error report accesses [`FullBacktraceCollector`]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub unsafe fn with_asan_report(
|
pub unsafe fn with_asan_report(
|
||||||
rt: Pin<Box<AsanGiovese>>,
|
|
||||||
filter: StdAddressFilter,
|
filter: StdAddressFilter,
|
||||||
options: &QemuAsanOptions,
|
options: &QemuAsanOptions,
|
||||||
|
env: &[(String, String)],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::with_error_callback(
|
Self::with_error_callback(
|
||||||
rt,
|
|
||||||
filter,
|
filter,
|
||||||
Box::new(|rt, qemu, pc, err| unsafe { asan_report(rt, qemu, pc, &err) }),
|
Box::new(|rt, qemu, pc, err| unsafe { asan_report(rt, qemu, pc, &err) }),
|
||||||
options,
|
options,
|
||||||
|
env,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -842,66 +768,30 @@ impl AsanModule {
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool {
|
pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool {
|
||||||
AsanGiovese::is_invalid_access(qemu, addr, size)
|
AsanGiovese::is_invalid_access_n(qemu, addr, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
pub fn read<const N: usize>(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) {
|
if self.enabled() && AsanGiovese::is_invalid_access::<N>(qemu, addr) {
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 1));
|
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, N));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_2(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_2(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_4(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_4(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_8(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_8(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 8));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
pub fn read_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) {
|
if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) {
|
||||||
self.rt
|
self.rt
|
||||||
.report_or_crash(qemu, pc, AsanError::Read(addr, size));
|
.report_or_crash(qemu, pc, AsanError::Read(addr, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
pub fn write<const N: usize>(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) {
|
if self.enabled() && AsanGiovese::is_invalid_access::<N>(qemu, addr) {
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 1));
|
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, N));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_2(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_2(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_4(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_4(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_8(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_8(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 8));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
pub fn write_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) {
|
if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) {
|
||||||
self.rt
|
self.rt
|
||||||
.report_or_crash(qemu, pc, AsanError::Write(addr, size));
|
.report_or_crash(qemu, pc, AsanError::Write(addr, size));
|
||||||
}
|
}
|
||||||
@ -927,47 +817,108 @@ where
|
|||||||
type ModuleAddressFilter = StdAddressFilter;
|
type ModuleAddressFilter = StdAddressFilter;
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
fn post_qemu_init<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
fn pre_qemu_init<ET>(
|
||||||
|
&mut self,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
qemu_params: &mut QemuParams,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
let mut args: Vec<String> = qemu_params.to_cli();
|
||||||
|
|
||||||
|
let current = env::current_exe().unwrap();
|
||||||
|
let asan_lib = fs::canonicalize(current)
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("libqasan.so");
|
||||||
|
let asan_lib = asan_lib
|
||||||
|
.to_str()
|
||||||
|
.expect("The path to the asan lib is invalid")
|
||||||
|
.to_string();
|
||||||
|
let add_asan =
|
||||||
|
|e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..];
|
||||||
|
|
||||||
|
// TODO: adapt since qemu does not take envp anymore as parameter
|
||||||
|
let mut added = false;
|
||||||
|
for (k, v) in &mut self.env {
|
||||||
|
if k == "QEMU_SET_ENV" {
|
||||||
|
let mut new_v = vec![];
|
||||||
|
for e in v.split(',') {
|
||||||
|
if e.starts_with("LD_PRELOAD=") {
|
||||||
|
added = true;
|
||||||
|
new_v.push(add_asan(e));
|
||||||
|
} else {
|
||||||
|
new_v.push(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*v = new_v.join(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 0..args.len() {
|
||||||
|
if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") {
|
||||||
|
added = true;
|
||||||
|
args[i + 1] = add_asan(&args[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !added {
|
||||||
|
args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib);
|
||||||
|
args.insert(1, "-E".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
AsanGiovese::init(&mut self.rt, emulator_modules.hooks().qemu_hooks());
|
||||||
|
}
|
||||||
|
|
||||||
|
*qemu_params = QemuParams::Cli(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
emulator_modules.syscalls(Hook::Function(qasan_fake_syscall::<ET, S>));
|
emulator_modules.pre_syscalls(Hook::Function(qasan_fake_syscall::<ET, S>));
|
||||||
|
|
||||||
if self.rt.error_callback.is_some() {
|
if self.rt.error_callback.is_some() {
|
||||||
emulator_modules.crash_function(oncrash_asan::<ET, S>);
|
emulator_modules.crash_function(oncrash_asan::<ET, S>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
emulator_modules.reads(
|
emulator_modules.reads(
|
||||||
Hook::Function(gen_readwrite_asan::<ET, S>),
|
Hook::Function(gen_readwrite_asan::<ET, S>),
|
||||||
Hook::Function(trace_read1_asan::<ET, S>),
|
Hook::Function(trace_read_asan::<ET, S, 1>),
|
||||||
Hook::Function(trace_read2_asan::<ET, S>),
|
Hook::Function(trace_read_asan::<ET, S, 2>),
|
||||||
Hook::Function(trace_read4_asan::<ET, S>),
|
Hook::Function(trace_read_asan::<ET, S, 4>),
|
||||||
Hook::Function(trace_read8_asan::<ET, S>),
|
Hook::Function(trace_read_asan::<ET, S, 8>),
|
||||||
Hook::Function(trace_read_n_asan::<ET, S>),
|
Hook::Function(trace_read_n_asan::<ET, S>),
|
||||||
);
|
);
|
||||||
|
|
||||||
if emulator_modules.get::<SnapshotModule>().is_none() {
|
if emulator_modules.get::<SnapshotModule>().is_none() {
|
||||||
emulator_modules.writes(
|
emulator_modules.writes(
|
||||||
Hook::Function(gen_readwrite_asan::<ET, S>),
|
Hook::Function(gen_readwrite_asan::<ET, S>),
|
||||||
Hook::Function(trace_write1_asan::<ET, S>),
|
Hook::Function(trace_write_asan::<ET, S, 1>),
|
||||||
Hook::Function(trace_write2_asan::<ET, S>),
|
Hook::Function(trace_write_asan::<ET, S, 2>),
|
||||||
Hook::Function(trace_write4_asan::<ET, S>),
|
Hook::Function(trace_write_asan::<ET, S, 4>),
|
||||||
Hook::Function(trace_write8_asan::<ET, S>),
|
Hook::Function(trace_write_asan::<ET, S, 8>),
|
||||||
Hook::Function(trace_write_n_asan::<ET, S>),
|
Hook::Function(trace_write_n_asan::<ET, S>),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// track writes for both modules as opt
|
// track writes for both modules as opt
|
||||||
emulator_modules.writes(
|
emulator_modules.writes(
|
||||||
Hook::Function(gen_write_asan_snapshot::<ET, S>),
|
Hook::Function(gen_write_asan_snapshot::<ET, S>),
|
||||||
Hook::Function(trace_write1_asan_snapshot::<ET, S>),
|
Hook::Function(trace_write_asan_snapshot::<ET, S, 1>),
|
||||||
Hook::Function(trace_write2_asan_snapshot::<ET, S>),
|
Hook::Function(trace_write_asan_snapshot::<ET, S, 2>),
|
||||||
Hook::Function(trace_write4_asan_snapshot::<ET, S>),
|
Hook::Function(trace_write_asan_snapshot::<ET, S, 4>),
|
||||||
Hook::Function(trace_write8_asan_snapshot::<ET, S>),
|
Hook::Function(trace_write_asan_snapshot::<ET, S, 8>),
|
||||||
Hook::Function(trace_write_n_asan_snapshot::<ET, S>),
|
Hook::Function(trace_write_n_asan_snapshot::<ET, S>),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -975,21 +926,23 @@ where
|
|||||||
|
|
||||||
fn pre_exec<ET>(
|
fn pre_exec<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
) where
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if self.empty {
|
if self.empty {
|
||||||
self.rt.snapshot(emulator_modules.qemu());
|
self.rt.snapshot(qemu);
|
||||||
self.empty = false;
|
self.empty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec<OT, ET>(
|
fn post_exec<OT, ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
@ -998,7 +951,7 @@ where
|
|||||||
OT: ObserversTuple<S::Input, S>,
|
OT: ObserversTuple<S::Input, S>,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if self.reset(emulator_modules.qemu()) == AsanRollback::HasLeaks {
|
if self.reset(qemu) == AsanRollback::HasLeaks {
|
||||||
*exit_kind = ExitKind::Crash;
|
*exit_kind = ExitKind::Crash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1012,18 +965,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn oncrash_asan<ET, S>(emulator_modules: &mut EmulatorModules<ET, S>, target_sig: i32)
|
pub fn oncrash_asan<ET, S>(
|
||||||
where
|
qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
target_sig: i32,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
{
|
{
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().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<ET, S>(
|
pub fn gen_readwrite_asan<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -1042,7 +998,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read1_asan<ET, S>(
|
pub fn trace_read_asan<ET, S, const N: usize>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
@ -1051,54 +1008,12 @@ pub fn trace_read1_asan<ET, S>(
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
{
|
{
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
||||||
h.read_1(qemu, id as GuestAddr, addr);
|
h.read::<N>(qemu, id as GuestAddr, addr);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_read2_asan<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.read_2(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_read4_asan<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.read_4(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_read8_asan<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.read_8(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read_n_asan<ET, S>(
|
pub fn trace_read_n_asan<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
@ -1108,12 +1023,12 @@ pub fn trace_read_n_asan<ET, S>(
|
|||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().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<ET, S>(
|
pub fn trace_write_asan<ET, S, const N: usize>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
@ -1122,54 +1037,12 @@ pub fn trace_write1_asan<ET, S>(
|
|||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
||||||
h.write_1(qemu, id as GuestAddr, addr);
|
h.write::<N>(qemu, id as GuestAddr, addr);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_write2_asan<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.write_2(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_write4_asan<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.write_4(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_write8_asan<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.write_8(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write_n_asan<ET, S>(
|
pub fn trace_write_n_asan<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
@ -1179,12 +1052,12 @@ pub fn trace_write_n_asan<ET, S>(
|
|||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().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<ET, S>(
|
pub fn gen_write_asan_snapshot<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -1203,7 +1076,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write1_asan_snapshot<ET, S>(
|
pub fn trace_write_asan_snapshot<ET, S, const N: usize>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
@ -1213,69 +1087,15 @@ pub fn trace_write1_asan_snapshot<ET, S>(
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
||||||
h.write_1(qemu, id as GuestAddr, addr);
|
h.write::<N>(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
|
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
|
||||||
h.access(addr, 1);
|
h.access(addr, N);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_write2_asan_snapshot<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
if id != 0 {
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.write_2(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
|
|
||||||
h.access(addr, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_write4_asan_snapshot<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
if id != 0 {
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.write_4(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
|
|
||||||
h.access(addr, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trace_write8_asan_snapshot<ET, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
id: u64,
|
|
||||||
addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
if id != 0 {
|
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().unwrap();
|
|
||||||
h.write_8(qemu, id as GuestAddr, addr);
|
|
||||||
}
|
|
||||||
let h = emulator_modules.get_mut::<SnapshotModule>().unwrap();
|
|
||||||
h.access(addr, 8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write_n_asan_snapshot<ET, S>(
|
pub fn trace_write_n_asan_snapshot<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
id: u64,
|
id: u64,
|
||||||
@ -1286,7 +1106,6 @@ pub fn trace_write_n_asan_snapshot<ET, S>(
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().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);
|
||||||
}
|
}
|
||||||
@ -1296,6 +1115,7 @@ pub fn trace_write_n_asan_snapshot<ET, S>(
|
|||||||
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
#[expect(clippy::too_many_arguments)]
|
||||||
pub fn qasan_fake_syscall<ET, S>(
|
pub fn qasan_fake_syscall<ET, S>(
|
||||||
|
qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
sys_num: i32,
|
sys_num: i32,
|
||||||
@ -1313,7 +1133,6 @@ where
|
|||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if sys_num == QASAN_FAKESYS_NR {
|
if sys_num == QASAN_FAKESYS_NR {
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let h = emulator_modules.get_mut::<AsanModule>().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 => {
|
||||||
|
@ -15,86 +15,11 @@ use crate::sys::libafl_tcg_gen_asan;
|
|||||||
use crate::{
|
use crate::{
|
||||||
emu::EmulatorModules,
|
emu::EmulatorModules,
|
||||||
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter},
|
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, StdAddressFilter},
|
||||||
qemu::{Hook, MemAccessInfo, Qemu, QemuInitError},
|
qemu::{Hook, MemAccessInfo, Qemu},
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
|
QemuParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
static mut ASAN_GUEST_INITED: bool = false;
|
|
||||||
|
|
||||||
pub fn init_qemu_with_asan_guest(
|
|
||||||
args: &mut Vec<String>,
|
|
||||||
env: &mut [(String, String)],
|
|
||||||
) -> Result<(Qemu, String), QemuInitError> {
|
|
||||||
let current = env::current_exe().unwrap();
|
|
||||||
let asan_lib = fs::canonicalize(current)
|
|
||||||
.unwrap()
|
|
||||||
.parent()
|
|
||||||
.unwrap()
|
|
||||||
.join("libgasan.so");
|
|
||||||
|
|
||||||
let asan_lib = env::var_os("CUSTOM_ASAN_PATH")
|
|
||||||
.map_or(asan_lib, |x| PathBuf::from(x.to_string_lossy().to_string()));
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
asan_lib.as_path().exists(),
|
|
||||||
"The ASAN library doesn't exist: {asan_lib:#?}"
|
|
||||||
);
|
|
||||||
|
|
||||||
let asan_lib = asan_lib
|
|
||||||
.to_str()
|
|
||||||
.expect("The path to the asan lib is invalid")
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
println!("Loading ASAN: {asan_lib:}");
|
|
||||||
|
|
||||||
let add_asan =
|
|
||||||
|e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..];
|
|
||||||
|
|
||||||
let mut added = false;
|
|
||||||
for (k, v) in &mut *env {
|
|
||||||
if k == "QEMU_SET_ENV" {
|
|
||||||
let mut new_v = vec![];
|
|
||||||
for e in v.split(',') {
|
|
||||||
if e.starts_with("LD_PRELOAD=") {
|
|
||||||
added = true;
|
|
||||||
new_v.push(add_asan(e));
|
|
||||||
} else {
|
|
||||||
new_v.push(e.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*v = new_v.join(",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i in 0..args.len() {
|
|
||||||
if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") {
|
|
||||||
added = true;
|
|
||||||
args[i + 1] = add_asan(&args[i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !added {
|
|
||||||
args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib);
|
|
||||||
args.insert(1, "-E".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if env::var("QASAN_DEBUG").is_ok() {
|
|
||||||
args.push("-E".into());
|
|
||||||
args.push("QASAN_DEBUG=1".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if env::var("QASAN_LOG").is_ok() {
|
|
||||||
args.push("-E".into());
|
|
||||||
args.push("QASAN_LOG=1".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ASAN_GUEST_INITED = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let emu = Qemu::init(args)?;
|
|
||||||
Ok((emu, asan_lib))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct QemuAsanGuestMapping {
|
struct QemuAsanGuestMapping {
|
||||||
start: GuestAddr,
|
start: GuestAddr,
|
||||||
@ -119,8 +44,10 @@ impl From<&MapInfo> for QemuAsanGuestMapping {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AsanGuestModule<F> {
|
pub struct AsanGuestModule<F> {
|
||||||
|
env: Vec<(String, String)>,
|
||||||
filter: F,
|
filter: F,
|
||||||
mappings: Vec<QemuAsanGuestMapping>,
|
mappings: Option<Vec<QemuAsanGuestMapping>>,
|
||||||
|
asan_lib: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
@ -152,8 +79,8 @@ impl<F> AsanGuestModule<F> {
|
|||||||
|
|
||||||
impl AsanGuestModule<StdAddressFilter> {
|
impl AsanGuestModule<StdAddressFilter> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn default(qemu: Qemu, asan: &str) -> Self {
|
pub fn default(env: &[(String, String)]) -> Self {
|
||||||
Self::new(qemu, asan, StdAddressFilter::default())
|
Self::new(env, StdAddressFilter::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +89,184 @@ where
|
|||||||
F: AddressFilter,
|
F: AddressFilter,
|
||||||
{
|
{
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(qemu: Qemu, asan: &str, filter: F) -> Self {
|
pub fn new(env: &[(String, String)], filter: F) -> Self {
|
||||||
|
Self {
|
||||||
|
env: env.to_vec(),
|
||||||
|
filter,
|
||||||
|
mappings: None,
|
||||||
|
asan_lib: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn must_instrument(&self, addr: GuestAddr) -> bool {
|
||||||
|
self.filter.allowed(&addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
|
fn gen_readwrite_guest_asan<ET, F, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
pc: GuestAddr,
|
||||||
|
addr: *mut TCGTemp,
|
||||||
|
info: MemAccessInfo,
|
||||||
|
) -> Option<u64>
|
||||||
|
where
|
||||||
|
F: AddressFilter,
|
||||||
|
S: Unpin + UsesInput,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
let h = emulator_modules.get_mut::<AsanGuestModule<F>>().unwrap();
|
||||||
|
if !h.must_instrument(pc) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't sanitize the sanitizer! */
|
||||||
|
unsafe {
|
||||||
|
if h.mappings
|
||||||
|
.as_mut()
|
||||||
|
.unwrap_unchecked()
|
||||||
|
.iter()
|
||||||
|
.any(|m| m.start <= pc && pc < m.end)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = info.size();
|
||||||
|
|
||||||
|
/* TODO - If our size is > 8 then do things via a runtime callback */
|
||||||
|
assert!(size <= 8, "I shouldn't be here!");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libafl_tcg_gen_asan(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "clippy")]
|
||||||
|
#[expect(unused_variables)]
|
||||||
|
unsafe fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize) {}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
|
fn guest_trace_error_asan<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
_id: u64,
|
||||||
|
_addr: GuestAddr,
|
||||||
|
) where
|
||||||
|
S: Unpin + UsesInput,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
panic!("I really shouldn't be here");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
|
fn guest_trace_error_n_asan<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
_id: u64,
|
||||||
|
_addr: GuestAddr,
|
||||||
|
_n: usize,
|
||||||
|
) where
|
||||||
|
S: Unpin + UsesInput,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
panic!("I really shouldn't be here either");
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, S> EmulatorModule<S> for AsanGuestModule<F>
|
||||||
|
where
|
||||||
|
F: AddressFilter,
|
||||||
|
S: Unpin + UsesInput,
|
||||||
|
{
|
||||||
|
type ModuleAddressFilter = F;
|
||||||
|
|
||||||
|
fn pre_qemu_init<ET>(
|
||||||
|
&mut self,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
qemu_params: &mut QemuParams,
|
||||||
|
) where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
let mut args = qemu_params.to_cli();
|
||||||
|
|
||||||
|
let current = env::current_exe().unwrap();
|
||||||
|
let asan_lib = fs::canonicalize(current)
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("libgasan.so");
|
||||||
|
|
||||||
|
let asan_lib = env::var_os("CUSTOM_ASAN_PATH")
|
||||||
|
.map_or(asan_lib, |x| PathBuf::from(x.to_string_lossy().to_string()));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
asan_lib.as_path().exists(),
|
||||||
|
"The ASAN library doesn't exist: {asan_lib:#?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let asan_lib = asan_lib
|
||||||
|
.to_str()
|
||||||
|
.expect("The path to the asan lib is invalid")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
println!("Loading ASAN: {asan_lib:}");
|
||||||
|
|
||||||
|
let add_asan =
|
||||||
|
|e: &str| "LD_PRELOAD=".to_string() + &asan_lib + " " + &e["LD_PRELOAD=".len()..];
|
||||||
|
|
||||||
|
let mut added = false;
|
||||||
|
for (k, v) in &mut self.env {
|
||||||
|
if k == "QEMU_SET_ENV" {
|
||||||
|
let mut new_v = vec![];
|
||||||
|
for e in v.split(',') {
|
||||||
|
if e.starts_with("LD_PRELOAD=") {
|
||||||
|
added = true;
|
||||||
|
new_v.push(add_asan(e));
|
||||||
|
} else {
|
||||||
|
new_v.push(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*v = new_v.join(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 0..args.len() {
|
||||||
|
if args[i] == "-E" && i + 1 < args.len() && args[i + 1].starts_with("LD_PRELOAD=") {
|
||||||
|
added = true;
|
||||||
|
args[i + 1] = add_asan(&args[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !added {
|
||||||
|
args.insert(1, "LD_PRELOAD=".to_string() + &asan_lib);
|
||||||
|
args.insert(1, "-E".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if env::var("QASAN_DEBUG").is_ok() {
|
||||||
|
args.push("-E".into());
|
||||||
|
args.push("QASAN_DEBUG=1".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if env::var("QASAN_LOG").is_ok() {
|
||||||
|
args.push("-E".into());
|
||||||
|
args.push("QASAN_LOG=1".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
*qemu_params = QemuParams::Cli(args);
|
||||||
|
|
||||||
|
self.asan_lib = Some(asan_lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_qemu_init<ET>(&mut self, qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
for mapping in qemu.mappings() {
|
for mapping in qemu.mappings() {
|
||||||
println!("mapping: {mapping:#?}");
|
println!("mapping: {mapping:#?}");
|
||||||
}
|
}
|
||||||
@ -188,98 +292,21 @@ where
|
|||||||
|
|
||||||
let mappings = mappings
|
let mappings = mappings
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|m| m.path == asan)
|
.filter(|m| &m.path == self.asan_lib.as_ref().unwrap())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<QemuAsanGuestMapping>>();
|
.collect::<Vec<QemuAsanGuestMapping>>();
|
||||||
|
|
||||||
for mapping in &mappings {
|
for mapping in &mappings {
|
||||||
println!("asan mapping: {mapping:#?}");
|
println!("asan mapping: {mapping:#?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Self { filter, mappings }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
fn first_exec<ET>(
|
||||||
pub fn must_instrument(&self, addr: GuestAddr) -> bool {
|
&mut self,
|
||||||
self.filter.allowed(&addr)
|
_qemu: Qemu,
|
||||||
}
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
}
|
_state: &mut S,
|
||||||
|
) where
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
|
||||||
fn gen_readwrite_guest_asan<ET, F, S>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
pc: GuestAddr,
|
|
||||||
addr: *mut TCGTemp,
|
|
||||||
info: MemAccessInfo,
|
|
||||||
) -> Option<u64>
|
|
||||||
where
|
|
||||||
F: AddressFilter,
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
let h = emulator_modules.get_mut::<AsanGuestModule<F>>().unwrap();
|
|
||||||
if !h.must_instrument(pc) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Don't sanitize the sanitizer! */
|
|
||||||
if h.mappings.iter().any(|m| m.start <= pc && pc < m.end) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let size = info.size();
|
|
||||||
|
|
||||||
/* TODO - If our size is > 8 then do things via a runtime callback */
|
|
||||||
assert!(size <= 8, "I shouldn't be here!");
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
libafl_tcg_gen_asan(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "clippy")]
|
|
||||||
#[expect(unused_variables)]
|
|
||||||
unsafe fn libafl_tcg_gen_asan(addr: *mut TCGTemp, size: usize) {}
|
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
|
||||||
fn guest_trace_error_asan<ET, S>(
|
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
_id: u64,
|
|
||||||
_addr: GuestAddr,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
panic!("I really shouldn't be here");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
|
||||||
fn guest_trace_error_n_asan<ET, S>(
|
|
||||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
_id: u64,
|
|
||||||
_addr: GuestAddr,
|
|
||||||
_n: usize,
|
|
||||||
) where
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
panic!("I really shouldn't be here either");
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F, S> EmulatorModule<S> for AsanGuestModule<F>
|
|
||||||
where
|
|
||||||
F: AddressFilter,
|
|
||||||
S: Unpin + UsesInput,
|
|
||||||
{
|
|
||||||
type ModuleAddressFilter = F;
|
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
|
||||||
where
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
{
|
{
|
||||||
|
@ -211,12 +211,15 @@ impl InjectionModule {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_call_check<ET, S>(emulator_modules: &mut EmulatorModules<ET, S>, id: usize, parameter: u8)
|
fn on_call_check<ET, S>(
|
||||||
where
|
qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
id: usize,
|
||||||
|
parameter: u8,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
S: Unpin + UsesInput,
|
S: Unpin + UsesInput,
|
||||||
{
|
{
|
||||||
let qemu = emulator_modules.qemu();
|
|
||||||
let reg: GuestAddr = qemu
|
let reg: GuestAddr = qemu
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -262,18 +265,21 @@ where
|
|||||||
{
|
{
|
||||||
type ModuleAddressFilter = NopAddressFilter;
|
type ModuleAddressFilter = NopAddressFilter;
|
||||||
|
|
||||||
fn post_qemu_init<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
emulator_modules.syscalls(Hook::Function(syscall_hook::<ET, S>));
|
emulator_modules.pre_syscalls(Hook::Function(syscall_hook::<ET, S>));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
fn first_exec<ET>(
|
||||||
where
|
&mut self,
|
||||||
|
qemu: Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: &mut S,
|
||||||
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
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() {
|
||||||
@ -324,8 +330,8 @@ where
|
|||||||
for hook_addr in hook_addrs {
|
for hook_addr in hook_addrs {
|
||||||
emulator_modules.instructions(
|
emulator_modules.instructions(
|
||||||
hook_addr,
|
hook_addr,
|
||||||
Hook::Closure(Box::new(move |hooks, _state, _guest_addr| {
|
Hook::Closure(Box::new(move |qemu, hooks, _state, _guest_addr| {
|
||||||
Self::on_call_check(hooks, id, param);
|
Self::on_call_check(qemu, hooks, id, param);
|
||||||
})),
|
})),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
@ -347,6 +353,7 @@ where
|
|||||||
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
#[allow(clippy::needless_pass_by_value)] // no longer a problem with nightly
|
||||||
fn syscall_hook<ET, S>(
|
fn syscall_hook<ET, S>(
|
||||||
// Our instantiated [`EmulatorModules`]
|
// Our instantiated [`EmulatorModules`]
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
// Syscall number
|
// Syscall number
|
||||||
|
@ -11,9 +11,9 @@ pub use snapshot::{IntervalSnapshotFilter, SnapshotModule};
|
|||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub mod asan;
|
pub mod asan;
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub use asan::{init_qemu_with_asan, AsanModule};
|
pub use asan::AsanModule;
|
||||||
|
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub mod asan_guest;
|
pub mod asan_guest;
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub use asan_guest::{init_qemu_with_asan_guest, AsanGuestModule};
|
pub use asan_guest::AsanGuestModule;
|
||||||
|
@ -680,7 +680,7 @@ where
|
|||||||
{
|
{
|
||||||
type ModuleAddressFilter = NopAddressFilter;
|
type ModuleAddressFilter = NopAddressFilter;
|
||||||
|
|
||||||
fn post_qemu_init<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
@ -697,23 +697,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !self.accurate_unmap {
|
if !self.accurate_unmap {
|
||||||
emulator_modules.syscalls(Hook::Function(filter_mmap_snapshot::<ET, S>));
|
emulator_modules.pre_syscalls(Hook::Function(filter_mmap_snapshot::<ET, S>));
|
||||||
}
|
}
|
||||||
emulator_modules.after_syscalls(Hook::Function(trace_mmap_snapshot::<ET, S>));
|
emulator_modules.post_syscalls(Hook::Function(trace_mmap_snapshot::<ET, S>));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec<ET>(
|
fn pre_exec<ET>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
qemu: Qemu,
|
||||||
|
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
) where
|
) where
|
||||||
ET: EmulatorModuleTuple<S>,
|
ET: EmulatorModuleTuple<S>,
|
||||||
{
|
{
|
||||||
if self.empty {
|
if self.empty {
|
||||||
self.snapshot(emulator_modules.qemu());
|
self.snapshot(qemu);
|
||||||
} else {
|
} else {
|
||||||
self.reset(emulator_modules.qemu());
|
self.reset(qemu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,6 +728,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write_snapshot<ET, S, const SIZE: usize>(
|
pub fn trace_write_snapshot<ET, S, const SIZE: usize>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
_id: u64,
|
_id: u64,
|
||||||
@ -740,6 +742,7 @@ pub fn trace_write_snapshot<ET, S, const SIZE: usize>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write_n_snapshot<ET, S>(
|
pub fn trace_write_n_snapshot<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
_id: u64,
|
_id: u64,
|
||||||
@ -755,6 +758,7 @@ pub fn trace_write_n_snapshot<ET, S>(
|
|||||||
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
#[expect(clippy::too_many_arguments)]
|
||||||
pub fn filter_mmap_snapshot<ET, S>(
|
pub fn filter_mmap_snapshot<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
sys_num: i32,
|
sys_num: i32,
|
||||||
@ -782,6 +786,7 @@ where
|
|||||||
|
|
||||||
#[expect(non_upper_case_globals, clippy::too_many_arguments)]
|
#[expect(non_upper_case_globals, clippy::too_many_arguments)]
|
||||||
pub fn trace_mmap_snapshot<ET, S>(
|
pub fn trace_mmap_snapshot<ET, S>(
|
||||||
|
_qemu: Qemu,
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
_state: Option<&mut S>,
|
_state: Option<&mut S>,
|
||||||
result: GuestAddr,
|
result: GuestAddr,
|
||||||
|
@ -2,20 +2,13 @@ use core::{
|
|||||||
fmt,
|
fmt,
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::path::{Path, PathBuf};
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::OnceLock,
|
|
||||||
};
|
|
||||||
|
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
use libafl_derive;
|
use libafl_derive;
|
||||||
use strum_macros;
|
use strum_macros;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
use crate::{Qemu, QemuInitError};
|
|
||||||
|
|
||||||
pub(super) static QEMU_CONFIG: OnceLock<QemuConfig> = OnceLock::new();
|
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
#[derive(Debug, strum_macros::Display, Clone)]
|
#[derive(Debug, strum_macros::Display, Clone)]
|
||||||
#[strum(prefix = "-accel ", serialize_all = "lowercase")]
|
#[strum(prefix = "-accel ", serialize_all = "lowercase")]
|
||||||
@ -304,15 +297,6 @@ impl<R: AsRef<Path>> From<R> for Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, libafl_derive::Display, TypedBuilder, Getters)]
|
#[derive(Debug, Clone, libafl_derive::Display, TypedBuilder, Getters)]
|
||||||
#[builder(build_method(into = Result<Qemu, QemuInitError>), builder_method(vis = "pub(crate)",
|
|
||||||
doc = "Since Qemu is a zero sized struct, this is not a completely standard builder pattern. \
|
|
||||||
The Qemu configuration is not stored in the Qemu struct after build() but in QEMU_CONFIG \
|
|
||||||
Therefore, to use the derived builder and avoid boilerplate a builder for QemuConfig is \
|
|
||||||
derived. \
|
|
||||||
The QemuConfig::builder is called in Qemu::builder() which is the only place where it should \
|
|
||||||
be called, in this way the one to one matching of Qemu and QemuConfig is enforced. Therefore \
|
|
||||||
its visibility is pub(crate)"))]
|
|
||||||
#[getset(get = "pub")]
|
|
||||||
pub struct QemuConfig {
|
pub struct QemuConfig {
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
#[builder(default, setter(strip_option))]
|
#[builder(default, setter(strip_option))]
|
||||||
@ -350,40 +334,17 @@ pub struct QemuConfig {
|
|||||||
program: Program,
|
program: Program,
|
||||||
} // Adding something here? Please leave Program as the last field
|
} // Adding something here? Please leave Program as the last field
|
||||||
|
|
||||||
impl From<QemuConfig> for Result<Qemu, QemuInitError> {
|
|
||||||
/// This method is necessary to make the API resemble a typical builder pattern, i.e.
|
|
||||||
/// `Qemu::builder().foo(bar).build()`, while still leveraging `TypedBuilder` for this
|
|
||||||
/// non-standard use case where `Qemu` doesn't store the configuration.
|
|
||||||
/// Internally, `TypedBuilder` is used to generate a builder for `QemuConfig`.
|
|
||||||
/// This `QemuConfig.into()` method is used by the derived `QemuConfigBuilder.build()`
|
|
||||||
/// to go from `QemuConfigBuilder` to `QemuConfig`, and finally to `Qemu` in one fn.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// returns `QemuInitError` if the Qemu initialization fails, including cases where Qemu has
|
|
||||||
/// already been initialized.
|
|
||||||
fn from(config: QemuConfig) -> Self {
|
|
||||||
let args = config
|
|
||||||
.to_string()
|
|
||||||
.split(' ')
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
let qemu = Qemu::init(&args)?;
|
|
||||||
QEMU_CONFIG
|
|
||||||
.set(config)
|
|
||||||
.map_err(|_| unreachable!("BUG: QEMU_CONFIG was already set but Qemu was not init!"))?;
|
|
||||||
Ok(qemu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::Qemu;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
fn usermode() {
|
fn usermode() {
|
||||||
let program = "/bin/pwd";
|
let program = "/bin/pwd";
|
||||||
let qemu = Qemu::builder().program("/bin/pwd").build().unwrap();
|
let qemu_config = QemuConfig::builder().program("/bin/pwd").build();
|
||||||
|
let qemu = Qemu::init(qemu_config).unwrap();
|
||||||
let config = qemu.get_config().unwrap();
|
let config = qemu.get_config().unwrap();
|
||||||
assert_eq!(config.to_string().trim(), program.trim());
|
assert_eq!(config.to_string().trim(), program.trim());
|
||||||
}
|
}
|
||||||
|
155
libafl_qemu/src/qemu/error.rs
Normal file
155
libafl_qemu/src/qemu/error.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
use core::fmt;
|
||||||
|
use std::{convert::Infallible, fmt::Display};
|
||||||
|
|
||||||
|
use libafl_qemu_sys::{CPUStatePtr, GuestAddr};
|
||||||
|
|
||||||
|
use crate::CallingConvention;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum QemuError {
|
||||||
|
Init(QemuInitError),
|
||||||
|
Exit(QemuExitError),
|
||||||
|
RW(QemuRWError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum QemuInitError {
|
||||||
|
MultipleInstances,
|
||||||
|
NoParametersProvided,
|
||||||
|
EmptyArgs,
|
||||||
|
Infallible,
|
||||||
|
TooManyArgs(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum QemuExitError {
|
||||||
|
UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen.
|
||||||
|
UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum QemuRWErrorKind {
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum QemuRWErrorCause {
|
||||||
|
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
|
||||||
|
WrongArgument(i32),
|
||||||
|
CurrentCpuNotFound,
|
||||||
|
Reg(i32),
|
||||||
|
WrongMemoryLocation(GuestAddr, usize), // addr, size
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub struct QemuRWError {
|
||||||
|
kind: QemuRWErrorKind,
|
||||||
|
cause: QemuRWErrorCause,
|
||||||
|
cpu: Option<CPUStatePtr>, // Only makes sense when cause != CurrentCpuNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for QemuInitError {}
|
||||||
|
|
||||||
|
impl Display for QemuInitError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
QemuInitError::MultipleInstances => {
|
||||||
|
write!(f, "Only one instance of the QEMU Emulator is permitted")
|
||||||
|
}
|
||||||
|
QemuInitError::NoParametersProvided => {
|
||||||
|
write!(f, "No parameters were provided to initialize QEMU.")
|
||||||
|
}
|
||||||
|
QemuInitError::EmptyArgs => {
|
||||||
|
write!(f, "QEMU emulator args cannot be empty")
|
||||||
|
}
|
||||||
|
QemuInitError::TooManyArgs(n) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Too many arguments passed to QEMU emulator ({n} > i32::MAX)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
QemuInitError::Infallible => {
|
||||||
|
panic!("Infallible error, should never be reached.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<QemuInitError> for libafl::Error {
|
||||||
|
fn from(err: QemuInitError) -> Self {
|
||||||
|
libafl::Error::unknown(format!("{err}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Infallible> for QemuInitError {
|
||||||
|
fn from(_: Infallible) -> Self {
|
||||||
|
QemuInitError::Infallible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuRWError {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option<CPUStatePtr>) -> Self {
|
||||||
|
Self { kind, cause, cpu }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrong_reg<R>(kind: QemuRWErrorKind, reg: R, cpu: Option<CPUStatePtr>) -> Self
|
||||||
|
where
|
||||||
|
R: Into<i32> + Clone,
|
||||||
|
{
|
||||||
|
Self::new(kind, QemuRWErrorCause::Reg(reg.into()), cpu)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wrong_mem_location(
|
||||||
|
kind: QemuRWErrorKind,
|
||||||
|
cpu: CPUStatePtr,
|
||||||
|
addr: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
) -> Self {
|
||||||
|
Self::new(
|
||||||
|
kind,
|
||||||
|
QemuRWErrorCause::WrongMemoryLocation(addr, size),
|
||||||
|
Some(cpu),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self {
|
||||||
|
Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self {
|
||||||
|
Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_conv(
|
||||||
|
kind: QemuRWErrorKind,
|
||||||
|
expected_conv: CallingConvention,
|
||||||
|
given_conv: CallingConvention,
|
||||||
|
) -> Result<(), Self> {
|
||||||
|
if expected_conv != given_conv {
|
||||||
|
return Err(Self::new(
|
||||||
|
kind,
|
||||||
|
QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<QemuError> for libafl::Error {
|
||||||
|
fn from(qemu_error: QemuError) -> Self {
|
||||||
|
libafl::Error::runtime(qemu_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<QemuError> for String {
|
||||||
|
fn from(qemu_error: QemuError) -> Self {
|
||||||
|
format!("LibAFL QEMU Error: {qemu_error:?}")
|
||||||
|
}
|
||||||
|
}
|
@ -100,16 +100,16 @@ impl<F, C, R: Clone> Hook<F, C, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! create_wrapper {
|
macro_rules! create_pre_init_wrapper {
|
||||||
($name:ident, ($($param:ident : $param_type:ty),*)) => {
|
($name:ident, ($($param:ident : $param_type:ty),*)) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
pub extern "C" fn [<func_ $name _hook_wrapper>]<ET, S>(hook: &mut c_void, $($param: $param_type),*)
|
pub extern "C" fn [<func_ $name _hook_wrapper>]<ET, S>(hook: &mut (), $($param: $param_type),*)
|
||||||
where
|
where
|
||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
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));
|
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<()>(hook));
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,13 +128,13 @@ macro_rules! create_wrapper {
|
|||||||
};
|
};
|
||||||
($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => {
|
($name:ident, ($($param:ident : $param_type:ty),*), $ret_type:ty) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
pub extern "C" fn [<func_ $name _hook_wrapper>]<ET, S>(hook: &mut c_void, $($param: $param_type),*) -> $ret_type
|
pub extern "C" fn [<func_ $name _hook_wrapper>]<ET, S>(hook: &mut (), $($param: $param_type),*) -> $ret_type
|
||||||
where
|
where
|
||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
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));
|
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<()>(hook));
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*)
|
func(modules, inprocess_get_state::<S>(), $($param),*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,6 +153,63 @@ macro_rules! create_wrapper {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (), $($param: $param_type),*)
|
||||||
|
where
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) = transmute(ptr::from_mut::<()>(hook));
|
||||||
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn [<closure_ $name _hook_wrapper>]<ET, S>(hook: &mut FatPtr, $($param: $param_type),*)
|
||||||
|
where
|
||||||
|
S: Unpin + UsesInput,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
let func: &mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)> = &mut *(ptr::from_mut::<FatPtr>(hook) as *mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)>);
|
||||||
|
func(qemu, 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 (), $($param: $param_type),*) -> $ret_type
|
||||||
|
where
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> $ret_type= transmute(ptr::from_mut::<()>(hook));
|
||||||
|
func(qemu, 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
|
||||||
|
S: UsesInput + Unpin,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
let func: &mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> $ret_type> = &mut *(ptr::from_mut::<FatPtr>(hook) as *mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> $ret_type>);
|
||||||
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! create_pre_exec_wrapper {
|
macro_rules! create_pre_exec_wrapper {
|
||||||
($name:ident, ($($param:ident : $param_type:ty),*), $hook_id:ident) => {
|
($name:ident, ($($param:ident : $param_type:ty),*), $hook_id:ident) => {
|
||||||
paste::paste! {
|
paste::paste! {
|
||||||
@ -161,21 +218,22 @@ macro_rules! create_pre_exec_wrapper {
|
|||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
|
||||||
match &mut hook.pre_run {
|
match &mut hook.pre_run {
|
||||||
HookRepr::Function(ptr) => {
|
HookRepr::Function(ptr) => {
|
||||||
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
|
||||||
transmute(*ptr);
|
transmute(*ptr);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*)
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*)
|
||||||
}
|
}
|
||||||
HookRepr::Closure(ptr) => {
|
HookRepr::Closure(ptr) => {
|
||||||
let func: &mut Box<
|
let func: &mut Box<
|
||||||
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
||||||
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<
|
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<
|
||||||
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
||||||
>);
|
>);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*)
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*)
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -193,21 +251,22 @@ macro_rules! create_post_exec_wrapper {
|
|||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
|
||||||
match &mut hook.post_run {
|
match &mut hook.post_run {
|
||||||
HookRepr::Function(ptr) => {
|
HookRepr::Function(ptr) => {
|
||||||
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
|
||||||
transmute(*ptr);
|
transmute(*ptr);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
HookRepr::Closure(ptr) => {
|
HookRepr::Closure(ptr) => {
|
||||||
let func: &mut Box<
|
let func: &mut Box<
|
||||||
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
||||||
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<
|
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<
|
||||||
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
||||||
>);
|
>);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -225,19 +284,20 @@ macro_rules! create_gen_wrapper {
|
|||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
|
||||||
match &mut hook.gen {
|
match &mut hook.gen {
|
||||||
HookRepr::Function(ptr) => {
|
HookRepr::Function(ptr) => {
|
||||||
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type> =
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type> =
|
||||||
transmute(*ptr);
|
transmute(*ptr);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id)
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
}
|
}
|
||||||
HookRepr::Closure(ptr) => {
|
HookRepr::Closure(ptr) => {
|
||||||
let func: &mut Box<
|
let func: &mut Box<
|
||||||
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type>,
|
dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type>,
|
||||||
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type>>);
|
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) -> Option<$ret_type>>);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id)
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
}
|
}
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
@ -255,18 +315,20 @@ macro_rules! create_post_gen_wrapper {
|
|||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
|
||||||
match &mut hook.post_gen {
|
match &mut hook.post_gen {
|
||||||
HookRepr::Function(ptr) => {
|
HookRepr::Function(ptr) => {
|
||||||
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) =
|
||||||
transmute(*ptr);
|
transmute(*ptr);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
HookRepr::Closure(ptr) => {
|
HookRepr::Closure(ptr) => {
|
||||||
let func: &mut Box<
|
let func: &mut Box<
|
||||||
dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*),
|
||||||
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)>);
|
> = &mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)>);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -284,16 +346,18 @@ macro_rules! create_exec_wrapper {
|
|||||||
S: UsesInput + Unpin,
|
S: UsesInput + Unpin,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let qemu = Qemu::get_unchecked();
|
||||||
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
let modules = EmulatorModules::<ET, S>::emulator_modules_mut_unchecked();
|
||||||
|
|
||||||
match &mut hook.execs[$execidx] {
|
match &mut hook.execs[$execidx] {
|
||||||
HookRepr::Function(ptr) => {
|
HookRepr::Function(ptr) => {
|
||||||
let func: fn(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) = transmute(*ptr);
|
let func: fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*) = transmute(*ptr);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
HookRepr::Closure(ptr) => {
|
HookRepr::Closure(ptr) => {
|
||||||
let func: &mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)> =
|
let func: &mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)> =
|
||||||
&mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<dyn FnMut(&mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)>);
|
&mut *(ptr::from_mut::<FatPtr>(ptr) as *mut Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, $($param_type),*)>);
|
||||||
func(modules, inprocess_get_state::<S>(), $($param),*);
|
func(qemu, modules, inprocess_get_state::<S>(), $($param),*);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -359,8 +423,8 @@ macro_rules! create_hook_types {
|
|||||||
// Instruction hook wrappers
|
// Instruction hook wrappers
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
Instruction,
|
Instruction,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, GuestAddr),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, GuestAddr),
|
||||||
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, GuestAddr)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, GuestAddr)>,
|
||||||
extern "C" fn(*const (), pc: GuestAddr)
|
extern "C" fn(*const (), pc: GuestAddr)
|
||||||
);
|
);
|
||||||
create_hook_id!(Instruction, libafl_qemu_remove_instruction_hook, true);
|
create_hook_id!(Instruction, libafl_qemu_remove_instruction_hook, true);
|
||||||
@ -369,9 +433,9 @@ create_wrapper!(instruction, (pc: GuestAddr));
|
|||||||
// Backdoor hook wrappers
|
// Backdoor hook wrappers
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
Backdoor,
|
Backdoor,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUArchStatePtr, GuestAddr),
|
fn(Qemu, &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)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, GuestAddr)>,
|
||||||
extern "C" fn(*const (), cpu: CPUArchStatePtr, pc: GuestAddr)
|
extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUArchStatePtr, pc: GuestAddr)
|
||||||
);
|
);
|
||||||
create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true);
|
create_hook_id!(Backdoor, libafl_qemu_remove_backdoor_hook, true);
|
||||||
create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr));
|
create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr));
|
||||||
@ -381,6 +445,7 @@ create_wrapper!(backdoor, (cpu: CPUArchStatePtr, pc: GuestAddr));
|
|||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
PreSyscall,
|
PreSyscall,
|
||||||
fn(
|
fn(
|
||||||
|
Qemu,
|
||||||
&mut EmulatorModules<ET, S>,
|
&mut EmulatorModules<ET, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
sys_num: i32,
|
sys_num: i32,
|
||||||
@ -395,6 +460,7 @@ create_hook_types!(
|
|||||||
) -> SyscallHookResult,
|
) -> SyscallHookResult,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&'a mut S>,
|
Option<&'a mut S>,
|
||||||
i32,
|
i32,
|
||||||
@ -445,6 +511,7 @@ create_wrapper!(
|
|||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
PostSyscall,
|
PostSyscall,
|
||||||
fn(
|
fn(
|
||||||
|
Qemu,
|
||||||
&mut EmulatorModules<ET, S>,
|
&mut EmulatorModules<ET, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
res: GuestAddr,
|
res: GuestAddr,
|
||||||
@ -460,6 +527,7 @@ create_hook_types!(
|
|||||||
) -> GuestAddr,
|
) -> GuestAddr,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
@ -520,23 +588,23 @@ create_hook_types!(
|
|||||||
u32,
|
u32,
|
||||||
) -> bool,
|
) -> bool,
|
||||||
>,
|
>,
|
||||||
extern "C" fn(*const (), env: CPUArchStatePtr, tid: u32) -> bool
|
extern "C" fn(libafl_qemu_opaque: *const (), env: CPUArchStatePtr, tid: u32) -> bool
|
||||||
);
|
);
|
||||||
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
|
create_hook_id!(NewThread, libafl_qemu_remove_new_thread_hook, false);
|
||||||
create_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool);
|
create_pre_init_wrapper!(new_thread, (env: CPUArchStatePtr, tid: u32), bool);
|
||||||
|
|
||||||
// CPU Run hook wrappers
|
// CPU Run hook wrappers
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
CpuPreRun,
|
CpuPreRun,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUStatePtr),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUStatePtr),
|
||||||
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, CPUStatePtr)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, CPUStatePtr)>,
|
||||||
extern "C" fn(*const (), cpu: CPUStatePtr)
|
extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUStatePtr)
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
CpuPostRun,
|
CpuPostRun,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUStatePtr),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, cpu: CPUStatePtr),
|
||||||
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, CPUStatePtr)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, CPUStatePtr)>,
|
||||||
extern "C" fn(*const (), cpu: CPUStatePtr)
|
extern "C" fn(libafl_qemu_opaque: *const (), cpu: CPUStatePtr)
|
||||||
);
|
);
|
||||||
create_hook_id!(CpuRun, libafl_qemu_remove_cpu_run_hook, false);
|
create_hook_id!(CpuRun, libafl_qemu_remove_cpu_run_hook, false);
|
||||||
create_pre_exec_wrapper!(cpu_run, (cpu: CPUStatePtr), CpuRunHookId);
|
create_pre_exec_wrapper!(cpu_run, (cpu: CPUStatePtr), CpuRunHookId);
|
||||||
@ -546,22 +614,29 @@ create_wrapper!(cpu_run, (cpu: CPUStatePtr));
|
|||||||
// Edge hook wrappers
|
// Edge hook wrappers
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
EdgeGen,
|
EdgeGen,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option<u64>,
|
fn(
|
||||||
|
Qemu,
|
||||||
|
&mut EmulatorModules<ET, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
src: GuestAddr,
|
||||||
|
dest: GuestAddr,
|
||||||
|
) -> Option<u64>,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&'a mut S>,
|
Option<&'a mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
extern "C" fn(*const (), src: GuestAddr, dest: GuestAddr) -> u64
|
extern "C" fn(libafl_qemu_opaque: *const (), src: GuestAddr, dest: GuestAddr) -> u64
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
EdgeExec,
|
EdgeExec,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, id: u64),
|
||||||
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64)>,
|
||||||
unsafe extern "C" fn(*const (), id: u64)
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64)
|
||||||
);
|
);
|
||||||
create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true);
|
create_hook_id!(Edge, libafl_qemu_remove_edge_hook, true);
|
||||||
create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId);
|
create_gen_wrapper!(edge, (src: GuestAddr, dest: GuestAddr), u64, 1, EdgeHookId);
|
||||||
@ -570,27 +645,36 @@ create_exec_wrapper!(edge, (id: u64), 0, 1, EdgeHookId);
|
|||||||
// Block hook wrappers
|
// Block hook wrappers
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
BlockGen,
|
BlockGen,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr) -> Option<u64>,
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr) -> Option<u64>,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&'a mut S>,
|
Option<&'a mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
unsafe extern "C" fn(*const (), pc: GuestAddr) -> u64
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr) -> u64
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
BlockPostGen,
|
BlockPostGen,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr, block_length: GuestUsize),
|
fn(Qemu, &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)>,
|
Box<
|
||||||
unsafe extern "C" fn(*const (), pc: GuestAddr, block_length: GuestUsize)
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
|
&'a mut EmulatorModules<ET, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
GuestAddr,
|
||||||
|
GuestUsize,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr, block_length: GuestUsize)
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
BlockExec,
|
BlockExec,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, id: u64),
|
||||||
Box<dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64)>,
|
||||||
unsafe extern "C" fn(*const (), id: u64)
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64)
|
||||||
);
|
);
|
||||||
|
|
||||||
create_hook_id!(Block, libafl_qemu_remove_block_hook, true);
|
create_hook_id!(Block, libafl_qemu_remove_block_hook, true);
|
||||||
@ -602,7 +686,8 @@ create_exec_wrapper!(block, (id: u64), 0, 1, BlockHookId);
|
|||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
ReadGen,
|
ReadGen,
|
||||||
fn(
|
fn(
|
||||||
qemu_modules: &mut EmulatorModules<ET, S>,
|
Qemu,
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
addr: *mut TCGTemp,
|
addr: *mut TCGTemp,
|
||||||
@ -610,6 +695,7 @@ create_hook_types!(
|
|||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&'a mut S>,
|
Option<&'a mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
@ -617,21 +703,33 @@ create_hook_types!(
|
|||||||
MemAccessInfo,
|
MemAccessInfo,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64
|
unsafe extern "C" fn(
|
||||||
|
libafl_qemu_opaque: *const (),
|
||||||
|
pc: GuestAddr,
|
||||||
|
addr: *mut TCGTemp,
|
||||||
|
info: MemAccessInfo,
|
||||||
|
) -> u64
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
ReadExec,
|
ReadExec,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr),
|
fn(Qemu, &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)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr)>,
|
||||||
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr)
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr)
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
ReadExecN,
|
ReadExecN,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr, usize),
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
|
&'a mut EmulatorModules<ET, S>,
|
||||||
|
Option<&'a mut S>,
|
||||||
|
u64,
|
||||||
|
GuestAddr,
|
||||||
|
usize,
|
||||||
|
),
|
||||||
>,
|
>,
|
||||||
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize)
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr, size: usize)
|
||||||
);
|
);
|
||||||
create_hook_id!(Read, libafl_qemu_remove_read_hook, true);
|
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_gen_wrapper!(read, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, ReadHookId);
|
||||||
@ -651,6 +749,7 @@ create_exec_wrapper!(
|
|||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
WriteGen,
|
WriteGen,
|
||||||
fn(
|
fn(
|
||||||
|
Qemu,
|
||||||
&mut EmulatorModules<ET, S>,
|
&mut EmulatorModules<ET, S>,
|
||||||
Option<&mut S>,
|
Option<&mut S>,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
@ -659,6 +758,7 @@ create_hook_types!(
|
|||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&'a mut S>,
|
Option<&'a mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
@ -666,21 +766,33 @@ create_hook_types!(
|
|||||||
MemAccessInfo,
|
MemAccessInfo,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
unsafe extern "C" fn(*const (), pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo) -> u64
|
unsafe extern "C" fn(
|
||||||
|
libafl_qemu_opaque: *const (),
|
||||||
|
pc: GuestAddr,
|
||||||
|
addr: *mut TCGTemp,
|
||||||
|
info: MemAccessInfo,
|
||||||
|
) -> u64
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
WriteExec,
|
WriteExec,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr),
|
fn(Qemu, &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)>,
|
Box<dyn for<'a> FnMut(Qemu, &'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr)>,
|
||||||
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr)
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr)
|
||||||
);
|
);
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
WriteExecN,
|
WriteExecN,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
|
fn(Qemu, &mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(&'a mut EmulatorModules<ET, S>, Option<&'a mut S>, u64, GuestAddr, usize),
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
|
&'a mut EmulatorModules<ET, S>,
|
||||||
|
Option<&'a mut S>,
|
||||||
|
u64,
|
||||||
|
GuestAddr,
|
||||||
|
usize,
|
||||||
|
),
|
||||||
>,
|
>,
|
||||||
unsafe extern "C" fn(*const (), id: u64, addr: GuestAddr, size: usize)
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr, size: usize)
|
||||||
);
|
);
|
||||||
create_hook_id!(Write, libafl_qemu_remove_write_hook, true);
|
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_gen_wrapper!(write, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, WriteHookId);
|
||||||
@ -699,16 +811,23 @@ create_exec_wrapper!(
|
|||||||
// Cmp hook wrappers
|
// Cmp hook wrappers
|
||||||
create_hook_types!(
|
create_hook_types!(
|
||||||
CmpGen,
|
CmpGen,
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, pc: GuestAddr, size: usize) -> Option<u64>,
|
fn(
|
||||||
|
Qemu,
|
||||||
|
&mut EmulatorModules<ET, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
pc: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
) -> Option<u64>,
|
||||||
Box<
|
Box<
|
||||||
dyn for<'a> FnMut(
|
dyn for<'a> FnMut(
|
||||||
|
Qemu,
|
||||||
&'a mut EmulatorModules<ET, S>,
|
&'a mut EmulatorModules<ET, S>,
|
||||||
Option<&'a mut S>,
|
Option<&'a mut S>,
|
||||||
GuestAddr,
|
GuestAddr,
|
||||||
usize,
|
usize,
|
||||||
) -> Option<u64>,
|
) -> Option<u64>,
|
||||||
>,
|
>,
|
||||||
unsafe extern "C" fn(*const (), pc: GuestAddr, size: usize) -> u64
|
unsafe extern "C" fn(libafl_qemu_opaque: *const (), pc: GuestAddr, size: usize) -> u64
|
||||||
);
|
);
|
||||||
pub type CmpExecHook<ET, S, SZ> = Hook<
|
pub type CmpExecHook<ET, S, SZ> = Hook<
|
||||||
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, v0: SZ, v1: SZ),
|
fn(&mut EmulatorModules<ET, S>, Option<&mut S>, id: u64, v0: SZ, v1: SZ),
|
||||||
@ -724,9 +843,9 @@ create_exec_wrapper!(cmp, (id: u64, v0: u64, v1: u64), 3, 4, CmpHookId);
|
|||||||
|
|
||||||
// Crash hook wrappers
|
// Crash hook wrappers
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
pub type CrashHookFn<ET, S> = fn(&mut EmulatorModules<ET, S>, i32);
|
pub type CrashHookFn<ET, S> = fn(Qemu, &mut EmulatorModules<ET, S>, i32);
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
pub type CrashHookClosure<ET, S> = Box<dyn FnMut(&mut EmulatorModules<ET, S>, i32)>;
|
pub type CrashHookClosure<ET, S> = Box<dyn FnMut(Qemu, &mut EmulatorModules<ET, S>, i32)>;
|
||||||
|
|
||||||
/// The thin wrapper around QEMU hooks.
|
/// The thin wrapper around QEMU hooks.
|
||||||
/// It is considered unsafe to use it directly.
|
/// It is considered unsafe to use it directly.
|
||||||
|
@ -14,6 +14,7 @@ use std::{
|
|||||||
mem::MaybeUninit,
|
mem::MaybeUninit,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
sync::OnceLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl_bolts::os::unix_signals::Signal;
|
use libafl_bolts::os::unix_signals::Signal;
|
||||||
@ -31,7 +32,12 @@ use strum::IntoEnumIterator;
|
|||||||
use crate::{GuestAddrKind, GuestReg, Regs};
|
use crate::{GuestAddrKind, GuestReg, Regs};
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
use config::{QemuConfig, QemuConfigBuilder, QEMU_CONFIG};
|
use config::QemuConfig;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub use error::{
|
||||||
|
QemuError, QemuExitError, QemuInitError, QemuRWError, QemuRWErrorCause, QemuRWErrorKind,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
mod usermode;
|
mod usermode;
|
||||||
@ -48,30 +54,33 @@ pub use hooks::*;
|
|||||||
|
|
||||||
static mut QEMU_IS_INITIALIZED: bool = false;
|
static mut QEMU_IS_INITIALIZED: bool = false;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub(super) static QEMU_CONFIG: OnceLock<QemuConfig> = OnceLock::new();
|
||||||
pub enum QemuError {
|
|
||||||
Init(QemuInitError),
|
#[expect(clippy::vec_box)]
|
||||||
Exit(QemuExitError),
|
static mut GDB_COMMANDS: Vec<Box<FatPtr>> = Vec::new();
|
||||||
RW(QemuRWError),
|
|
||||||
|
pub trait HookId {
|
||||||
|
fn remove(&self, invalidate_block: bool) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<QemuError> for libafl::Error {
|
pub trait ArchExtras {
|
||||||
fn from(qemu_error: QemuError) -> Self {
|
fn read_return_address(&self) -> Result<GuestReg, QemuRWError>;
|
||||||
libafl::Error::runtime(qemu_error)
|
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
|
||||||
}
|
where
|
||||||
}
|
T: Into<GuestReg>;
|
||||||
|
fn read_function_argument(
|
||||||
impl From<QemuError> for String {
|
&self,
|
||||||
fn from(qemu_error: QemuError) -> Self {
|
conv: CallingConvention,
|
||||||
format!("LibAFL QEMU Error: {qemu_error:?}")
|
idx: u8,
|
||||||
}
|
) -> Result<GuestReg, QemuRWError>;
|
||||||
}
|
fn write_function_argument<T>(
|
||||||
|
&self,
|
||||||
#[derive(Debug)]
|
conv: CallingConvention,
|
||||||
pub enum QemuInitError {
|
idx: i32,
|
||||||
MultipleInstances,
|
val: T,
|
||||||
EmptyArgs,
|
) -> Result<(), QemuRWError>
|
||||||
TooManyArgs(usize),
|
where
|
||||||
|
T: Into<GuestReg>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -89,81 +98,6 @@ pub enum QemuExitReason {
|
|||||||
Timeout,
|
Timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum QemuExitError {
|
|
||||||
UnknownKind, // Exit reason was not NULL, but exit kind is unknown. Should never happen.
|
|
||||||
UnexpectedExit, // Qemu exited without going through an expected exit point. Can be caused by a crash for example.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum QemuRWErrorKind {
|
|
||||||
Read,
|
|
||||||
Write,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum QemuRWErrorCause {
|
|
||||||
WrongCallingConvention(CallingConvention, CallingConvention), // expected, given
|
|
||||||
WrongArgument(i32),
|
|
||||||
CurrentCpuNotFound,
|
|
||||||
Reg(i32),
|
|
||||||
WrongMemoryLocation(GuestAddr, usize), // addr, size
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub struct QemuRWError {
|
|
||||||
kind: QemuRWErrorKind,
|
|
||||||
cause: QemuRWErrorCause,
|
|
||||||
cpu: Option<CPUStatePtr>, // Only makes sense when cause != CurrentCpuNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QemuRWError {
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(kind: QemuRWErrorKind, cause: QemuRWErrorCause, cpu: Option<CPUStatePtr>) -> Self {
|
|
||||||
Self { kind, cause, cpu }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wrong_mem_location(
|
|
||||||
kind: QemuRWErrorKind,
|
|
||||||
cpu: CPUStatePtr,
|
|
||||||
addr: GuestAddr,
|
|
||||||
size: usize,
|
|
||||||
) -> Self {
|
|
||||||
Self::new(
|
|
||||||
kind,
|
|
||||||
QemuRWErrorCause::WrongMemoryLocation(addr, size),
|
|
||||||
Some(cpu),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn current_cpu_not_found(kind: QemuRWErrorKind) -> Self {
|
|
||||||
Self::new(kind, QemuRWErrorCause::CurrentCpuNotFound, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_argument_error(kind: QemuRWErrorKind, reg_id: i32) -> Self {
|
|
||||||
Self::new(kind, QemuRWErrorCause::WrongArgument(reg_id), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_conv(
|
|
||||||
kind: QemuRWErrorKind,
|
|
||||||
expected_conv: CallingConvention,
|
|
||||||
given_conv: CallingConvention,
|
|
||||||
) -> Result<(), Self> {
|
|
||||||
if expected_conv != given_conv {
|
|
||||||
return Err(Self::new(
|
|
||||||
kind,
|
|
||||||
QemuRWErrorCause::WrongCallingConvention(expected_conv, given_conv),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thin wrapper around QEMU.
|
/// The thin wrapper around QEMU.
|
||||||
/// It is considered unsafe to use it directly.
|
/// It is considered unsafe to use it directly.
|
||||||
/// Prefer using `Emulator` instead in case of doubt.
|
/// Prefer using `Emulator` instead in case of doubt.
|
||||||
@ -172,6 +106,12 @@ pub struct Qemu {
|
|||||||
_private: (),
|
_private: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum QemuParams {
|
||||||
|
Config(QemuConfig),
|
||||||
|
Cli(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct QemuMemoryChunk {
|
pub struct QemuMemoryChunk {
|
||||||
addr: GuestAddrKind,
|
addr: GuestAddrKind,
|
||||||
@ -179,18 +119,6 @@ pub struct QemuMemoryChunk {
|
|||||||
cpu: Option<CPU>,
|
cpu: Option<CPU>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::vec_box)]
|
|
||||||
static mut GDB_COMMANDS: Vec<Box<FatPtr>> = Vec::new();
|
|
||||||
|
|
||||||
unsafe extern "C" fn gdb_cmd(data: *mut c_void, buf: *mut u8, len: usize) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let closure = &mut *(data as *mut Box<dyn for<'r> FnMut(Qemu, &'r str) -> bool>);
|
|
||||||
let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len));
|
|
||||||
let qemu = Qemu::get_unchecked();
|
|
||||||
closure(qemu, cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum QemuShutdownCause {
|
pub enum QemuShutdownCause {
|
||||||
None,
|
None,
|
||||||
@ -223,37 +151,15 @@ pub enum CallingConvention {
|
|||||||
Cdecl,
|
Cdecl,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HookId {
|
|
||||||
fn remove(&self, invalidate_block: bool) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct HookData(u64);
|
pub struct HookData(u64);
|
||||||
|
|
||||||
impl std::error::Error for QemuInitError {}
|
unsafe extern "C" fn gdb_cmd(data: *mut c_void, buf: *mut u8, len: usize) -> bool {
|
||||||
|
unsafe {
|
||||||
impl Display for QemuInitError {
|
let closure = &mut *(data as *mut Box<dyn for<'r> FnMut(Qemu, &'r str) -> bool>);
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
let cmd = std::str::from_utf8_unchecked(std::slice::from_raw_parts(buf, len));
|
||||||
match self {
|
let qemu = Qemu::get_unchecked();
|
||||||
QemuInitError::MultipleInstances => {
|
closure(qemu, cmd)
|
||||||
write!(f, "Only one instance of the QEMU Emulator is permitted")
|
|
||||||
}
|
|
||||||
QemuInitError::EmptyArgs => {
|
|
||||||
write!(f, "QEMU emulator args cannot be empty")
|
|
||||||
}
|
|
||||||
QemuInitError::TooManyArgs(n) => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Too many arguments passed to QEMU emulator ({n} > i32::MAX)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<QemuInitError> for libafl::Error {
|
|
||||||
fn from(err: QemuInitError) -> Self {
|
|
||||||
libafl::Error::unknown(format!("{err}"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +174,64 @@ impl Display for QemuExitReason {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<QemuConfig> for QemuParams {
|
||||||
|
fn from(config: QemuConfig) -> Self {
|
||||||
|
QemuParams::Config(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl TryFrom<QemuConfigBuilder> for QemuParams {
|
||||||
|
// type Error = QemuInitError;
|
||||||
|
//
|
||||||
|
// fn try_from(config_builder: QemuConfigBuilder) -> Result<Self, Self::Error> {
|
||||||
|
// Ok(QemuParams::Config(
|
||||||
|
// config_builder
|
||||||
|
// .build()
|
||||||
|
// .map_err(QemuInitError::ConfigurationError)?,
|
||||||
|
// ))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<T> From<&[T]> for QemuParams
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn from(cli: &[T]) -> Self {
|
||||||
|
QemuParams::Cli(cli.iter().map(|x| x.as_ref().into()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<&Vec<T>> for QemuParams
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn from(cli: &Vec<T>) -> Self {
|
||||||
|
cli.as_slice().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Vec<T>> for QemuParams
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn from(cli: Vec<T>) -> Self {
|
||||||
|
(&cli).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuParams {
|
||||||
|
pub fn to_cli(&self) -> Vec<String> {
|
||||||
|
match self {
|
||||||
|
QemuParams::Config(cfg) => cfg
|
||||||
|
.to_string()
|
||||||
|
.split(' ')
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect(),
|
||||||
|
QemuParams::Cli(cli) => cli.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MemAccessInfo {
|
impl MemAccessInfo {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn memop(&self) -> libafl_qemu_sys::MemOp {
|
pub fn memop(&self) -> libafl_qemu_sys::MemOp {
|
||||||
@ -318,26 +282,6 @@ impl From<libafl_qemu_sys::MemOpIdx> for MemAccessInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArchExtras {
|
|
||||||
fn read_return_address(&self) -> Result<GuestReg, QemuRWError>;
|
|
||||||
fn write_return_address<T>(&self, val: T) -> Result<(), QemuRWError>
|
|
||||||
where
|
|
||||||
T: Into<GuestReg>;
|
|
||||||
fn read_function_argument(
|
|
||||||
&self,
|
|
||||||
conv: CallingConvention,
|
|
||||||
idx: u8,
|
|
||||||
) -> Result<GuestReg, QemuRWError>;
|
|
||||||
fn write_function_argument<T>(
|
|
||||||
&self,
|
|
||||||
conv: CallingConvention,
|
|
||||||
idx: i32,
|
|
||||||
val: T,
|
|
||||||
) -> Result<(), QemuRWError>
|
|
||||||
where
|
|
||||||
T: Into<GuestReg>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[expect(clippy::cast_sign_loss)]
|
#[expect(clippy::cast_sign_loss)]
|
||||||
@ -367,11 +311,11 @@ impl CPU {
|
|||||||
let mut val = MaybeUninit::uninit();
|
let mut val = MaybeUninit::uninit();
|
||||||
let success = libafl_qemu_read_reg(self.ptr, reg_id, val.as_mut_ptr() as *mut u8);
|
let success = libafl_qemu_read_reg(self.ptr, reg_id, val.as_mut_ptr() as *mut u8);
|
||||||
if success == 0 {
|
if success == 0 {
|
||||||
Err(QemuRWError {
|
Err(QemuRWError::wrong_reg(
|
||||||
kind: QemuRWErrorKind::Write,
|
QemuRWErrorKind::Write,
|
||||||
cause: QemuRWErrorCause::Reg(reg.into()),
|
reg,
|
||||||
cpu: Some(self.ptr),
|
Some(self.ptr),
|
||||||
})
|
))
|
||||||
} else {
|
} else {
|
||||||
#[cfg(feature = "be")]
|
#[cfg(feature = "be")]
|
||||||
return Ok(GuestReg::from_be(val.assume_init()).into());
|
return Ok(GuestReg::from_be(val.assume_init()).into());
|
||||||
@ -396,11 +340,11 @@ impl CPU {
|
|||||||
|
|
||||||
let success = unsafe { libafl_qemu_write_reg(self.ptr, reg_id, &raw const val as *mut u8) };
|
let success = unsafe { libafl_qemu_write_reg(self.ptr, reg_id, &raw const val as *mut u8) };
|
||||||
if success == 0 {
|
if success == 0 {
|
||||||
Err(QemuRWError {
|
Err(QemuRWError::wrong_reg(
|
||||||
kind: QemuRWErrorKind::Write,
|
QemuRWErrorKind::Write,
|
||||||
cause: QemuRWErrorCause::Reg(reg.into()),
|
reg,
|
||||||
cpu: Some(self.ptr),
|
Some(self.ptr),
|
||||||
})
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -571,14 +515,25 @@ impl From<u8> for HookData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Qemu {
|
impl Qemu {
|
||||||
/// For more details about the parameters check
|
|
||||||
/// [the QEMU documentation](https://www.qemu.org/docs/master/about/).
|
|
||||||
pub fn builder() -> QemuConfigBuilder {
|
|
||||||
QemuConfig::builder()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::similar_names)]
|
#[expect(clippy::similar_names)]
|
||||||
pub fn init(args: &[String]) -> Result<Self, QemuInitError> {
|
pub fn init<T>(params: T) -> Result<Self, QemuInitError>
|
||||||
|
where
|
||||||
|
T: Into<QemuParams>,
|
||||||
|
{
|
||||||
|
let params: QemuParams = params.into();
|
||||||
|
|
||||||
|
match ¶ms {
|
||||||
|
QemuParams::Config(cfg) => {
|
||||||
|
QEMU_CONFIG
|
||||||
|
.set(cfg.clone())
|
||||||
|
.map_err(|_| unreachable!("QEMU_CONFIG was already set but Qemu was not init!"))
|
||||||
|
.expect("Could not set QEMU Config.");
|
||||||
|
}
|
||||||
|
QemuParams::Cli(_) => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = params.to_cli();
|
||||||
|
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Err(QemuInitError::EmptyArgs);
|
return Err(QemuInitError::EmptyArgs);
|
||||||
}
|
}
|
||||||
@ -592,6 +547,7 @@ impl Qemu {
|
|||||||
if QEMU_IS_INITIALIZED {
|
if QEMU_IS_INITIALIZED {
|
||||||
return Err(QemuInitError::MultipleInstances);
|
return Err(QemuInitError::MultipleInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
QEMU_IS_INITIALIZED = true;
|
QEMU_IS_INITIALIZED = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +556,7 @@ impl Qemu {
|
|||||||
|
|
||||||
let args: Vec<CString> = args
|
let args: Vec<CString> = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| CString::new(x.clone()).unwrap())
|
.map(|x| CString::new(AsRef::<str>::as_ref(x)).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect();
|
let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect();
|
||||||
argv.push(ptr::null()); // argv is always null terminated.
|
argv.push(ptr::null()); // argv is always null terminated.
|
||||||
@ -904,11 +860,7 @@ impl Qemu {
|
|||||||
impl ArchExtras for Qemu {
|
impl ArchExtras for Qemu {
|
||||||
fn read_return_address(&self) -> Result<GuestReg, QemuRWError> {
|
fn read_return_address(&self) -> Result<GuestReg, QemuRWError> {
|
||||||
self.current_cpu()
|
self.current_cpu()
|
||||||
.ok_or(QemuRWError {
|
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
|
||||||
kind: QemuRWErrorKind::Read,
|
|
||||||
cause: QemuRWErrorCause::CurrentCpuNotFound,
|
|
||||||
cpu: None,
|
|
||||||
})?
|
|
||||||
.read_return_address()
|
.read_return_address()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,8 +263,8 @@ impl Qemu {
|
|||||||
libafl_qemu_sys::syx_snapshot_root_restore(snapshot);
|
libafl_qemu_sys::syx_snapshot_root_restore(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::missing_safety_doc)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[expect(clippy::missing_safety_doc)]
|
||||||
pub unsafe fn check_fast_snapshot(
|
pub unsafe fn check_fast_snapshot(
|
||||||
&self,
|
&self,
|
||||||
ref_snapshot: FastSnapshotPtr,
|
ref_snapshot: FastSnapshotPtr,
|
||||||
|
@ -118,7 +118,7 @@ where
|
|||||||
{
|
{
|
||||||
/// Run the fuzzer
|
/// Run the fuzzer
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub fn run(&mut self, qemu: Qemu) {
|
pub fn run(&mut self, qemu_cli: &[String]) {
|
||||||
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,
|
||||||
@ -240,7 +240,11 @@ where
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(qemu_cli.to_owned())
|
||||||
|
.modules(modules)
|
||||||
|
.build()
|
||||||
|
.expect("Could not initialize Emulator");
|
||||||
|
|
||||||
let executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
emulator,
|
emulator,
|
||||||
@ -357,7 +361,11 @@ where
|
|||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(qemu_cli.to_owned())
|
||||||
|
.modules(modules)
|
||||||
|
.build()
|
||||||
|
.expect("Could not initialize Emulator");
|
||||||
|
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
emulator,
|
emulator,
|
||||||
@ -476,7 +484,6 @@ pub mod pybind {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use libafl_bolts::core_affinity::Cores;
|
use libafl_bolts::core_affinity::Cores;
|
||||||
use libafl_qemu::qemu::pybind::Qemu;
|
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
use pyo3::{prelude::*, types::PyBytes};
|
||||||
|
|
||||||
use crate::qemu;
|
use crate::qemu;
|
||||||
@ -533,7 +540,7 @@ pub mod pybind {
|
|||||||
|
|
||||||
/// Run the fuzzer
|
/// Run the fuzzer
|
||||||
#[expect(clippy::needless_pass_by_value)]
|
#[expect(clippy::needless_pass_by_value)]
|
||||||
pub fn run(&self, qemu: &Qemu, harness: PyObject) {
|
pub fn run(&self, qemu_cli: Vec<String>, harness: PyObject) {
|
||||||
qemu::QemuBytesCoverageSugar::builder()
|
qemu::QemuBytesCoverageSugar::builder()
|
||||||
.input_dirs(&self.input_dirs)
|
.input_dirs(&self.input_dirs)
|
||||||
.output_dir(self.output_dir.clone())
|
.output_dir(self.output_dir.clone())
|
||||||
@ -552,7 +559,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_cli);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user