WIP: QEMU exit handler (#1745)
* Added paging filtering. Reworked address range filtering to fit with new generic code. * Fix: renamed remaining QemuInstrumentationFilter instances. * Renamed sync breakpoint to sync exit. * Split emu in systemmode.rs / usermode.rs for specific code. EmuExitHandler implementation. * sync_backdoor.rs removal. Formatting. * Updated `bindgen` and `which`. Adapting code to work with update. * fix: reconfigure cleanly if prior configure was interrupted abruptly. * Enable sanitizers in QEMU during debug. * Added target-usable files. * Added breakpoint structure. * Adapted other files to work with ExitHandler. * Adapted existing fuzzer to work with new exit handler. * fix: use get to avoid crashes. * Updated README to indicate cargo-make should be installed. * Added QEMU internal exit handler. * Adapted qemu_systemmode example with new exit handler. * Fixed fuzzers to work with new exit handler. * Trying to fix CI (#1739) * test * dummy * dummy * Added new examples. * Forgot to add build scripts. * format * format * clang-format * python emulator adaptation. * fixed python bindings. * clippy fixes. * python bindings. * fix qemu_sugar. * fix fuzzbench. * fixed import issues. * misc fixes. * renamed crate. * Updated x86_64 stub bindings. * Fixed static naming. * binding fmt * clippy * clippy * Removed useless return statement. * removed advice to install cargo-make in individual repositories. * symcc_update (#1749) * Remove unused create_anymap_for_trait macro (fixes #1719) (#1752) * Fix `as_object` UB discussed in #1748 (#1751) * Fix as_object UB discussed in #1748 * More cleanup, more less UB * Fix fixes * Added uninit_on_shmem api * clippy * fmt * trying to fix fuzzers, libfuzzer wrapper * Add OwnedRefMit::owned constructor, libfuzzer fix * Some more fixes * Add BacktaceObserver::owned fn * fmt * more fmt * Ignore SigPipe by default (#1741) * Ignore SigPipe by default * Fix no_std * fmt * Fix incorrect imports (#1758) * Fix incorrect imports https://doc.rust-lang.org/core/simd/trait.SimdOrd.html * Fix * Try fix ci * Documentation fixes (#1761) * Documentation fixes * Fix InProcessExecutor url * Update all urls to latest * Miri ignores for M1 regex (#1762) * Enabling DrCov on Windows (#1765) * Enabling DrCov for Windows * Dedup common code in scheduler (#1702) * dedup common code in scheduler * del eco * fixing * fix * replace `Emulator::new_empty` by `Emulator::get` calls outside of `emu.rs` for safety. (#1763) * Add mute_inprocess_target fn, SimpleFdLogger::set_logger, and more (#1754) * Add mute_inprocess_target fn, SimpleFdLogger::set_logger, set_error_print_panic_hook * Trying to fix #1753 * typo * More fix * Fix test? * more testcase fixes * Fix: renamed remaining QemuInstrumentationFilter instances. * Split emu in systemmode.rs / usermode.rs for specific code. EmuExitHandler implementation. * format * format * format * Replace sync_exit with sync_backdoor. * Rework command system. * fix bad import. * format. * cargo fmt * disable af-xdp as well to avoid linking errors. * End of merging. * format. * Adaptation for usermode. * format. * injection support. * usermode fixes. format. * clippy * clippy + format * Do not unwrap emu + format. * fix: entry_point breakpoint * inital commit. * clippy * tests * clippy * adapt example * systemmode. * renaming * fmt * fix lints. * more lint fix. * even more lint fixes. * always more lint fixes. * lint fix. * allow unused qualifications for crate when it could be confusing. * Still lint fixes. * Lint fixes on generated code. * Some lint fixes. * merge continue. * renamed modules as well. * fixing merge. * systemmode compiling. * fmt * fix early emulator drop. * fmt * fix cast to c_void of the wrong object. * Added global enum for snapshot managers. Some renaming. * move things around. * WIP: generic inclusion of QEMU Executor in exit handler. * * Moved extern calls to `libafl_qemu_sys` * Replaced old `Emulator` by `Qemu` and only kept C functions wrappers * Now `Emulator` is for higher-level interactions with QEMU. Kept old functions for compatibility calling to `Qemu` functions * A direct side effect of this slit is the removal of the `IsEmuExitHandler` trait dependency added in many parts of the code. * Removed old dirty casting for `QemuExecutor` helpers and used the brand-new access to `QemuExecutorState` instead. * Minor changes to `Qemu` and `Emulator` `get` methods for cleaner getters. * Add missing `Qemu` function. * Updated `qemu_systemmode` example. * Adapted QEMU fuzzers + renaming. * Fixed python. * fix libafl_sugar with new implementation. * fix dangling RefCell. adapt new examples. TODO: merge `libafl_systemmode.*` examples. * clippy. * fix more fuzzers. * clippy. * Implement `HasInstrumentationFilter` generically. Updated `StdInstrumentationFilter` accordingly. * Renamed breakpoint functions for QEMU. `qemu.run()` handling. * Removed OnceCell / RefCell in signature. more explicit `MmapPerms` method names. * minor code refactoring * Emulator::run_handle refactoring * deprecated Emulator functions calling directly to QEMU functions. * IsSnapshotManager -> SnapshotManager * IsEmuExitHandler -> EmuExitHandler + fmt * Generic register when it makes sense. * reverted IsSnapshotManager -> SnapshotManager because of a collision. * fix syntax + clippy * fmt --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com> Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: lazymio <mio@lazym.io> Co-authored-by: Bet4 <0xbet4@gmail.com> Co-authored-by: mkravchik <mkravchik@hotmail.com>
This commit is contained in:
parent
50843b19d1
commit
44c841ffb1
@ -39,7 +39,7 @@ use libafl::{
|
|||||||
};
|
};
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
current_nanos, current_time,
|
current_nanos, current_time,
|
||||||
os::dup2,
|
os::{dup2, unix_signals::Signal},
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
@ -49,10 +49,10 @@ use libafl_qemu::{
|
|||||||
cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper},
|
cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper},
|
||||||
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE},
|
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Emulator,
|
|
||||||
filter_qemu_args,
|
filter_qemu_args,
|
||||||
hooks::QemuHooks,
|
hooks::QemuHooks,
|
||||||
GuestReg, MmapPerms, QemuForkExecutor, Regs,
|
GuestReg, MmapPerms, Qemu, QemuExitReason, QemuExitReasonError, QemuForkExecutor,
|
||||||
|
QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use nix::{self, unistd::dup};
|
use nix::{self, unistd::dup};
|
||||||
@ -148,33 +148,38 @@ fn fuzz(
|
|||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let emu = Emulator::new(&args, &env)?;
|
let qemu = Qemu::init(&args, &env)?;
|
||||||
|
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
|
||||||
|
|
||||||
let test_one_input_ptr = elf
|
let test_one_input_ptr = elf
|
||||||
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
qemu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||||
unsafe { emu.run() };
|
unsafe {
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
|
println!("Break at {:#x}", qemu.read_reg::<_, u64>(Regs::Pc).unwrap());
|
||||||
|
|
||||||
let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
|
let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
let mut ret_addr = [0; 8];
|
let mut ret_addr = [0; 8];
|
||||||
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
|
unsafe { qemu.read_mem(stack_ptr, &mut ret_addr) };
|
||||||
let ret_addr = u64::from_le_bytes(ret_addr);
|
let ret_addr = u64::from_le_bytes(ret_addr);
|
||||||
|
|
||||||
println!("Stack pointer = {stack_ptr:#x}");
|
println!("Stack pointer = {stack_ptr:#x}");
|
||||||
println!("Return address = {ret_addr:#x}");
|
println!("Return address = {ret_addr:#x}");
|
||||||
|
|
||||||
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
qemu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||||
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
qemu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
||||||
|
|
||||||
let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
|
let input_addr = qemu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap();
|
||||||
println!("Placing input at {input_addr:#x}");
|
println!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
let log = RefCell::new(
|
let log = RefCell::new(
|
||||||
@ -314,21 +319,28 @@ fn fuzz(
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.write_mem(input_addr, buf);
|
qemu.write_mem(input_addr, buf);
|
||||||
|
|
||||||
emu.write_reg(Regs::Rdi, input_addr).unwrap();
|
qemu.write_reg(Regs::Rdi, input_addr).unwrap();
|
||||||
emu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
|
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
|
||||||
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
|
qemu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
|
||||||
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
|
qemu.write_reg(Regs::Rsp, stack_ptr).unwrap();
|
||||||
|
|
||||||
emu.run();
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
|
process::exit(0)
|
||||||
|
}
|
||||||
|
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut hooks = QemuHooks::new(
|
let mut hooks = QemuHooks::new(
|
||||||
emu.clone(),
|
qemu.clone(),
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
QemuEdgeCoverageChildHelper::default(),
|
QemuEdgeCoverageChildHelper::default(),
|
||||||
QemuCmpLogChildHelper::default(),
|
QemuCmpLogChildHelper::default(),
|
||||||
|
@ -38,7 +38,7 @@ use libafl::{
|
|||||||
};
|
};
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
current_nanos, current_time,
|
current_nanos, current_time,
|
||||||
os::dup2,
|
os::{dup2, unix_signals::Signal},
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
@ -53,11 +53,14 @@ use libafl_qemu::{
|
|||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
filter_qemu_args,
|
filter_qemu_args,
|
||||||
hooks::QemuHooks,
|
hooks::QemuHooks,
|
||||||
Emulator,
|
|
||||||
GuestReg,
|
GuestReg,
|
||||||
//snapshot::QemuSnapshotHelper,
|
//snapshot::QemuSnapshotHelper,
|
||||||
MmapPerms,
|
MmapPerms,
|
||||||
|
Qemu,
|
||||||
QemuExecutor,
|
QemuExecutor,
|
||||||
|
QemuExitReason,
|
||||||
|
QemuExitReasonError,
|
||||||
|
QemuShutdownCause,
|
||||||
Regs,
|
Regs,
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -172,34 +175,39 @@ fn fuzz(
|
|||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let emu = Emulator::new(&args, &env).unwrap();
|
let qemu = Qemu::init(&args, &env).unwrap();
|
||||||
// let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap();
|
// let (emu, asan) = init_with_asan(&mut args, &mut env).unwrap();
|
||||||
|
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
|
||||||
|
|
||||||
let test_one_input_ptr = elf
|
let test_one_input_ptr = elf
|
||||||
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
println!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
qemu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||||
unsafe { emu.run() };
|
unsafe {
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap());
|
println!("Break at {:#x}", qemu.read_reg::<_, u64>(Regs::Pc).unwrap());
|
||||||
|
|
||||||
let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap();
|
let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
let mut ret_addr = [0; 8];
|
let mut ret_addr = [0; 8];
|
||||||
unsafe { emu.read_mem(stack_ptr, &mut ret_addr) };
|
unsafe { qemu.read_mem(stack_ptr, &mut ret_addr) };
|
||||||
let ret_addr = u64::from_le_bytes(ret_addr);
|
let ret_addr = u64::from_le_bytes(ret_addr);
|
||||||
|
|
||||||
println!("Stack pointer = {stack_ptr:#x}");
|
println!("Stack pointer = {stack_ptr:#x}");
|
||||||
println!("Return address = {ret_addr:#x}");
|
println!("Return address = {ret_addr:#x}");
|
||||||
|
|
||||||
emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
qemu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput
|
||||||
emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
qemu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr
|
||||||
|
|
||||||
let input_addr = emu
|
let input_addr = qemu
|
||||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("Placing input at {input_addr:#x}");
|
println!("Placing input at {input_addr:#x}");
|
||||||
@ -328,21 +336,28 @@ fn fuzz(
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.write_mem(input_addr, buf);
|
qemu.write_mem(input_addr, buf);
|
||||||
|
|
||||||
emu.write_reg(Regs::Rdi, input_addr).unwrap();
|
qemu.write_reg(Regs::Rdi, input_addr).unwrap();
|
||||||
emu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
|
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
|
||||||
emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
|
qemu.write_reg(Regs::Rip, test_one_input_ptr).unwrap();
|
||||||
emu.write_reg(Regs::Rsp, stack_ptr).unwrap();
|
qemu.write_reg(Regs::Rsp, stack_ptr).unwrap();
|
||||||
|
|
||||||
emu.run();
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
|
process::exit(0)
|
||||||
|
}
|
||||||
|
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut hooks = QemuHooks::new(
|
let mut hooks = QemuHooks::new(
|
||||||
emu.clone(),
|
qemu.clone(),
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
QemuEdgeCoverageHelper::default(),
|
QemuEdgeCoverageHelper::default(),
|
||||||
QemuCmpLogHelper::default(),
|
QemuCmpLogHelper::default(),
|
||||||
|
@ -21,6 +21,7 @@ use libafl::{
|
|||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
current_nanos,
|
current_nanos,
|
||||||
|
os::unix_signals::Signal,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
@ -29,9 +30,8 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE},
|
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Emulator,
|
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, QemuExitReason,
|
||||||
ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, QemuForkExecutor, QemuHooks,
|
QemuExitReasonError, QemuForkExecutor, QemuHooks, QemuShutdownCause, Regs,
|
||||||
Regs,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -113,31 +113,31 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
|
|
||||||
env::remove_var("LD_LIBRARY_PATH");
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let emu = Emulator::new(&options.args, &env).unwrap();
|
let qemu = Qemu::init(&options.args, &env).unwrap();
|
||||||
|
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
|
|
||||||
let test_one_input_ptr = elf
|
let test_one_input_ptr = elf
|
||||||
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
emu.entry_break(test_one_input_ptr);
|
qemu.entry_break(test_one_input_ptr);
|
||||||
|
|
||||||
let pc: GuestReg = emu.read_reg(Regs::Pc).unwrap();
|
let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
log::debug!("Break at {pc:#x}");
|
log::debug!("Break at {pc:#x}");
|
||||||
|
|
||||||
let ret_addr: GuestAddr = emu.read_return_address().unwrap();
|
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
||||||
log::debug!("Return address = {ret_addr:#x}");
|
log::debug!("Return address = {ret_addr:#x}");
|
||||||
emu.set_breakpoint(ret_addr);
|
qemu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
let input_addr = emu
|
let input_addr = qemu
|
||||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
log::debug!("Placing input at {input_addr:#x}");
|
log::debug!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
let stack_ptr: GuestAddr = emu.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 mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
@ -201,24 +201,29 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
let len = len as GuestReg;
|
let len = len as GuestReg;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.write_mem(input_addr, buf);
|
qemu.write_mem(input_addr, buf);
|
||||||
emu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
||||||
emu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
||||||
emu.write_return_address(ret_addr).unwrap();
|
qemu.write_return_address(ret_addr).unwrap();
|
||||||
emu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)
|
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
emu.write_function_argument(CallingConvention::Cdecl, 1, len)
|
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
emu.run();
|
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
|
process::exit(0)
|
||||||
|
}
|
||||||
|
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitKind::Ok
|
ExitKind::Ok
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut hooks = QemuHooks::new(
|
let mut hooks = QemuHooks::new(qemu, tuple_list!(QemuEdgeCoverageChildHelper::default(),));
|
||||||
emu.clone(),
|
|
||||||
tuple_list!(QemuEdgeCoverageChildHelper::default(),),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut executor = QemuForkExecutor::new(
|
let mut executor = QemuForkExecutor::new(
|
||||||
&mut hooks,
|
&mut hooks,
|
||||||
|
@ -20,14 +20,16 @@ use libafl::{
|
|||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
current_nanos,
|
current_nanos,
|
||||||
|
os::unix_signals::Signal,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
AsSlice,
|
AsSlice,
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
drcov::QemuDrCovHelper, elf::EasyElf, emu::Emulator, ArchExtras, CallingConvention, GuestAddr,
|
drcov::QemuDrCovHelper, elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg,
|
||||||
GuestReg, MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationAddressRangeFilter, Regs,
|
MmapPerms, Qemu, QemuExecutor, QemuExitReason, QemuHooks,
|
||||||
|
QemuInstrumentationAddressRangeFilter, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
|
|
||||||
@ -118,19 +120,19 @@ pub fn fuzz() {
|
|||||||
|
|
||||||
env::remove_var("LD_LIBRARY_PATH");
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let emu = Emulator::new(&options.args, &env).unwrap();
|
let qemu = Qemu::init(&options.args, &env).unwrap();
|
||||||
|
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap();
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
|
|
||||||
let test_one_input_ptr = elf
|
let test_one_input_ptr = elf
|
||||||
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
log::debug!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
emu.entry_break(test_one_input_ptr);
|
qemu.entry_break(test_one_input_ptr);
|
||||||
|
|
||||||
for m in emu.mappings() {
|
for m in qemu.mappings() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Mapping: 0x{:016x}-0x{:016x}, {}",
|
"Mapping: 0x{:016x}-0x{:016x}, {}",
|
||||||
m.start(),
|
m.start(),
|
||||||
@ -139,30 +141,38 @@ pub fn fuzz() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pc: GuestReg = emu.read_reg(Regs::Pc).unwrap();
|
let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
log::debug!("Break at {pc:#x}");
|
log::debug!("Break at {pc:#x}");
|
||||||
|
|
||||||
let ret_addr: GuestAddr = emu.read_return_address().unwrap();
|
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
||||||
log::debug!("Return address = {ret_addr:#x}");
|
log::debug!("Return address = {ret_addr:#x}");
|
||||||
|
|
||||||
emu.set_breakpoint(ret_addr);
|
qemu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
let input_addr = emu
|
let input_addr = qemu
|
||||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
log::debug!("Placing input at {input_addr:#x}");
|
log::debug!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
let stack_ptr: GuestAddr = emu.read_reg(Regs::Sp).unwrap();
|
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
|
|
||||||
let reset = |buf: &[u8], len: GuestReg| -> Result<(), String> {
|
let reset = |buf: &[u8], len: GuestReg| -> Result<(), String> {
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.write_mem(input_addr, buf);
|
qemu.write_mem(input_addr, buf);
|
||||||
emu.write_reg(Regs::Pc, test_one_input_ptr)?;
|
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;
|
||||||
emu.write_reg(Regs::Sp, stack_ptr)?;
|
qemu.write_reg(Regs::Sp, stack_ptr)?;
|
||||||
emu.write_return_address(ret_addr)?;
|
qemu.write_return_address(ret_addr)?;
|
||||||
emu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?;
|
qemu.write_function_argument(CallingConvention::Cdecl, 0, input_addr)?;
|
||||||
emu.write_function_argument(CallingConvention::Cdecl, 1, len)?;
|
qemu.write_function_argument(CallingConvention::Cdecl, 1, len)?;
|
||||||
emu.run();
|
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(Signal::SigInterrupt))) => {
|
||||||
|
process::exit(0)
|
||||||
|
}
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -218,7 +228,7 @@ pub fn fuzz() {
|
|||||||
let scheduler = QueueScheduler::new();
|
let scheduler = QueueScheduler::new();
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
let rangemap = emu
|
let rangemap = qemu
|
||||||
.mappings()
|
.mappings()
|
||||||
.filter_map(|m| {
|
.filter_map(|m| {
|
||||||
m.path()
|
m.path()
|
||||||
@ -241,7 +251,7 @@ pub fn fuzz() {
|
|||||||
coverage.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
|
coverage.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
|
||||||
|
|
||||||
let mut hooks = QemuHooks::new(
|
let mut hooks = QemuHooks::new(
|
||||||
emu.clone(),
|
qemu,
|
||||||
tuple_list!(QemuDrCovHelper::new(
|
tuple_list!(QemuDrCovHelper::new(
|
||||||
QemuInstrumentationAddressRangeFilter::None,
|
QemuInstrumentationAddressRangeFilter::None,
|
||||||
rangemap,
|
rangemap,
|
||||||
|
@ -11,11 +11,11 @@ use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list};
|
|||||||
#[cfg(feature = "injections")]
|
#[cfg(feature = "injections")]
|
||||||
use libafl_qemu::injections::QemuInjectionHelper;
|
use libafl_qemu::injections::QemuInjectionHelper;
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
asan::{init_with_asan, QemuAsanHelper},
|
asan::{init_qemu_with_asan, QemuAsanHelper},
|
||||||
cmplog::QemuCmpLogHelper,
|
cmplog::QemuCmpLogHelper,
|
||||||
edges::QemuEdgeCoverageHelper,
|
edges::QemuEdgeCoverageHelper,
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
ArchExtras, Emulator, GuestAddr, QemuInstrumentationAddressRangeFilter,
|
ArchExtras, GuestAddr, Qemu, QemuInstrumentationAddressRangeFilter,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -53,21 +53,18 @@ impl<'a> Client<'a> {
|
|||||||
.collect::<Vec<(String, String)>>()
|
.collect::<Vec<(String, String)>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_pc(emu: &Emulator) -> Result<GuestAddr, Error> {
|
fn start_pc(qemu: &Qemu) -> Result<GuestAddr, Error> {
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
|
||||||
|
|
||||||
let start_pc = elf
|
let start_pc = elf
|
||||||
.resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr())
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
.ok_or_else(|| Error::empty_optional("Symbol LLVMFuzzerTestOneInput not found"))?;
|
.ok_or_else(|| Error::empty_optional("Symbol LLVMFuzzerTestOneInput not found"))?;
|
||||||
Ok(start_pc)
|
Ok(start_pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::similar_names)] // elf != self
|
#[allow(clippy::similar_names)] // elf != self
|
||||||
fn coverage_filter(
|
fn coverage_filter(&self, qemu: &Qemu) -> Result<QemuInstrumentationAddressRangeFilter, Error> {
|
||||||
&self,
|
|
||||||
emu: &Emulator,
|
|
||||||
) -> Result<QemuInstrumentationAddressRangeFilter, Error> {
|
|
||||||
/* Conversion is required on 32-bit targets, but not on 64-bit ones */
|
/* Conversion is required on 32-bit targets, but not on 64-bit ones */
|
||||||
if let Some(includes) = &self.options.include {
|
if let Some(includes) = &self.options.include {
|
||||||
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
|
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
|
||||||
@ -91,9 +88,9 @@ impl<'a> Client<'a> {
|
|||||||
Ok(QemuInstrumentationAddressRangeFilter::DenyList(rules))
|
Ok(QemuInstrumentationAddressRangeFilter::DenyList(rules))
|
||||||
} else {
|
} else {
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
|
||||||
let range = elf
|
let range = elf
|
||||||
.get_section(".text", emu.load_addr())
|
.get_section(".text", qemu.load_addr())
|
||||||
.ok_or_else(|| Error::key_not_found("Failed to find .text section"))?;
|
.ok_or_else(|| Error::key_not_found("Failed to find .text section"))?;
|
||||||
Ok(QemuInstrumentationAddressRangeFilter::AllowList(vec![
|
Ok(QemuInstrumentationAddressRangeFilter::AllowList(vec![
|
||||||
range,
|
range,
|
||||||
@ -113,16 +110,16 @@ impl<'a> Client<'a> {
|
|||||||
let mut env = self.env();
|
let mut env = self.env();
|
||||||
log::debug!("ENV: {:#?}", env);
|
log::debug!("ENV: {:#?}", env);
|
||||||
|
|
||||||
let (emu, mut asan) = {
|
let (qemu, mut asan) = {
|
||||||
if self.options.is_asan_core(core_id) {
|
if self.options.is_asan_core(core_id) {
|
||||||
let (emu, asan) = init_with_asan(&mut args, &mut env)?;
|
let (emu, asan) = init_qemu_with_asan(&mut args, &mut env)?;
|
||||||
(emu, Some(asan))
|
(emu, Some(asan))
|
||||||
} else {
|
} else {
|
||||||
(Emulator::new(&args, &env)?, None)
|
(Qemu::init(&args, &env)?, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let start_pc = Self::start_pc(&emu)?;
|
let start_pc = Self::start_pc(&qemu)?;
|
||||||
log::debug!("start_pc @ {start_pc:#x}");
|
log::debug!("start_pc @ {start_pc:#x}");
|
||||||
|
|
||||||
#[cfg(not(feature = "injections"))]
|
#[cfg(not(feature = "injections"))]
|
||||||
@ -146,22 +143,22 @@ impl<'a> Client<'a> {
|
|||||||
|
|
||||||
let extra_tokens = injection_helper.as_ref().map(|h| h.tokens.clone());
|
let extra_tokens = injection_helper.as_ref().map(|h| h.tokens.clone());
|
||||||
|
|
||||||
emu.entry_break(start_pc);
|
qemu.entry_break(start_pc);
|
||||||
|
|
||||||
let ret_addr: GuestAddr = emu
|
let ret_addr: GuestAddr = qemu
|
||||||
.read_return_address()
|
.read_return_address()
|
||||||
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
|
||||||
log::debug!("ret_addr = {ret_addr:#x}");
|
log::debug!("ret_addr = {ret_addr:#x}");
|
||||||
emu.set_breakpoint(ret_addr);
|
qemu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
let is_asan = self.options.is_asan_core(core_id);
|
let is_asan = self.options.is_asan_core(core_id);
|
||||||
let is_cmplog = self.options.is_cmplog_core(core_id);
|
let is_cmplog = self.options.is_cmplog_core(core_id);
|
||||||
|
|
||||||
let edge_coverage_helper = QemuEdgeCoverageHelper::new(self.coverage_filter(&emu)?);
|
let edge_coverage_helper = QemuEdgeCoverageHelper::new(self.coverage_filter(&qemu)?);
|
||||||
|
|
||||||
let instance = Instance::builder()
|
let instance = Instance::builder()
|
||||||
.options(self.options)
|
.options(self.options)
|
||||||
.emu(&emu)
|
.qemu(&qemu)
|
||||||
.mgr(mgr)
|
.mgr(mgr)
|
||||||
.core_id(core_id)
|
.core_id(core_id)
|
||||||
.extra_tokens(extra_tokens);
|
.extra_tokens(extra_tokens);
|
||||||
|
@ -4,10 +4,10 @@ use libafl::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use libafl_bolts::AsSlice;
|
use libafl_bolts::AsSlice;
|
||||||
use libafl_qemu::{ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, Regs};
|
use libafl_qemu::{ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, Regs};
|
||||||
|
|
||||||
pub struct Harness<'a> {
|
pub struct Harness<'a> {
|
||||||
emu: &'a Emulator,
|
qemu: &'a Qemu,
|
||||||
input_addr: GuestAddr,
|
input_addr: GuestAddr,
|
||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
stack_ptr: GuestAddr,
|
stack_ptr: GuestAddr,
|
||||||
@ -17,25 +17,25 @@ pub struct Harness<'a> {
|
|||||||
pub const MAX_INPUT_SIZE: usize = 1_048_576; // 1MB
|
pub const MAX_INPUT_SIZE: usize = 1_048_576; // 1MB
|
||||||
|
|
||||||
impl<'a> Harness<'a> {
|
impl<'a> Harness<'a> {
|
||||||
pub fn new(emu: &Emulator) -> Result<Harness, Error> {
|
pub fn new(qemu: &Qemu) -> Result<Harness, Error> {
|
||||||
let input_addr = emu
|
let input_addr = qemu
|
||||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to map input buffer: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to map input buffer: {e:}")))?;
|
||||||
|
|
||||||
let pc: GuestReg = emu
|
let pc: GuestReg = qemu
|
||||||
.read_reg(Regs::Pc)
|
.read_reg(Regs::Pc)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to read PC: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to read PC: {e:}")))?;
|
||||||
|
|
||||||
let stack_ptr: GuestAddr = emu
|
let stack_ptr: GuestAddr = qemu
|
||||||
.read_reg(Regs::Sp)
|
.read_reg(Regs::Sp)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to read stack pointer: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to read stack pointer: {e:}")))?;
|
||||||
|
|
||||||
let ret_addr: GuestAddr = emu
|
let ret_addr: GuestAddr = qemu
|
||||||
.read_return_address()
|
.read_return_address()
|
||||||
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:}")))?;
|
||||||
|
|
||||||
Ok(Harness {
|
Ok(Harness {
|
||||||
emu,
|
qemu,
|
||||||
input_addr,
|
input_addr,
|
||||||
pc,
|
pc,
|
||||||
stack_ptr,
|
stack_ptr,
|
||||||
@ -58,29 +58,29 @@ impl<'a> Harness<'a> {
|
|||||||
}
|
}
|
||||||
let len = len as GuestReg;
|
let len = len as GuestReg;
|
||||||
|
|
||||||
unsafe { self.emu.write_mem(self.input_addr, buf) };
|
unsafe { self.qemu.write_mem(self.input_addr, buf) };
|
||||||
|
|
||||||
self.emu
|
self.qemu
|
||||||
.write_reg(Regs::Pc, self.pc)
|
.write_reg(Regs::Pc, self.pc)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write PC: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write PC: {e:}")))?;
|
||||||
|
|
||||||
self.emu
|
self.qemu
|
||||||
.write_reg(Regs::Sp, self.stack_ptr)
|
.write_reg(Regs::Sp, self.stack_ptr)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write SP: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write SP: {e:}")))?;
|
||||||
|
|
||||||
self.emu
|
self.qemu
|
||||||
.write_return_address(self.ret_addr)
|
.write_return_address(self.ret_addr)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:}")))?;
|
||||||
|
|
||||||
self.emu
|
self.qemu
|
||||||
.write_function_argument(CallingConvention::Cdecl, 0, self.input_addr)
|
.write_function_argument(CallingConvention::Cdecl, 0, self.input_addr)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:}")))?;
|
||||||
|
|
||||||
self.emu
|
self.qemu
|
||||||
.write_function_argument(CallingConvention::Cdecl, 1, len)
|
.write_function_argument(CallingConvention::Cdecl, 1, len)
|
||||||
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:}")))?;
|
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:}")))?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = self.emu.run();
|
let _ = self.qemu.run();
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ use libafl_qemu::{
|
|||||||
cmplog::CmpLogObserver,
|
cmplog::CmpLogObserver,
|
||||||
edges::{edges_map_mut_slice, MAX_EDGES_NUM},
|
edges::{edges_map_mut_slice, MAX_EDGES_NUM},
|
||||||
helper::QemuHelperTuple,
|
helper::QemuHelperTuple,
|
||||||
Emulator, QemuExecutor, QemuHooks,
|
Qemu, QemuExecutor, QemuHooks,
|
||||||
};
|
};
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ pub type ClientMgr<M> =
|
|||||||
#[derive(TypedBuilder)]
|
#[derive(TypedBuilder)]
|
||||||
pub struct Instance<'a, M: Monitor> {
|
pub struct Instance<'a, M: Monitor> {
|
||||||
options: &'a FuzzerOptions,
|
options: &'a FuzzerOptions,
|
||||||
emu: &'a Emulator,
|
qemu: &'a Qemu,
|
||||||
mgr: ClientMgr<M>,
|
mgr: ClientMgr<M>,
|
||||||
core_id: CoreId,
|
core_id: CoreId,
|
||||||
extra_tokens: Option<Vec<String>>,
|
extra_tokens: Option<Vec<String>>,
|
||||||
@ -72,7 +72,7 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
|||||||
where
|
where
|
||||||
QT: QemuHelperTuple<ClientState> + Debug,
|
QT: QemuHelperTuple<ClientState> + Debug,
|
||||||
{
|
{
|
||||||
let mut hooks = QemuHooks::new(self.emu.clone(), helpers);
|
let mut hooks = QemuHooks::new(self.qemu.clone(), helpers);
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = unsafe {
|
let edges_observer = unsafe {
|
||||||
@ -147,7 +147,7 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
|||||||
|
|
||||||
state.add_metadata(tokens);
|
state.add_metadata(tokens);
|
||||||
|
|
||||||
let harness = Harness::new(self.emu)?;
|
let harness = Harness::new(self.qemu)?;
|
||||||
let mut harness = |input: &BytesInput| harness.run(input);
|
let mut harness = |input: &BytesInput| harness.run(input);
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
@ -18,4 +18,5 @@ codegen-units = 1
|
|||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
||||||
|
libafl_qemu_sys = { path = "../../libafl_qemu/libafl_qemu_sys", features = ["arm", "systemmode"] }
|
||||||
env_logger = "*"
|
env_logger = "*"
|
||||||
|
@ -22,6 +22,7 @@ use libafl::{
|
|||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
core_affinity::Cores,
|
core_affinity::Cores,
|
||||||
current_nanos,
|
current_nanos,
|
||||||
|
os::unix_signals::Signal,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
@ -30,9 +31,10 @@ use libafl_bolts::{
|
|||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
|
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
|
||||||
elf::EasyElf,
|
elf::EasyElf,
|
||||||
emu::Emulator,
|
emu::Qemu,
|
||||||
GuestPhysAddr, QemuExecutor, QemuHooks, Regs,
|
QemuExecutor, QemuExitReason, QemuExitReasonError, QemuHooks, QemuShutdownCause, Regs,
|
||||||
};
|
};
|
||||||
|
use libafl_qemu_sys::GuestPhysAddr;
|
||||||
|
|
||||||
pub static mut MAX_INPUT_SIZE: usize = 50;
|
pub static mut MAX_INPUT_SIZE: usize = 50;
|
||||||
|
|
||||||
@ -83,17 +85,20 @@ pub fn fuzz() {
|
|||||||
// Initialize QEMU
|
// Initialize QEMU
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let emu = Emulator::new(&args, &env).unwrap();
|
let qemu = Qemu::init(&args, &env).unwrap();
|
||||||
|
|
||||||
emu.set_breakpoint(main_addr);
|
qemu.set_breakpoint(main_addr);
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.run().unwrap();
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
}
|
}
|
||||||
emu.remove_breakpoint(main_addr);
|
}
|
||||||
|
qemu.remove_breakpoint(main_addr);
|
||||||
|
|
||||||
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
qemu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||||
|
|
||||||
let devices = emu.list_devices();
|
let devices = qemu.list_devices();
|
||||||
println!("Devices = {devices:?}");
|
println!("Devices = {devices:?}");
|
||||||
|
|
||||||
// let saved_cpu_states: Vec<_> = (0..emu.num_cpus())
|
// let saved_cpu_states: Vec<_> = (0..emu.num_cpus())
|
||||||
@ -102,7 +107,7 @@ pub fn fuzz() {
|
|||||||
|
|
||||||
// emu.save_snapshot("start", true);
|
// emu.save_snapshot("start", true);
|
||||||
|
|
||||||
let snap = emu.create_fast_snapshot(true);
|
let snap = qemu.create_fast_snapshot(true);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |input: &BytesInput| {
|
||||||
@ -115,13 +120,20 @@ pub fn fuzz() {
|
|||||||
// len = MAX_INPUT_SIZE;
|
// len = MAX_INPUT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
emu.write_phys_mem(input_addr, buf);
|
qemu.write_phys_mem(input_addr, buf);
|
||||||
|
|
||||||
let _ = emu.run();
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
||||||
|
Signal::SigInterrupt,
|
||||||
|
))) => process::exit(0),
|
||||||
|
Err(QemuExitReasonError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
|
||||||
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
||||||
let mut pcs = (0..emu.num_cpus())
|
let mut pcs = (0..qemu.num_cpus())
|
||||||
.map(|i| emu.cpu_from_index(i))
|
.map(|i| qemu.cpu_from_index(i))
|
||||||
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
||||||
let ret = match pcs
|
let ret = match pcs
|
||||||
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
||||||
@ -139,7 +151,7 @@ pub fn fuzz() {
|
|||||||
// emu.load_snapshot("start", true);
|
// emu.load_snapshot("start", true);
|
||||||
|
|
||||||
// OPTION 3: restore a fast devices+mem snapshot
|
// OPTION 3: restore a fast devices+mem snapshot
|
||||||
emu.restore_fast_snapshot(snap);
|
qemu.restore_fast_snapshot(snap);
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
@ -194,7 +206,8 @@ pub fn fuzz() {
|
|||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
let mut hooks = QemuHooks::new(emu.clone(), tuple_list!(QemuEdgeCoverageHelper::default()));
|
let mut hooks =
|
||||||
|
QemuHooks::new(qemu.clone(), tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||||
|
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
|
1
fuzzers/qemu_systemmode_breakpoints/.gitignore
vendored
Normal file
1
fuzzers/qemu_systemmode_breakpoints/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.qcow2
|
21
fuzzers/qemu_systemmode_breakpoints/Cargo.toml
Normal file
21
fuzzers/qemu_systemmode_breakpoints/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "qemu_systemmode_breakpoints"
|
||||||
|
version = "0.11.1"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>", "Romain Malmain <romain.malmain@pm.me>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
incremental = true
|
||||||
|
debug = true
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
||||||
|
env_logger = "*"
|
26
fuzzers/qemu_systemmode_breakpoints/README.md
Normal file
26
fuzzers/qemu_systemmode_breakpoints/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Qemu systemmode with launcher
|
||||||
|
|
||||||
|
This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build this example, run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
cd example; sh build.sh; cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...).
|
||||||
|
Create on and then run the fuzzer:
|
||||||
|
```bash
|
||||||
|
# create an image
|
||||||
|
qemu-img create -f qcow2 dummy.qcow2 32M
|
||||||
|
# run the fuzzer
|
||||||
|
KERNEL=./example/example.elf target/release/qemu_systemmode -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
||||||
|
```
|
||||||
|
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
|
1
fuzzers/qemu_systemmode_breakpoints/corpus/random
Normal file
1
fuzzers/qemu_systemmode_breakpoints/corpus/random
Normal file
@ -0,0 +1 @@
|
|||||||
|
<1F>y¯»Jv•<>ֵָ
l<>ֵp†אZGײ…rֶs‚Yֿס‘ֻ—CMRױ÷}קS7;Iֹo¿7ָ6זlע1<D7A2>˜1R¯¬ף׳ָ^־<>p¬ד¯>G÷&_¬<5F>˜|1½¾;ג¥ינװסr<D7A1>ח<EFBFBD>o4ג¼ֶ¯ֽU<D6BD>ׁE<`ש÷²÷ּLע"<22>ף6Vֶ·k<1D>4/³"–<>צtכקֺf7ײF¯°ְ<C2B0><D6B0>d]<5D>®%ץ|8•#wµוּNU›ƒnש‹8€<>%¼4ַo־<6F>ֻ—–
|
BIN
fuzzers/qemu_systemmode_breakpoints/corpus/zero
Normal file
BIN
fuzzers/qemu_systemmode_breakpoints/corpus/zero
Normal file
Binary file not shown.
2
fuzzers/qemu_systemmode_breakpoints/example/build.sh
Executable file
2
fuzzers/qemu_systemmode_breakpoints/example/build.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
arm-none-eabi-gcc -ggdb -ffreestanding -nostartfiles -lgcc -T mps2_m3.ld -mcpu=cortex-m3 main.c startup.c -o example.elf
|
33
fuzzers/qemu_systemmode_breakpoints/example/main.c
Normal file
33
fuzzers/qemu_systemmode_breakpoints/example/main.c
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
int __attribute__((noinline)) BREAKPOINT() {
|
||||||
|
for (;;) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) {
|
||||||
|
if (Data[3] == 0) {
|
||||||
|
while (1) {}
|
||||||
|
} // cause a timeout
|
||||||
|
for (int i = 0; i < Size; i++) {
|
||||||
|
// if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to
|
||||||
|
// crash
|
||||||
|
for (int j = i + 1; j < Size; j++) {
|
||||||
|
if (Data[j] == 0) { continue; }
|
||||||
|
if (Data[j] > Data[i]) {
|
||||||
|
int tmp = Data[i];
|
||||||
|
Data[i] = Data[j];
|
||||||
|
Data[j] = tmp;
|
||||||
|
if (Data[i] <= 100) { j--; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BREAKPOINT();
|
||||||
|
}
|
||||||
|
unsigned int FUZZ_INPUT[] = {
|
||||||
|
101, 201, 700, 230, 860, 234, 980, 200, 340, 678, 230, 134, 900,
|
||||||
|
236, 900, 123, 800, 123, 658, 607, 246, 804, 567, 568, 207, 407,
|
||||||
|
246, 678, 457, 892, 834, 456, 878, 246, 699, 854, 234, 844, 290,
|
||||||
|
125, 324, 560, 852, 928, 910, 790, 853, 345, 234, 586,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
||||||
|
}
|
143
fuzzers/qemu_systemmode_breakpoints/example/mps2_m3.ld
Normal file
143
fuzzers/qemu_systemmode_breakpoints/example/mps2_m3.ld
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS V202112.00
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
RAM (xrw) : ORIGIN = 0x00000000, LENGTH = 4M
|
||||||
|
/* Originally */
|
||||||
|
/* FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M */
|
||||||
|
/* RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4M */
|
||||||
|
}
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
_Min_Heap_Size = 0x300000 ; /* Required amount of heap. */
|
||||||
|
_Min_Stack_Size = 0x4000 ; /* Required amount of stack. */
|
||||||
|
M_VECTOR_RAM_SIZE = (16 + 48) * 4;
|
||||||
|
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
__vector_table = .;
|
||||||
|
KEEP(*(.isr_vector))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > RAM /* FLASH */
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text*)
|
||||||
|
KEEP (*(.init))
|
||||||
|
KEEP (*(.fini))
|
||||||
|
KEEP(*(.eh_frame))
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .;
|
||||||
|
} > RAM /* FLASH */
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >RAM /* FLASH */
|
||||||
|
|
||||||
|
.ARM :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||||
|
__exidx_end = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >RAM /* FLASH */
|
||||||
|
|
||||||
|
.interrupts_ram :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__VECTOR_RAM__ = .;
|
||||||
|
__interrupts_ram_start__ = .;
|
||||||
|
. += M_VECTOR_RAM_SIZE;
|
||||||
|
. = ALIGN(4);
|
||||||
|
__interrupts_ram_end = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
.data : /* AT ( _sidata ) */
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .;
|
||||||
|
} > RAM /* RAM AT > FLASH */
|
||||||
|
|
||||||
|
.uninitialized (NOLOAD):
|
||||||
|
{
|
||||||
|
. = ALIGN(32);
|
||||||
|
__uninitialized_start = .;
|
||||||
|
*(.uninitialized)
|
||||||
|
KEEP(*(.keep.uninitialized))
|
||||||
|
. = ALIGN(32);
|
||||||
|
__uninitialized_end = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sbss = .;
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
.heap :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE ( end = . );
|
||||||
|
PROVIDE ( _end = . );
|
||||||
|
_heap_bottom = .;
|
||||||
|
. = . + _Min_Heap_Size;
|
||||||
|
_heap_top = .;
|
||||||
|
. = . + _Min_Stack_Size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/* Set stack top to end of RAM, and stack limit move down by
|
||||||
|
* size of stack_dummy section */
|
||||||
|
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
__StackLimit = __StackTop - _Min_Stack_Size;
|
||||||
|
PROVIDE(__stack = __StackTop);
|
||||||
|
|
||||||
|
/* Check if data + heap + stack exceeds RAM limit */
|
||||||
|
ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack")
|
||||||
|
}
|
||||||
|
|
107
fuzzers/qemu_systemmode_breakpoints/example/startup.c
Normal file
107
fuzzers/qemu_systemmode_breakpoints/example/startup.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS V202112.00
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
|
||||||
|
extern int main();
|
||||||
|
|
||||||
|
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
|
||||||
|
|
||||||
|
/* Prevent optimization so gcc does not replace code with memcpy */
|
||||||
|
__attribute__((optimize("O0"))) __attribute__((naked)) void Reset_Handler(
|
||||||
|
void) {
|
||||||
|
/* set stack pointer */
|
||||||
|
__asm volatile("ldr r0, =_estack");
|
||||||
|
__asm volatile("mov sp, r0");
|
||||||
|
|
||||||
|
/* copy .data section from flash to RAM */
|
||||||
|
// Not needed for this example, see linker script
|
||||||
|
// for( uint32_t * src = &_sidata, * dest = &_sdata; dest < &_edata; )
|
||||||
|
// {
|
||||||
|
// *dest++ = *src++;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* zero out .bss section */
|
||||||
|
for (uint32_t *dest = &_sbss; dest < &_ebss;) {
|
||||||
|
*dest++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* jump to board initialisation */
|
||||||
|
void _start(void);
|
||||||
|
_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t *isr_vector[] __attribute__((section(".isr_vector"))) = {
|
||||||
|
(uint32_t *)&_estack,
|
||||||
|
(uint32_t *)&Reset_Handler, /* Reset -15 */
|
||||||
|
0, /* NMI_Handler -14 */
|
||||||
|
0, /* HardFault_Handler -13 */
|
||||||
|
0, /* MemManage_Handler -12 */
|
||||||
|
0, /* BusFault_Handler -11 */
|
||||||
|
0, /* UsageFault_Handler -10 */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved -6 */
|
||||||
|
0, /* SVC_Handler -5 */
|
||||||
|
0, /* DebugMon_Handler -4 */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* PendSV handler -2 */
|
||||||
|
0, /* SysTick_Handler -1 */
|
||||||
|
0, /* uart0 receive 0 */
|
||||||
|
0, /* uart0 transmit */
|
||||||
|
0, /* uart1 receive */
|
||||||
|
0, /* uart1 transmit */
|
||||||
|
0, /* uart 2 receive */
|
||||||
|
0, /* uart 2 transmit */
|
||||||
|
0, /* GPIO 0 combined interrupt */
|
||||||
|
0, /* GPIO 2 combined interrupt */
|
||||||
|
0, /* Timer 0 */
|
||||||
|
0, /* Timer 1 */
|
||||||
|
0, /* Dial Timer */
|
||||||
|
0, /* SPI0 SPI1 */
|
||||||
|
0, /* uart overflow 1, 2,3 */
|
||||||
|
0, /* Ethernet 13 */
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((naked)) void exit(__attribute__((unused)) int status) {
|
||||||
|
/* Force qemu to exit using ARM Semihosting */
|
||||||
|
__asm volatile(
|
||||||
|
"mov r1, r0\n"
|
||||||
|
"cmp r1, #0\n"
|
||||||
|
"bne .notclean\n"
|
||||||
|
"ldr r1, =0x20026\n" /* ADP_Stopped_ApplicationExit, a clean exit */
|
||||||
|
".notclean:\n"
|
||||||
|
"movs r0, #0x18\n" /* SYS_EXIT */
|
||||||
|
"bkpt 0xab\n"
|
||||||
|
"end: b end\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _start(void) {
|
||||||
|
main();
|
||||||
|
exit(0);
|
||||||
|
}
|
272
fuzzers/qemu_systemmode_breakpoints/src/fuzzer.rs
Normal file
272
fuzzers/qemu_systemmode_breakpoints/src/fuzzer.rs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels
|
||||||
|
//!
|
||||||
|
use core::{ptr::addr_of_mut, time::Duration};
|
||||||
|
use std::{env, path::PathBuf, process};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
|
events::{launcher::Launcher, EventConfig},
|
||||||
|
executors::ExitKind,
|
||||||
|
feedback_or, feedback_or_fast,
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::BytesInput,
|
||||||
|
monitors::MultiMonitor,
|
||||||
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
|
observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
||||||
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
|
stages::{CalibrationStage, StdMutationalStage},
|
||||||
|
state::{HasCorpus, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::{
|
||||||
|
core_affinity::Cores,
|
||||||
|
current_nanos,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
breakpoint::Breakpoint,
|
||||||
|
command::{Command, EmulatorMemoryChunk, EndCommand, StartCommand},
|
||||||
|
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
|
||||||
|
elf::EasyElf,
|
||||||
|
emu::Emulator,
|
||||||
|
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
||||||
|
EmuExitReasonError, FastSnapshotManager, GuestPhysAddr, GuestReg, HandlerError, HandlerResult,
|
||||||
|
QemuHooks, StdEmuExitHandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
// use libafl_qemu::QemuSnapshotBuilder; // for normal qemu snapshot
|
||||||
|
|
||||||
|
pub static mut MAX_INPUT_SIZE: usize = 50;
|
||||||
|
|
||||||
|
pub fn fuzz() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||||
|
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||||
|
};
|
||||||
|
// Hardcoded parameters
|
||||||
|
let timeout = Duration::from_secs(3);
|
||||||
|
let broker_port = 1337;
|
||||||
|
let cores = Cores::from_cmdline("1").unwrap();
|
||||||
|
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||||
|
let objective_dir = PathBuf::from("./crashes");
|
||||||
|
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let elf = EasyElf::from_file(
|
||||||
|
env::var("KERNEL").expect("KERNEL env not set"),
|
||||||
|
&mut elf_buffer,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let input_addr = elf
|
||||||
|
.resolve_symbol(
|
||||||
|
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr;
|
||||||
|
println!("FUZZ_INPUT @ {input_addr:#x}");
|
||||||
|
|
||||||
|
let main_addr = elf
|
||||||
|
.resolve_symbol("main", 0)
|
||||||
|
.expect("Symbol main not found");
|
||||||
|
println!("main address = {main_addr:#x}");
|
||||||
|
|
||||||
|
let breakpoint = elf
|
||||||
|
.resolve_symbol(
|
||||||
|
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Symbol or env BREAKPOINT not found");
|
||||||
|
println!("Breakpoint address = {breakpoint:#x}");
|
||||||
|
|
||||||
|
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||||
|
// Initialize QEMU
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
|
||||||
|
// Choose Snapshot Builder
|
||||||
|
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
|
||||||
|
let emu_snapshot_manager = FastSnapshotManager::new(false);
|
||||||
|
|
||||||
|
// Choose Exit Handler
|
||||||
|
let emu_exit_handler = StdEmuExitHandler::new(emu_snapshot_manager);
|
||||||
|
|
||||||
|
// Create emulator
|
||||||
|
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap();
|
||||||
|
|
||||||
|
// Set breakpoints of interest with corresponding commands.
|
||||||
|
emu.add_breakpoint(
|
||||||
|
Breakpoint::with_command(
|
||||||
|
main_addr,
|
||||||
|
Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::phys(
|
||||||
|
input_addr,
|
||||||
|
unsafe { MAX_INPUT_SIZE } as GuestReg,
|
||||||
|
None,
|
||||||
|
))),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
emu.add_breakpoint(
|
||||||
|
Breakpoint::with_command(
|
||||||
|
breakpoint,
|
||||||
|
Command::EndCommand(EndCommand::new(Some(ExitKind::Ok))),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
let devices = emu.list_devices();
|
||||||
|
println!("Devices = {:?}", devices);
|
||||||
|
|
||||||
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
|
let mut harness =
|
||||||
|
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
||||||
|
match emu.run(input, qemu_executor_state) {
|
||||||
|
Ok(handler_result) => match handler_result {
|
||||||
|
HandlerResult::UnhandledExit(unhandled_exit) => {
|
||||||
|
panic!("Unhandled exit: {}", unhandled_exit)
|
||||||
|
}
|
||||||
|
HandlerResult::EndOfRun(exit_kind) => return exit_kind,
|
||||||
|
HandlerResult::Interrupted => {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(handler_error) => match handler_error {
|
||||||
|
HandlerError::QemuExitReasonError(emu_exit_reason_error) => {
|
||||||
|
match emu_exit_reason_error {
|
||||||
|
EmuExitReasonError::UnknownKind => panic!("unknown kind"),
|
||||||
|
EmuExitReasonError::UnexpectedExit => return ExitKind::Crash,
|
||||||
|
_ => {
|
||||||
|
panic!("Emu Exit unhandled error: {:?}", emu_exit_reason_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Unhandled error: {:?}", handler_error),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an observation channel using the coverage map
|
||||||
|
let edges_observer = unsafe {
|
||||||
|
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
|
||||||
|
"edges",
|
||||||
|
edges_map_mut_slice(),
|
||||||
|
addr_of_mut!(MAX_EDGES_NUM),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::tracking(&edges_observer, true, true),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// A feedback to choose if an input is a solution or not
|
||||||
|
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(objective_dir.clone()).unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// The feedbacks can report the data that should persist in the State.
|
||||||
|
&mut feedback,
|
||||||
|
// Same for objective feedbacks
|
||||||
|
&mut objective,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
let mut hooks = QemuHooks::new(
|
||||||
|
emu.qemu().clone(),
|
||||||
|
tuple_list!(QemuEdgeCoverageHelper::default()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup an havoc mutator with a mutational stage
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
|
let calibration_feedback = MaxMapFeedback::tracking(&edges_observer, true, true);
|
||||||
|
let mut stages = tuple_list!(
|
||||||
|
StdMutationalStage::new(mutator),
|
||||||
|
CalibrationStage::new(&calibration_feedback)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a QEMU in-process executor
|
||||||
|
let mut executor = StatefulQemuExecutor::new(
|
||||||
|
&mut hooks,
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(edges_observer, time_observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
.expect("Failed to create QemuExecutor");
|
||||||
|
|
||||||
|
// Instead of calling the timeout handler and restart the process, trigger a breakpoint ASAP
|
||||||
|
executor.break_on_timeout();
|
||||||
|
|
||||||
|
if state.must_load_initial_inputs() {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
.unwrap();
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// The shared memory allocator
|
||||||
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
// The stats reporter for the broker
|
||||||
|
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||||
|
|
||||||
|
// let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||||
|
// let mut mgr = SimpleEventManager::new(monitor);
|
||||||
|
// run_client(None, mgr, 0);
|
||||||
|
|
||||||
|
// Build and run a Launcher
|
||||||
|
match Launcher::builder()
|
||||||
|
.shmem_provider(shmem_provider)
|
||||||
|
.broker_port(broker_port)
|
||||||
|
.configuration(EventConfig::from_build_id())
|
||||||
|
.monitor(monitor)
|
||||||
|
.run_client(&mut run_client)
|
||||||
|
.cores(&cores)
|
||||||
|
// .stdout_file(Some("/dev/null"))
|
||||||
|
.build()
|
||||||
|
.launch()
|
||||||
|
{
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||||
|
Err(err) => panic!("Failed to run launcher: {err:?}"),
|
||||||
|
}
|
||||||
|
}
|
13
fuzzers/qemu_systemmode_breakpoints/src/main.rs
Normal file
13
fuzzers/qemu_systemmode_breakpoints/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod fuzzer;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn main() {
|
||||||
|
fuzzer::fuzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||||
|
}
|
1
fuzzers/qemu_systemmode_sync_backdoor/.gitignore
vendored
Normal file
1
fuzzers/qemu_systemmode_sync_backdoor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.qcow2
|
21
fuzzers/qemu_systemmode_sync_backdoor/Cargo.toml
Normal file
21
fuzzers/qemu_systemmode_sync_backdoor/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "qemu_systemmode_sync_backdoor"
|
||||||
|
version = "0.11.1"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>", "Romain Malmain <romain.malmain@pm.me>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std"]
|
||||||
|
std = []
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
incremental = true
|
||||||
|
debug = true
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
||||||
|
env_logger = "*"
|
26
fuzzers/qemu_systemmode_sync_backdoor/README.md
Normal file
26
fuzzers/qemu_systemmode_sync_backdoor/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Qemu systemmode with launcher
|
||||||
|
|
||||||
|
This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build this example, run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
cd example; sh build.sh; cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...).
|
||||||
|
Create on and then run the fuzzer:
|
||||||
|
```bash
|
||||||
|
# create an image
|
||||||
|
qemu-img create -f qcow2 dummy.qcow2 32M
|
||||||
|
# run the fuzzer
|
||||||
|
KERNEL=./example/example.elf target/release/qemu_systemmode -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
||||||
|
```
|
||||||
|
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
|
1
fuzzers/qemu_systemmode_sync_backdoor/corpus/random
Normal file
1
fuzzers/qemu_systemmode_sync_backdoor/corpus/random
Normal file
@ -0,0 +1 @@
|
|||||||
|
<1F>y¯»Jv•<>ֵָ
l<>ֵp†אZGײ…rֶs‚Yֿס‘ֻ—CMRױ÷}קS7;Iֹo¿7ָ6זlע1<D7A2>˜1R¯¬ף׳ָ^־<>p¬ד¯>G÷&_¬<5F>˜|1½¾;ג¥ינװסr<D7A1>ח<EFBFBD>o4ג¼ֶ¯ֽU<D6BD>ׁE<`ש÷²÷ּLע"<22>ף6Vֶ·k<1D>4/³"–<>צtכקֺf7ײF¯°ְ<C2B0><D6B0>d]<5D>®%ץ|8•#wµוּNU›ƒnש‹8€<>%¼4ַo־<6F>ֻ—–
|
BIN
fuzzers/qemu_systemmode_sync_backdoor/corpus/zero
Normal file
BIN
fuzzers/qemu_systemmode_sync_backdoor/corpus/zero
Normal file
Binary file not shown.
2
fuzzers/qemu_systemmode_sync_backdoor/example/build.sh
Executable file
2
fuzzers/qemu_systemmode_sync_backdoor/example/build.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
arm-none-eabi-gcc -ggdb -ffreestanding -nostartfiles -lgcc -T mps2_m3.ld -mcpu=cortex-m3 -I../../../libafl_qemu/runtime main.c startup.c -o example.elf
|
37
fuzzers/qemu_systemmode_sync_backdoor/example/main.c
Normal file
37
fuzzers/qemu_systemmode_sync_backdoor/example/main.c
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "libafl_exit.h"
|
||||||
|
|
||||||
|
int __attribute__((noinline)) BREAKPOINT() {
|
||||||
|
for (;;) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(unsigned int *Data, unsigned int Size) {
|
||||||
|
LIBAFL_EXIT_START_PHYS((unsigned int) Data, Size);
|
||||||
|
// if (Data[3] == 0) {
|
||||||
|
// while (1) {}
|
||||||
|
// } // cause a timeout
|
||||||
|
for (int i = 0; i < Size; i++) {
|
||||||
|
// if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to
|
||||||
|
// crash
|
||||||
|
for (int j = i + 1; j < Size; j++) {
|
||||||
|
if (Data[j] == 0) { continue; }
|
||||||
|
if (Data[j] > Data[i]) {
|
||||||
|
int tmp = Data[i];
|
||||||
|
Data[i] = Data[j];
|
||||||
|
Data[j] = tmp;
|
||||||
|
if (Data[i] <= 100) { j--; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LIBAFL_EXIT_END(LIBAFL_EXIT_END_OK);
|
||||||
|
return BREAKPOINT();
|
||||||
|
}
|
||||||
|
unsigned int FUZZ_INPUT[] = {
|
||||||
|
101, 201, 700, 230, 860, 234, 980, 200, 340, 678, 230, 134, 900,
|
||||||
|
236, 900, 123, 800, 123, 658, 607, 246, 804, 567, 568, 207, 407,
|
||||||
|
246, 678, 457, 892, 834, 456, 878, 246, 699, 854, 234, 844, 290,
|
||||||
|
125, 324, 560, 852, 928, 910, 790, 853, 345, 234, 586,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
||||||
|
}
|
143
fuzzers/qemu_systemmode_sync_backdoor/example/mps2_m3.ld
Normal file
143
fuzzers/qemu_systemmode_sync_backdoor/example/mps2_m3.ld
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS V202112.00
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
RAM (xrw) : ORIGIN = 0x00000000, LENGTH = 4M
|
||||||
|
/* Originally */
|
||||||
|
/* FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M */
|
||||||
|
/* RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4M */
|
||||||
|
}
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
_Min_Heap_Size = 0x300000 ; /* Required amount of heap. */
|
||||||
|
_Min_Stack_Size = 0x4000 ; /* Required amount of stack. */
|
||||||
|
M_VECTOR_RAM_SIZE = (16 + 48) * 4;
|
||||||
|
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
__vector_table = .;
|
||||||
|
KEEP(*(.isr_vector))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > RAM /* FLASH */
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text*)
|
||||||
|
KEEP (*(.init))
|
||||||
|
KEEP (*(.fini))
|
||||||
|
KEEP(*(.eh_frame))
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .;
|
||||||
|
} > RAM /* FLASH */
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >RAM /* FLASH */
|
||||||
|
|
||||||
|
.ARM :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||||
|
__exidx_end = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >RAM /* FLASH */
|
||||||
|
|
||||||
|
.interrupts_ram :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__VECTOR_RAM__ = .;
|
||||||
|
__interrupts_ram_start__ = .;
|
||||||
|
. += M_VECTOR_RAM_SIZE;
|
||||||
|
. = ALIGN(4);
|
||||||
|
__interrupts_ram_end = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
.data : /* AT ( _sidata ) */
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .;
|
||||||
|
} > RAM /* RAM AT > FLASH */
|
||||||
|
|
||||||
|
.uninitialized (NOLOAD):
|
||||||
|
{
|
||||||
|
. = ALIGN(32);
|
||||||
|
__uninitialized_start = .;
|
||||||
|
*(.uninitialized)
|
||||||
|
KEEP(*(.keep.uninitialized))
|
||||||
|
. = ALIGN(32);
|
||||||
|
__uninitialized_end = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sbss = .;
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
.heap :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE ( end = . );
|
||||||
|
PROVIDE ( _end = . );
|
||||||
|
_heap_bottom = .;
|
||||||
|
. = . + _Min_Heap_Size;
|
||||||
|
_heap_top = .;
|
||||||
|
. = . + _Min_Stack_Size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/* Set stack top to end of RAM, and stack limit move down by
|
||||||
|
* size of stack_dummy section */
|
||||||
|
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
__StackLimit = __StackTop - _Min_Stack_Size;
|
||||||
|
PROVIDE(__stack = __StackTop);
|
||||||
|
|
||||||
|
/* Check if data + heap + stack exceeds RAM limit */
|
||||||
|
ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack")
|
||||||
|
}
|
||||||
|
|
107
fuzzers/qemu_systemmode_sync_backdoor/example/startup.c
Normal file
107
fuzzers/qemu_systemmode_sync_backdoor/example/startup.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS V202112.00
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
|
||||||
|
extern int main();
|
||||||
|
|
||||||
|
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
|
||||||
|
|
||||||
|
/* Prevent optimization so gcc does not replace code with memcpy */
|
||||||
|
__attribute__((optimize("O0"))) __attribute__((naked)) void Reset_Handler(
|
||||||
|
void) {
|
||||||
|
/* set stack pointer */
|
||||||
|
__asm volatile("ldr r0, =_estack");
|
||||||
|
__asm volatile("mov sp, r0");
|
||||||
|
|
||||||
|
/* copy .data section from flash to RAM */
|
||||||
|
// Not needed for this example, see linker script
|
||||||
|
// for( uint32_t * src = &_sidata, * dest = &_sdata; dest < &_edata; )
|
||||||
|
// {
|
||||||
|
// *dest++ = *src++;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* zero out .bss section */
|
||||||
|
for (uint32_t *dest = &_sbss; dest < &_ebss;) {
|
||||||
|
*dest++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* jump to board initialisation */
|
||||||
|
void _start(void);
|
||||||
|
_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t *isr_vector[] __attribute__((section(".isr_vector"))) = {
|
||||||
|
(uint32_t *)&_estack,
|
||||||
|
(uint32_t *)&Reset_Handler, /* Reset -15 */
|
||||||
|
0, /* NMI_Handler -14 */
|
||||||
|
0, /* HardFault_Handler -13 */
|
||||||
|
0, /* MemManage_Handler -12 */
|
||||||
|
0, /* BusFault_Handler -11 */
|
||||||
|
0, /* UsageFault_Handler -10 */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved -6 */
|
||||||
|
0, /* SVC_Handler -5 */
|
||||||
|
0, /* DebugMon_Handler -4 */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* PendSV handler -2 */
|
||||||
|
0, /* SysTick_Handler -1 */
|
||||||
|
0, /* uart0 receive 0 */
|
||||||
|
0, /* uart0 transmit */
|
||||||
|
0, /* uart1 receive */
|
||||||
|
0, /* uart1 transmit */
|
||||||
|
0, /* uart 2 receive */
|
||||||
|
0, /* uart 2 transmit */
|
||||||
|
0, /* GPIO 0 combined interrupt */
|
||||||
|
0, /* GPIO 2 combined interrupt */
|
||||||
|
0, /* Timer 0 */
|
||||||
|
0, /* Timer 1 */
|
||||||
|
0, /* Dial Timer */
|
||||||
|
0, /* SPI0 SPI1 */
|
||||||
|
0, /* uart overflow 1, 2,3 */
|
||||||
|
0, /* Ethernet 13 */
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((naked)) void exit(__attribute__((unused)) int status) {
|
||||||
|
/* Force qemu to exit using ARM Semihosting */
|
||||||
|
__asm volatile(
|
||||||
|
"mov r1, r0\n"
|
||||||
|
"cmp r1, #0\n"
|
||||||
|
"bne .notclean\n"
|
||||||
|
"ldr r1, =0x20026\n" /* ADP_Stopped_ApplicationExit, a clean exit */
|
||||||
|
".notclean:\n"
|
||||||
|
"movs r0, #0x18\n" /* SYS_EXIT */
|
||||||
|
"bkpt 0xab\n"
|
||||||
|
"end: b end\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _start(void) {
|
||||||
|
main();
|
||||||
|
exit(0);
|
||||||
|
}
|
213
fuzzers/qemu_systemmode_sync_backdoor/src/fuzzer.rs
Normal file
213
fuzzers/qemu_systemmode_sync_backdoor/src/fuzzer.rs
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels
|
||||||
|
//!
|
||||||
|
use core::{ptr::addr_of_mut, time::Duration};
|
||||||
|
use std::{env, path::PathBuf, process};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
|
events::{launcher::Launcher, EventConfig},
|
||||||
|
executors::ExitKind,
|
||||||
|
feedback_or, feedback_or_fast,
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::BytesInput,
|
||||||
|
monitors::MultiMonitor,
|
||||||
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
|
observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver},
|
||||||
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
|
stages::{CalibrationStage, StdMutationalStage},
|
||||||
|
state::{HasCorpus, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::{
|
||||||
|
core_affinity::Cores,
|
||||||
|
current_nanos,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
edges::{edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM},
|
||||||
|
emu::Emulator,
|
||||||
|
executor::{stateful::StatefulQemuExecutor, QemuExecutorState},
|
||||||
|
EmuExitReasonError, FastSnapshotManager, HandlerError, HandlerResult, QemuHooks,
|
||||||
|
StdEmuExitHandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
// use libafl_qemu::QemuSnapshotBuilder; for normal qemu snapshot
|
||||||
|
|
||||||
|
pub fn fuzz() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||||
|
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||||
|
};
|
||||||
|
// Hardcoded parameters
|
||||||
|
let timeout = Duration::from_secs(3);
|
||||||
|
let broker_port = 1337;
|
||||||
|
let cores = Cores::from_cmdline("1").unwrap();
|
||||||
|
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||||
|
let objective_dir = PathBuf::from("./crashes");
|
||||||
|
|
||||||
|
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||||
|
// Initialize QEMU
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
// let emu_snapshot_manager = QemuSnapshotBuilder::new(true);
|
||||||
|
let emu_snapshot_manager = FastSnapshotManager::new(false); // Create a snapshot manager (normal or fast for now).
|
||||||
|
let emu_exit_handler: StdEmuExitHandler<FastSnapshotManager> =
|
||||||
|
StdEmuExitHandler::new(emu_snapshot_manager); // Create an exit handler: it is the entity taking the decision of what should be done when QEMU returns.
|
||||||
|
let emu = Emulator::new(&args, &env, emu_exit_handler).unwrap(); // Create the emulator
|
||||||
|
|
||||||
|
let devices = emu.list_devices();
|
||||||
|
println!("Devices = {:?}", devices);
|
||||||
|
|
||||||
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
|
let mut harness =
|
||||||
|
|input: &BytesInput, qemu_executor_state: &mut QemuExecutorState<_, _>| unsafe {
|
||||||
|
match emu.run(input, qemu_executor_state) {
|
||||||
|
Ok(handler_result) => match handler_result {
|
||||||
|
HandlerResult::UnhandledExit(unhandled_exit) => {
|
||||||
|
panic!("Unhandled exit: {}", unhandled_exit)
|
||||||
|
}
|
||||||
|
HandlerResult::EndOfRun(exit_kind) => exit_kind,
|
||||||
|
HandlerResult::Interrupted => {
|
||||||
|
println!("Interrupted.");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(handler_error) => match handler_error {
|
||||||
|
HandlerError::QemuExitReasonError(emu_exit_reason_error) => {
|
||||||
|
match emu_exit_reason_error {
|
||||||
|
EmuExitReasonError::UnknownKind => panic!("unknown kind"),
|
||||||
|
EmuExitReasonError::UnexpectedExit => ExitKind::Crash,
|
||||||
|
_ => {
|
||||||
|
panic!("Emu Exit unhandled error: {:?}", emu_exit_reason_error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Unhandled error: {:?}", handler_error),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an observation channel using the coverage map
|
||||||
|
let edges_observer = unsafe {
|
||||||
|
HitcountsMapObserver::new(VariableMapObserver::from_mut_slice(
|
||||||
|
"edges",
|
||||||
|
edges_map_mut_slice(),
|
||||||
|
addr_of_mut!(MAX_EDGES_NUM),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::tracking(&edges_observer, true, true),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// A feedback to choose if an input is a solution or not
|
||||||
|
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(objective_dir.clone()).unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// The feedbacks can report the data that should persist in the State.
|
||||||
|
&mut feedback,
|
||||||
|
// Same for objective feedbacks
|
||||||
|
&mut objective,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
let mut hooks = QemuHooks::new(
|
||||||
|
emu.qemu().clone(),
|
||||||
|
tuple_list!(QemuEdgeCoverageHelper::default()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup an havoc mutator with a mutational stage
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
|
let calibration_feedback = MaxMapFeedback::tracking(&edges_observer, true, true);
|
||||||
|
let mut stages = tuple_list!(
|
||||||
|
StdMutationalStage::new(mutator),
|
||||||
|
CalibrationStage::new(&calibration_feedback)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a QEMU in-process executor
|
||||||
|
let mut executor = StatefulQemuExecutor::new(
|
||||||
|
&mut hooks,
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(edges_observer, time_observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
.expect("Failed to create QemuExecutor");
|
||||||
|
|
||||||
|
// Instead of calling the timeout handler and restart the process, trigger a breakpoint ASAP
|
||||||
|
executor.break_on_timeout();
|
||||||
|
|
||||||
|
if state.must_load_initial_inputs() {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
.unwrap();
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// The shared memory allocator
|
||||||
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
// The stats reporter for the broker
|
||||||
|
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||||
|
|
||||||
|
// let monitor = SimpleMonitor::new(|s| println!("{s}"));
|
||||||
|
// let mut mgr = SimpleEventManager::new(monitor);
|
||||||
|
// run_client(None, mgr, 0);
|
||||||
|
|
||||||
|
// Build and run a Launcher
|
||||||
|
match Launcher::builder()
|
||||||
|
.shmem_provider(shmem_provider)
|
||||||
|
.broker_port(broker_port)
|
||||||
|
.configuration(EventConfig::from_build_id())
|
||||||
|
.monitor(monitor)
|
||||||
|
.run_client(&mut run_client)
|
||||||
|
.cores(&cores)
|
||||||
|
// .stdout_file(Some("/dev/null"))
|
||||||
|
.build()
|
||||||
|
.launch()
|
||||||
|
{
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||||
|
Err(err) => panic!("Failed to run launcher: {err:?}"),
|
||||||
|
}
|
||||||
|
}
|
13
fuzzers/qemu_systemmode_sync_backdoor/src/main.rs
Normal file
13
fuzzers/qemu_systemmode_sync_backdoor/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod fuzzer;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn main() {
|
||||||
|
fuzzer::fuzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||||
|
}
|
@ -216,7 +216,7 @@ where
|
|||||||
"objectives": self.base.objective_size(),
|
"objectives": self.base.objective_size(),
|
||||||
"executions": self.base.total_execs(),
|
"executions": self.base.total_execs(),
|
||||||
"exec_sec": self.base.execs_per_sec(),
|
"exec_sec": self.base.execs_per_sec(),
|
||||||
"clients": &self.client_stats()[1..]
|
"clients": &self.client_stats().get(1..)
|
||||||
});
|
});
|
||||||
writeln!(&file, "{line}").expect("Unable to write JSON to file");
|
writeln!(&file, "{line}").expect("Unable to write JSON to file");
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,14 @@
|
|||||||
//! use std::env;
|
//! use std::env;
|
||||||
//!
|
//!
|
||||||
//! // make sure to add `features = ["qemu_cli"]` to the `libafl` crate in `Cargo.toml`
|
//! // make sure to add `features = ["qemu_cli"]` to the `libafl` crate in `Cargo.toml`
|
||||||
//! use libafl_qemu::Emulator;
|
//! use libafl_qemu::Qemu;
|
||||||
//!
|
//!
|
||||||
//! fn fuzz_with_qemu(mut options: FuzzerOptions) {
|
//! fn fuzz_with_qemu(mut options: FuzzerOptions) {
|
||||||
//! env::remove_var("LD_LIBRARY_PATH");
|
//! env::remove_var("LD_LIBRARY_PATH");
|
||||||
//!
|
//!
|
||||||
//! let env: Vec<(String, String)> = env::vars().collect();
|
//! let env: Vec<(String, String)> = env::vars().collect();
|
||||||
//!
|
//!
|
||||||
//! let emu = Emulator::new(&mut options.qemu_args.to_vec(), &mut env).unwrap();
|
//! let qemu = Qemu::init(&mut options.qemu_args.to_vec(), &mut env).unwrap();
|
||||||
//! // do other stuff...
|
//! // do other stuff...
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
@ -25,7 +25,7 @@ document-features = ["dep:document-features"]
|
|||||||
## Find injections during fuzzing
|
## Find injections during fuzzing
|
||||||
injections = ["serde_yaml", "toml"]
|
injections = ["serde_yaml", "toml"]
|
||||||
## Python bindings support
|
## Python bindings support
|
||||||
python = ["pyo3", "pyo3-build-config"]
|
python = ["pyo3", "pyo3-build-config", "libafl_qemu_sys/python"]
|
||||||
## Fork support
|
## Fork support
|
||||||
fork = ["libafl/fork"]
|
fork = ["libafl/fork"]
|
||||||
## Build libqasan for address sanitization
|
## Build libqasan for address sanitization
|
||||||
@ -94,6 +94,7 @@ document-features = { version = "0.2", optional = true }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { version = "0.18", optional = true }
|
pyo3-build-config = { version = "0.18", optional = true }
|
||||||
rustversion = "1.0"
|
rustversion = "1.0"
|
||||||
|
bindgen = "0.69"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "libafl_qemu"
|
name = "libafl_qemu"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::{env, fs, path::Path, process::Command};
|
use std::{env, fs, path::{Path, PathBuf}, process::Command};
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn build() {
|
pub fn build() {
|
||||||
// Note: Unique features are checked in libafl_qemu_sys
|
// Note: Unique features are checked in libafl_qemu_sys
|
||||||
|
|
||||||
@ -15,11 +16,15 @@ pub fn build() {
|
|||||||
|
|
||||||
let build_libqasan = cfg!(all(feature = "build_libqasan", not(feature = "hexagon")));
|
let build_libqasan = cfg!(all(feature = "build_libqasan", not(feature = "hexagon")));
|
||||||
|
|
||||||
|
let exit_hdr_dir = PathBuf::from("runtime");
|
||||||
|
let exit_hdr = exit_hdr_dir.join("libafl_exit.h");
|
||||||
|
|
||||||
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
println!("cargo:rustc-cfg=emulation_mode=\"{emulation_mode}\"");
|
||||||
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
println!("cargo:rerun-if-env-changed=EMULATION_MODE");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=build_linux.rs");
|
println!("cargo:rerun-if-changed=build_linux.rs");
|
||||||
|
println!("cargo:rerun-if-changed={}", exit_hdr_dir.display());
|
||||||
|
|
||||||
let cpu_target = if cfg!(feature = "x86_64") {
|
let cpu_target = if cfg!(feature = "x86_64") {
|
||||||
"x86_64".to_string()
|
"x86_64".to_string()
|
||||||
@ -62,11 +67,28 @@ pub fn build() {
|
|||||||
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let out_dir_path = Path::new(&out_dir);
|
let out_dir_path = Path::new(&out_dir);
|
||||||
let mut target_dir = out_dir_path.to_path_buf();
|
let out_dir_path_buf = out_dir_path.to_path_buf();
|
||||||
|
let mut target_dir = out_dir_path_buf.clone();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
target_dir.pop();
|
target_dir.pop();
|
||||||
|
|
||||||
|
let binding_file = out_dir_path_buf.join("backdoor_bindings.rs");
|
||||||
|
bindgen::Builder::default()
|
||||||
|
.derive_debug(true)
|
||||||
|
.derive_default(true)
|
||||||
|
.impl_debug(true)
|
||||||
|
.generate_comments(true)
|
||||||
|
.default_enum_style(bindgen::EnumVariation::NewType {
|
||||||
|
is_global: true,
|
||||||
|
is_bitfield: true,
|
||||||
|
})
|
||||||
|
.header(exit_hdr.display().to_string())
|
||||||
|
.generate()
|
||||||
|
.expect("Exit bindings generation failed.")
|
||||||
|
.write_to_file(binding_file)
|
||||||
|
.expect("Could not write bindings.");
|
||||||
|
|
||||||
if (emulation_mode == "usermode") && build_libqasan {
|
if (emulation_mode == "usermode") && build_libqasan {
|
||||||
let qasan_dir = Path::new("libqasan");
|
let qasan_dir = Path::new("libqasan");
|
||||||
let qasan_dir = fs::canonicalize(qasan_dir).unwrap();
|
let qasan_dir = fs::canonicalize(qasan_dir).unwrap();
|
||||||
|
@ -136,6 +136,7 @@ pub fn generate(
|
|||||||
.allowlist_type("MemOpIdx")
|
.allowlist_type("MemOpIdx")
|
||||||
.allowlist_type("MemOp")
|
.allowlist_type("MemOp")
|
||||||
.allowlist_type("DeviceSnapshotKind")
|
.allowlist_type("DeviceSnapshotKind")
|
||||||
|
.allowlist_type("ShutdownCause")
|
||||||
.allowlist_type("libafl_exit_reason")
|
.allowlist_type("libafl_exit_reason")
|
||||||
.allowlist_type("libafl_exit_reason_kind")
|
.allowlist_type("libafl_exit_reason_kind")
|
||||||
.allowlist_type("libafl_exit_reason_sync_backdoor")
|
.allowlist_type("libafl_exit_reason_sync_backdoor")
|
||||||
|
@ -30,12 +30,21 @@ be = []
|
|||||||
usermode = []
|
usermode = []
|
||||||
systemmode = []
|
systemmode = []
|
||||||
|
|
||||||
|
python = ["pyo3", "pyo3-build-config"]
|
||||||
|
|
||||||
slirp = [ "systemmode", "libafl_qemu_build/slirp" ] # build qemu with host libslirp (for user networking)
|
slirp = [ "systemmode", "libafl_qemu_build/slirp" ] # build qemu with host libslirp (for user networking)
|
||||||
shared = [ "libafl_qemu_build/shared" ]
|
shared = [ "libafl_qemu_build/shared" ]
|
||||||
|
|
||||||
clippy = [ "libafl_qemu_build/clippy" ] # special feature for clippy, don't use in normal projects
|
clippy = [ "libafl_qemu_build/clippy" ] # special feature for clippy, don't use in normal projects
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
paste = "1"
|
||||||
|
num_enum = "0.7"
|
||||||
|
libc = "0.2"
|
||||||
|
strum = "0.25"
|
||||||
|
strum_macros = "0.25"
|
||||||
|
pyo3 = { version = "0.18", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
libafl_qemu_build = { path = "../libafl_qemu_build", version = "0.11.2" }
|
libafl_qemu_build = { path = "../libafl_qemu_build", version = "0.11.2" }
|
||||||
|
pyo3-build-config = { version = "0.18", optional = true }
|
||||||
|
@ -19,12 +19,131 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|||||||
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
||||||
mod x86_64_stub_bindings;
|
mod x86_64_stub_bindings;
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
mod usermode;
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
pub use usermode::*;
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
mod systemmode;
|
||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
use paste::paste;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub use systemmode::*;
|
||||||
|
|
||||||
|
/// Safe linking with of extern "C" functions.
|
||||||
|
/// This macro makes sure the declared symbol is defined *at link time*, avoiding declaring non-existant symbols
|
||||||
|
/// that could be silently ignored during linking if unused.
|
||||||
|
///
|
||||||
|
/// This macro relies on a nightly feature, and can only be used in this mode
|
||||||
|
/// It is (nearly) a drop-in replacement for extern "C" { } blocks containing function and static declarations, and will have the same effect in practice.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! extern_c_checked {
|
||||||
|
() => {};
|
||||||
|
|
||||||
|
($visibility:vis fn $c_fn:ident($($param_ident:ident : $param_ty:ty),*) $( -> $ret_ty:ty )?; $($tail:tt)*) => {
|
||||||
|
paste! {
|
||||||
|
#[cfg_attr(nightly, used(linker))]
|
||||||
|
static [<__ $c_fn:upper __>]: unsafe extern "C" fn($($param_ty),*) $( -> $ret_ty )? = $c_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
$visibility fn $c_fn($($param_ident : $param_ty),*) $( -> $ret_ty )?;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern_c_checked!($($tail)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
($visibility:vis static $c_var:ident : $c_var_ty:ty; $($tail:tt)*) => {
|
||||||
|
paste! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(unused)]
|
||||||
|
struct [<__ $c_var:upper _STRUCT__>] { member: *const $c_var_ty }
|
||||||
|
|
||||||
|
unsafe impl Sync for [<__ $c_var:upper _STRUCT__>] {}
|
||||||
|
|
||||||
|
#[cfg_attr(nightly, used(linker))]
|
||||||
|
static [<__ $c_var:upper __>]: [<__ $c_var:upper _STRUCT__>] = unsafe { [<__ $c_var:upper _STRUCT__>] { member: core::ptr::addr_of!($c_var) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
$visibility static $c_var: $c_var_ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern_c_checked!($($tail)*);
|
||||||
|
};
|
||||||
|
|
||||||
|
($visibility:vis static mut $c_var:ident : $c_var_ty:ty; $($tail:tt)*) => {
|
||||||
|
paste! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(unused)]
|
||||||
|
struct [<__ $c_var:upper _STRUCT__>] { member: *const $c_var_ty }
|
||||||
|
|
||||||
|
unsafe impl Sync for [<__ $c_var:upper _STRUCT__>] {}
|
||||||
|
|
||||||
|
#[cfg_attr(nightly, used(linker))]
|
||||||
|
static mut [<__ $c_var:upper __>]: [<__ $c_var:upper _STRUCT__>] = unsafe { [<__ $c_var:upper _STRUCT__>] { member: core::ptr::addr_of!($c_var) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
$visibility static mut $c_var: $c_var_ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern_c_checked!($($tail)*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use core::ops::BitAnd;
|
use core::ops::BitAnd;
|
||||||
|
use std::{ffi::c_void, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
use pyo3::{pyclass, pymethods, IntoPy, PyObject, Python};
|
||||||
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
#[cfg(all(feature = "clippy", target_os = "linux"))]
|
||||||
pub use x86_64_stub_bindings::*;
|
pub use x86_64_stub_bindings::*;
|
||||||
|
|
||||||
|
pub type CPUStatePtr = *mut crate::CPUState;
|
||||||
|
pub type CPUArchStatePtr = *mut crate::CPUArchState;
|
||||||
|
pub type ExitReasonPtr = *mut crate::libafl_exit_reason;
|
||||||
|
|
||||||
|
pub type GuestUsize = crate::target_ulong;
|
||||||
|
pub type GuestIsize = crate::target_long;
|
||||||
|
|
||||||
|
pub type GuestAddr = crate::target_ulong;
|
||||||
|
pub type GuestPhysAddr = crate::hwaddr;
|
||||||
|
pub type GuestVirtAddr = crate::vaddr;
|
||||||
|
|
||||||
|
pub type GuestHwAddrInfo = crate::qemu_plugin_hwaddr;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||||
|
pub struct MapInfo {
|
||||||
|
start: GuestAddr,
|
||||||
|
end: GuestAddr,
|
||||||
|
offset: GuestAddr,
|
||||||
|
path: *const u8,
|
||||||
|
flags: i32,
|
||||||
|
is_priv: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct FatPtr(pub *const c_void, pub *const c_void);
|
||||||
|
|
||||||
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum MmapPerms {
|
||||||
|
None = 0,
|
||||||
|
Read = libc::PROT_READ,
|
||||||
|
Write = libc::PROT_WRITE,
|
||||||
|
Execute = libc::PROT_EXEC,
|
||||||
|
ReadWrite = libc::PROT_READ | libc::PROT_WRITE,
|
||||||
|
ReadExecute = libc::PROT_READ | libc::PROT_EXEC,
|
||||||
|
WriteExecute = libc::PROT_WRITE | libc::PROT_EXEC,
|
||||||
|
ReadWriteExecute = libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
|
||||||
|
}
|
||||||
|
|
||||||
// from include/exec/memop.h
|
// from include/exec/memop.h
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -50,3 +169,122 @@ pub fn make_plugin_meminfo(oi: MemOpIdx, rw: qemu_plugin_mem_rw) -> qemu_plugin_
|
|||||||
pub fn cpu_env(cpu: *mut CPUState) -> *mut CPUArchState {
|
pub fn cpu_env(cpu: *mut CPUState) -> *mut CPUArchState {
|
||||||
unsafe { cpu.add(1) as *mut CPUArchState }
|
unsafe { cpu.add(1) as *mut CPUArchState }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern_c_checked! {
|
||||||
|
//static libafl_page_size: GuestUsize;
|
||||||
|
pub fn libafl_page_from_addr(addr: GuestAddr) -> GuestAddr;
|
||||||
|
|
||||||
|
// CPUState* libafl_qemu_get_cpu(int cpu_index);
|
||||||
|
pub fn libafl_qemu_get_cpu(cpu_index: i32) -> CPUStatePtr;
|
||||||
|
// int libafl_qemu_num_cpus(void);
|
||||||
|
pub fn libafl_qemu_num_cpus() -> i32;
|
||||||
|
// CPUState* libafl_qemu_current_cpu(void);
|
||||||
|
pub fn libafl_qemu_current_cpu() -> CPUStatePtr;
|
||||||
|
|
||||||
|
// struct libafl_exit_reason* libafl_get_exit_reason(void);
|
||||||
|
// fn libafl_get_exit_reason() -> ExitReasonPtr;
|
||||||
|
|
||||||
|
pub fn libafl_qemu_cpu_index(cpu: CPUStatePtr) -> i32;
|
||||||
|
|
||||||
|
pub fn libafl_qemu_write_reg(cpu: CPUStatePtr, reg: i32, val: *const u8) -> i32;
|
||||||
|
pub fn libafl_qemu_read_reg(cpu: CPUStatePtr, reg: i32, val: *mut u8) -> i32;
|
||||||
|
pub fn libafl_qemu_num_regs(cpu: CPUStatePtr) -> i32;
|
||||||
|
|
||||||
|
// fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
|
||||||
|
// fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
|
||||||
|
pub fn libafl_flush_jit();
|
||||||
|
// fn libafl_qemu_trigger_breakpoint(cpu: CPUStatePtr);
|
||||||
|
|
||||||
|
pub fn strlen(s: *const u8) -> usize;
|
||||||
|
|
||||||
|
pub fn libafl_qemu_add_gdb_cmd(
|
||||||
|
callback: extern "C" fn(*const (), *const u8, usize) -> i32,
|
||||||
|
data: *const ()
|
||||||
|
);
|
||||||
|
pub fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "python", pymethods)]
|
||||||
|
impl MapInfo {
|
||||||
|
#[must_use]
|
||||||
|
pub fn start(&self) -> GuestAddr {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn end(&self) -> GuestAddr {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn offset(&self) -> GuestAddr {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn path(&self) -> Option<&str> {
|
||||||
|
if self.path.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
Some(from_utf8_unchecked(from_raw_parts(
|
||||||
|
self.path,
|
||||||
|
strlen(self.path),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn flags(&self) -> MmapPerms {
|
||||||
|
MmapPerms::try_from(self.flags).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_priv(&self) -> bool {
|
||||||
|
self.is_priv != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MmapPerms {
|
||||||
|
#[must_use]
|
||||||
|
pub fn readable(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
MmapPerms::Read
|
||||||
|
| MmapPerms::ReadWrite
|
||||||
|
| MmapPerms::ReadExecute
|
||||||
|
| MmapPerms::ReadWriteExecute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn writable(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
MmapPerms::Write
|
||||||
|
| MmapPerms::ReadWrite
|
||||||
|
| MmapPerms::WriteExecute
|
||||||
|
| MmapPerms::ReadWriteExecute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn executable(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
MmapPerms::Execute
|
||||||
|
| MmapPerms::ReadExecute
|
||||||
|
| MmapPerms::WriteExecute
|
||||||
|
| MmapPerms::ReadWriteExecute
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
impl IntoPy<PyObject> for MmapPerms {
|
||||||
|
fn into_py(self, py: Python) -> PyObject {
|
||||||
|
let n: i32 = self.into();
|
||||||
|
n.into_py(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
16
libafl_qemu/libafl_qemu_sys/src/systemmode.rs
Normal file
16
libafl_qemu/libafl_qemu_sys/src/systemmode.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
use crate::{extern_c_checked, CPUStatePtr, GuestPhysAddr};
|
||||||
|
|
||||||
|
extern_c_checked! {
|
||||||
|
pub fn qemu_init(argc: i32, argv: *const *const u8, envp: *const *const u8);
|
||||||
|
|
||||||
|
pub fn vm_start();
|
||||||
|
pub fn qemu_main_loop();
|
||||||
|
pub fn qemu_cleanup();
|
||||||
|
|
||||||
|
pub fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
|
pub fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
|
|
||||||
|
pub fn libafl_qemu_current_paging_id(cpu: CPUStatePtr) -> GuestPhysAddr;
|
||||||
|
}
|
36
libafl_qemu/libafl_qemu_sys/src/usermode.rs
Normal file
36
libafl_qemu/libafl_qemu_sys/src/usermode.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use core::ffi::c_void;
|
||||||
|
|
||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
use paste::paste;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
|
use crate::{extern_c_checked, GuestAddr, MapInfo};
|
||||||
|
|
||||||
|
extern_c_checked! {
|
||||||
|
pub fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
|
||||||
|
|
||||||
|
pub fn libafl_qemu_run() -> i32;
|
||||||
|
|
||||||
|
pub fn libafl_load_addr() -> u64;
|
||||||
|
pub fn libafl_get_brk() -> u64;
|
||||||
|
pub fn libafl_set_brk(brk: u64) -> u64;
|
||||||
|
|
||||||
|
pub fn read_self_maps() -> *const c_void;
|
||||||
|
pub fn free_self_maps(map_info: *const c_void);
|
||||||
|
|
||||||
|
pub fn libafl_maps_next(map_info: *const c_void, ret: *mut MapInfo) -> *const c_void;
|
||||||
|
|
||||||
|
pub static exec_path: *const u8;
|
||||||
|
pub static guest_base: usize;
|
||||||
|
pub static mut mmap_next_start: GuestAddr;
|
||||||
|
|
||||||
|
pub static mut libafl_dump_core_hook: unsafe extern "C" fn(i32);
|
||||||
|
pub static mut libafl_force_dfl: i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter, PartialEq, Eq)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum VerifyAccess {
|
||||||
|
Read = libc::PROT_READ,
|
||||||
|
Write = libc::PROT_READ | libc::PROT_WRITE,
|
||||||
|
}
|
176
libafl_qemu/runtime/libafl_exit.h
Normal file
176
libafl_qemu/runtime/libafl_exit.h
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#ifndef LIBAFL_EXIT_H
|
||||||
|
#define LIBAFL_EXIT_H
|
||||||
|
|
||||||
|
#define STRINGIFY(s) #s
|
||||||
|
#define XSTRINGIFY(s) STRINGIFY(s)
|
||||||
|
|
||||||
|
// Target Specific imports / definitions
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <intsafe.h>
|
||||||
|
|
||||||
|
typedef UINT64 libafl_word;
|
||||||
|
#define LIBAFL_CALLING_CONVENTION __fastcall
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint64_t libafl_word;
|
||||||
|
#define LIBAFL_CALLING_CONVENTION __attribute__(())
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef uint32_t libafl_word;
|
||||||
|
#define LIBAFL_CALLING_CONVENTION __attribute__(())
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LIBAFL_EXIT_OPCODE 0x66f23a0f
|
||||||
|
#define LIBAFL_EXIT_VERSION_NUMBER 0111 // TODO: find a nice way to set it.
|
||||||
|
|
||||||
|
typedef enum LibaflExit {
|
||||||
|
LIBAFL_EXIT_START_VIRT = 0,
|
||||||
|
LIBAFL_EXIT_START_PHYS = 1,
|
||||||
|
LIBAFL_EXIT_INPUT_VIRT = 2,
|
||||||
|
LIBAFL_EXIT_INPUT_PHYS = 3,
|
||||||
|
LIBAFL_EXIT_END = 4,
|
||||||
|
LIBAFL_EXIT_SAVE = 5,
|
||||||
|
LIBAFL_EXIT_LOAD = 6,
|
||||||
|
LIBAFL_EXIT_VERSION = 7,
|
||||||
|
LIBAFL_EXIT_VADDR_FILTER_ALLOW = 8,
|
||||||
|
} LibaflExit;
|
||||||
|
|
||||||
|
typedef enum LibaflExitEndStatus {
|
||||||
|
LIBAFL_EXIT_END_UNKNOWN = 0,
|
||||||
|
LIBAFL_EXIT_END_OK = 1,
|
||||||
|
LIBAFL_EXIT_END_CRASH = 2,
|
||||||
|
} LibaflExitEndParams;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call0(libafl_word action);
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call1(libafl_word action,
|
||||||
|
libafl_word arg1);
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call2(libafl_word action,
|
||||||
|
libafl_word arg1,
|
||||||
|
libafl_word arg2);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call0(libafl_word action) {
|
||||||
|
libafl_word ret;
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov %1, %%rax\n"
|
||||||
|
".dword " XSTRINGIFY(LIBAFL_EXIT_OPCODE) "\n"
|
||||||
|
"mov %%rax, %0\n"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(action)
|
||||||
|
: "%rax"
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call1(libafl_word action,
|
||||||
|
libafl_word arg1) {
|
||||||
|
libafl_word ret;
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov %1, %%rax\n"
|
||||||
|
"mov %2, %%rdi\n"
|
||||||
|
".dword " XSTRINGIFY(LIBAFL_EXIT_OPCODE) "\n"
|
||||||
|
"mov %%rax, %0\n"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(action), "g"(arg1)
|
||||||
|
: "%rax", "%rdi"
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call2(libafl_word action,
|
||||||
|
libafl_word arg1,
|
||||||
|
libafl_word arg2) {
|
||||||
|
libafl_word ret;
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov %1, %%rax\n"
|
||||||
|
"mov %2, %%rdi\n"
|
||||||
|
"mov %3, %%rsi\n"
|
||||||
|
".dword " XSTRINGIFY(LIBAFL_EXIT_OPCODE) "\n"
|
||||||
|
"mov %%rax, %0\n"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(action), "g"(arg1), "g"(arg2)
|
||||||
|
: "%rax", "%rdi", "%rsi"
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call0(libafl_word action) {
|
||||||
|
libafl_word ret;
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov r0, %1\n"
|
||||||
|
".word " XSTRINGIFY(LIBAFL_EXIT_OPCODE) "\n"
|
||||||
|
"mov %0, r0\n"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(action)
|
||||||
|
: "r0"
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call1(libafl_word action,
|
||||||
|
libafl_word arg1) {
|
||||||
|
libafl_word ret;
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov r0, %1\n"
|
||||||
|
"mov r1, %2\n"
|
||||||
|
".word " XSTRINGIFY(LIBAFL_EXIT_OPCODE) "\n"
|
||||||
|
"mov %0, r0\n"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(action), "r"(arg1)
|
||||||
|
: "r0", "r1"
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl_word LIBAFL_CALLING_CONVENTION _libafl_exit_call2(libafl_word action,
|
||||||
|
libafl_word arg1,
|
||||||
|
libafl_word arg2) {
|
||||||
|
libafl_word ret;
|
||||||
|
__asm__ volatile (
|
||||||
|
"mov r0, %1\n"
|
||||||
|
"mov r1, %2\n"
|
||||||
|
"mov r2, %3\n"
|
||||||
|
".word " XSTRINGIFY(LIBAFL_EXIT_OPCODE) "\n"
|
||||||
|
"mov %0, r0\n"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(action), "r"(arg1), "r"(arg2)
|
||||||
|
: "r0", "r1", "r2"
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LIBAFL_EXIT_START_VIRT(buf_vaddr, max_len) \
|
||||||
|
_libafl_exit_call2(LIBAFL_EXIT_START_VIRT, buf_vaddr, max_len)
|
||||||
|
#define LIBAFL_EXIT_START_PHYS(buf_paddr, max_len) \
|
||||||
|
_libafl_exit_call2(LIBAFL_EXIT_START_PHYS, buf_paddr, max_len)
|
||||||
|
#define LIBAFL_EXIT_INPUT_VIRT(buf_vaddr, max_len) \
|
||||||
|
_libafl_exit_call2(LIBAFL_EXIT_INPUT_VIRT, buf_vaddr, max_len)
|
||||||
|
#define LIBAFL_EXIT_INPUT_PHYS(buf_paddr, max_len) \
|
||||||
|
_libafl_exit_call2(LIBAFL_EXIT_INPUT_PHYS, buf_paddr, max_len)
|
||||||
|
#define LIBAFL_EXIT_END(status) _libafl_exit_call1(LIBAFL_EXIT_END, status)
|
||||||
|
#define LIBAFL_EXIT_SAVE() _libafl_exit_call0(LIBAFL_EXIT_SAVE)
|
||||||
|
#define LIBAFL_EXIT_LOAD() _libafl_exit_call0(LIBAFL_EXIT_LOAD)
|
||||||
|
#define LIBAFL_EXIT_VERSION() _libafl_exit_call0(LIBAFL_EXIT_VERSION_NUMBER)
|
||||||
|
|
||||||
|
#endif
|
63
libafl_qemu/runtime/libafl_exit_windows.asm
Executable file
63
libafl_qemu/runtime/libafl_exit_windows.asm
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
PUBLIC _libafl_exit_call0, _libafl_exit_call1, _libafl_exit_call2
|
||||||
|
|
||||||
|
LIBAFL_EXIT_OPCODE MACRO
|
||||||
|
dd 66f23a0fh
|
||||||
|
ENDM
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
; Execute LibAFL backdoor (no argument)
|
||||||
|
; Parameters:
|
||||||
|
; [RAX, OUT] Hook return value
|
||||||
|
; [RCX, IN] LibAFL Backdorr operation
|
||||||
|
_libafl_exit_call0:
|
||||||
|
mov rax, rcx
|
||||||
|
|
||||||
|
IFNDEF _DEBUG
|
||||||
|
LIBAFL_EXIT_OPCODE
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Execute LibAFL backdoor (one argument)
|
||||||
|
; Parameters:
|
||||||
|
; [RAX, OUT] Hook return value
|
||||||
|
; [RCX, IN] LibAFL Backdorr operation
|
||||||
|
; [RDX, IN] Arg1
|
||||||
|
_libafl_exit_call1:
|
||||||
|
push rdi
|
||||||
|
|
||||||
|
mov rax, rcx
|
||||||
|
mov rdi, rdx
|
||||||
|
|
||||||
|
IFNDEF _DEBUG
|
||||||
|
LIBAFL_EXIT_OPCODE
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
pop rdi
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Execute LibAFL backdoor (two arguments)
|
||||||
|
; Parameters:
|
||||||
|
; [RAX, OUT] Hook return value
|
||||||
|
; [RCX, IN] LibAFL Backdorr operation
|
||||||
|
; [RDX, IN] Arg1
|
||||||
|
; [R8, IN] Arg2
|
||||||
|
_libafl_exit_call2:
|
||||||
|
push rdi
|
||||||
|
push rsi
|
||||||
|
|
||||||
|
mov rax, rcx
|
||||||
|
mov rdi, rdx
|
||||||
|
mov rsi, r8
|
||||||
|
|
||||||
|
IFNDEF _DEBUG
|
||||||
|
LIBAFL_EXIT_OPCODE
|
||||||
|
ENDIF
|
||||||
|
pop rsi
|
||||||
|
pop rdi
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
END
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::aarch64::*;
|
pub use syscall_numbers::aarch64::*;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -49,19 +49,19 @@ pub enum Regs {
|
|||||||
Pstate = 33,
|
Pstate = 33,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::X0,
|
BackdoorArgs::Ret => Regs::X0,
|
||||||
SyncBackdoorArgs::Cmd => Regs::X0,
|
BackdoorArgs::Cmd => Regs::X0,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::X1,
|
BackdoorArgs::Arg1 => Regs::X1,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::X2,
|
BackdoorArgs::Arg2 => Regs::X2,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::X3,
|
BackdoorArgs::Arg3 => Regs::X3,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::X4,
|
BackdoorArgs::Arg4 => Regs::X4,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::X5,
|
BackdoorArgs::Arg5 => Regs::X5,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::X6,
|
BackdoorArgs::Arg6 => Regs::X6,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::arm::*;
|
pub use syscall_numbers::arm::*;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the ARM instruction set.
|
/// Registers for the ARM instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -33,19 +33,19 @@ pub enum Regs {
|
|||||||
R25 = 25,
|
R25 = 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::R0,
|
BackdoorArgs::Ret => Regs::R0,
|
||||||
SyncBackdoorArgs::Cmd => Regs::R0,
|
BackdoorArgs::Cmd => Regs::R0,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::R1,
|
BackdoorArgs::Arg1 => Regs::R1,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::R2,
|
BackdoorArgs::Arg2 => Regs::R2,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::R3,
|
BackdoorArgs::Arg3 => Regs::R3,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::R4,
|
BackdoorArgs::Arg4 => Regs::R4,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::R5,
|
BackdoorArgs::Arg5 => Regs::R5,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::R6,
|
BackdoorArgs::Arg6 => Regs::R6,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use addr2line::object::{Object, ObjectSection};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, state::HasMetadata,
|
executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, state::HasMetadata,
|
||||||
};
|
};
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
use libc::{
|
use libc::{
|
||||||
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
|
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
|
||||||
};
|
};
|
||||||
@ -20,14 +21,14 @@ use rangemap::RangeMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calls::FullBacktraceCollector,
|
calls::FullBacktraceCollector,
|
||||||
emu::{EmuError, Emulator, MemAccessInfo, SyscallHookResult},
|
emu::{EmuError, MemAccessInfo, SyscallHookResult},
|
||||||
helper::{
|
helper::{
|
||||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
snapshot::QemuSnapshotHelper,
|
snapshot::QemuSnapshotHelper,
|
||||||
GuestAddr, Regs,
|
Qemu, Regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO at some point, merge parts with libafl_frida
|
// TODO at some point, merge parts with libafl_frida
|
||||||
@ -131,7 +132,7 @@ impl core::fmt::Display for AsanError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AsanErrorCallback = Box<dyn FnMut(&AsanGiovese, &Emulator, GuestAddr, AsanError)>;
|
pub type AsanErrorCallback = Box<dyn FnMut(&AsanGiovese, Qemu, GuestAddr, AsanError)>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AllocTreeItem {
|
pub struct AllocTreeItem {
|
||||||
@ -209,7 +210,7 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn new(emu: &Emulator) -> Pin<Box<Self>> {
|
fn new(emu: Qemu) -> 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(),
|
||||||
@ -237,34 +238,34 @@ impl AsanGiovese {
|
|||||||
) -> SyscallHookResult {
|
) -> SyscallHookResult {
|
||||||
if sys_num == QASAN_FAKESYS_NR {
|
if sys_num == QASAN_FAKESYS_NR {
|
||||||
let mut r = 0;
|
let mut r = 0;
|
||||||
let emulator = Emulator::get().unwrap();
|
let qemu = Qemu::get().unwrap();
|
||||||
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
|
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
|
||||||
QasanAction::Poison => {
|
QasanAction::Poison => {
|
||||||
self.poison(
|
self.poison(
|
||||||
&emulator,
|
qemu,
|
||||||
a1,
|
a1,
|
||||||
a2 as usize,
|
a2 as usize,
|
||||||
PoisonKind::try_from(a3 as i8).unwrap().into(),
|
PoisonKind::try_from(a3 as i8).unwrap().into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
QasanAction::UserPoison => {
|
QasanAction::UserPoison => {
|
||||||
self.poison(&emulator, a1, a2 as usize, PoisonKind::User.into());
|
self.poison(qemu, a1, a2 as usize, PoisonKind::User.into());
|
||||||
}
|
}
|
||||||
QasanAction::UnPoison => {
|
QasanAction::UnPoison => {
|
||||||
Self::unpoison(&emulator, a1, a2 as usize);
|
Self::unpoison(qemu, a1, a2 as usize);
|
||||||
}
|
}
|
||||||
QasanAction::IsPoison => {
|
QasanAction::IsPoison => {
|
||||||
if Self::is_invalid_access(&emulator, a1, a2 as usize) {
|
if Self::is_invalid_access(qemu, a1, a2 as usize) {
|
||||||
r = 1;
|
r = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QasanAction::Alloc => {
|
QasanAction::Alloc => {
|
||||||
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
|
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
self.allocation(pc, a1, a2);
|
self.allocation(pc, a1, a2);
|
||||||
}
|
}
|
||||||
QasanAction::Dealloc => {
|
QasanAction::Dealloc => {
|
||||||
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
|
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
self.deallocation(&emulator, pc, a1);
|
self.deallocation(qemu, pc, a1);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -284,9 +285,9 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_invalid_access_1(emu: &Emulator, addr: GuestAddr) -> bool {
|
pub fn is_invalid_access_1(qemu: Qemu, addr: GuestAddr) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = emu.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;
|
let k = *shadow_addr as isize;
|
||||||
k != 0 && (h & 7).wrapping_add(1) > k
|
k != 0 && (h & 7).wrapping_add(1) > k
|
||||||
@ -295,9 +296,9 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_invalid_access_2(emu: &Emulator, addr: GuestAddr) -> bool {
|
pub fn is_invalid_access_2(qemu: Qemu, addr: GuestAddr) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = emu.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;
|
let k = *shadow_addr as isize;
|
||||||
k != 0 && (h & 7).wrapping_add(2) > k
|
k != 0 && (h & 7).wrapping_add(2) > k
|
||||||
@ -306,9 +307,9 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_invalid_access_4(emu: &Emulator, addr: GuestAddr) -> bool {
|
pub fn is_invalid_access_4(qemu: Qemu, addr: GuestAddr) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = emu.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;
|
let k = *shadow_addr as isize;
|
||||||
k != 0 && (h & 7).wrapping_add(4) > k
|
k != 0 && (h & 7).wrapping_add(4) > k
|
||||||
@ -317,9 +318,9 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_invalid_access_8(emu: &Emulator, addr: GuestAddr) -> bool {
|
pub fn is_invalid_access_8(qemu: Qemu, addr: GuestAddr) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = emu.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);
|
||||||
*shadow_addr != 0
|
*shadow_addr != 0
|
||||||
}
|
}
|
||||||
@ -328,7 +329,7 @@ impl AsanGiovese {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn is_invalid_access(emu: &Emulator, addr: GuestAddr, n: usize) -> bool {
|
pub fn is_invalid_access(qemu: Qemu, addr: GuestAddr, n: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return false;
|
return false;
|
||||||
@ -343,12 +344,12 @@ impl AsanGiovese {
|
|||||||
let next_8 = (start & !7).wrapping_add(8);
|
let next_8 = (start & !7).wrapping_add(8);
|
||||||
let first_size = next_8.wrapping_sub(start) as isize;
|
let first_size = next_8.wrapping_sub(start) as isize;
|
||||||
if n <= first_size {
|
if n <= first_size {
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) 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;
|
let k = *shadow_addr as isize;
|
||||||
return k != 0 && (h & 7).wrapping_add(n) > k;
|
return k != 0 && (h & 7).wrapping_add(n) > k;
|
||||||
}
|
}
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) 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;
|
let k = *shadow_addr as isize;
|
||||||
if k != 0 && (h & 7).wrapping_add(first_size) > k {
|
if k != 0 && (h & 7).wrapping_add(first_size) > k {
|
||||||
@ -358,7 +359,7 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while start < last_8 {
|
while start < last_8 {
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
if *shadow_addr != 0 {
|
if *shadow_addr != 0 {
|
||||||
return true;
|
return true;
|
||||||
@ -367,7 +368,7 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if last_8 != end {
|
if last_8 != end {
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) as isize;
|
||||||
let last_size = end.wrapping_sub(last_8) as isize;
|
let last_size = end.wrapping_sub(last_8) 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;
|
let k = *shadow_addr as isize;
|
||||||
@ -380,7 +381,7 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn poison(&mut self, emu: &Emulator, addr: GuestAddr, n: usize, poison_byte: i8) -> bool {
|
pub fn poison(&mut self, qemu: Qemu, addr: GuestAddr, n: usize, poison_byte: i8) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return false;
|
return false;
|
||||||
@ -406,14 +407,14 @@ impl AsanGiovese {
|
|||||||
if n < first_size {
|
if n < first_size {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
*shadow_addr = (8isize).wrapping_sub(first_size) as i8;
|
*shadow_addr = (8isize).wrapping_sub(first_size) as i8;
|
||||||
start = next_8;
|
start = next_8;
|
||||||
}
|
}
|
||||||
|
|
||||||
while start < last_8 {
|
while start < last_8 {
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
*shadow_addr = poison_byte;
|
*shadow_addr = poison_byte;
|
||||||
start = (start).wrapping_add(8);
|
start = (start).wrapping_add(8);
|
||||||
@ -426,14 +427,14 @@ impl AsanGiovese {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::must_use_candidate)]
|
#[allow(clippy::must_use_candidate)]
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
pub fn unpoison(emu: &Emulator, addr: GuestAddr, n: usize) -> bool {
|
pub fn unpoison(qemu: Qemu, addr: GuestAddr, n: usize) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
let n = n as isize;
|
let n = n as isize;
|
||||||
let mut start = addr;
|
let mut start = addr;
|
||||||
let end = start.wrapping_add(n as GuestAddr);
|
let end = start.wrapping_add(n as GuestAddr);
|
||||||
|
|
||||||
while start < end {
|
while start < end {
|
||||||
let h = emu.g2h::<*const c_void>(start) as isize;
|
let h = qemu.g2h::<*const c_void>(start) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
*shadow_addr = 0;
|
*shadow_addr = 0;
|
||||||
start = (start).wrapping_add(8);
|
start = (start).wrapping_add(8);
|
||||||
@ -443,9 +444,9 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unpoison_page(emu: &Emulator, page: GuestAddr) {
|
pub fn unpoison_page(qemu: Qemu, page: GuestAddr) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = emu.g2h::<*const c_void>(page) as isize;
|
let h = qemu.g2h::<*const c_void>(page) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
shadow_addr.write_bytes(0, SHADOW_PAGE_SIZE);
|
shadow_addr.write_bytes(0, SHADOW_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
@ -453,26 +454,26 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
fn get_shadow_page(emu: &Emulator, page: GuestAddr) -> &mut [i8] {
|
fn get_shadow_page(qemu: &Qemu, page: GuestAddr) -> &mut [i8] {
|
||||||
unsafe {
|
unsafe {
|
||||||
let h = emu.g2h::<*const c_void>(page) as isize;
|
let h = qemu.g2h::<*const c_void>(page) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
std::slice::from_raw_parts_mut(shadow_addr, SHADOW_PAGE_SIZE)
|
std::slice::from_raw_parts_mut(shadow_addr, SHADOW_PAGE_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_or_crash(&mut self, emu: &Emulator, pc: GuestAddr, error: AsanError) {
|
pub fn report_or_crash(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) {
|
||||||
if let Some(mut cb) = self.error_callback.take() {
|
if let Some(mut cb) = self.error_callback.take() {
|
||||||
(cb)(self, emu, pc, error);
|
(cb)(self, qemu, pc, error);
|
||||||
self.error_callback = Some(cb);
|
self.error_callback = Some(cb);
|
||||||
} else {
|
} else {
|
||||||
std::process::abort();
|
std::process::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(&mut self, emu: &Emulator, pc: GuestAddr, error: AsanError) {
|
pub fn report(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) {
|
||||||
if let Some(mut cb) = self.error_callback.take() {
|
if let Some(mut cb) = self.error_callback.take() {
|
||||||
(cb)(self, emu, pc, error);
|
(cb)(self, qemu, pc, error);
|
||||||
self.error_callback = Some(cb);
|
self.error_callback = Some(cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -502,7 +503,7 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc_free(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
pub fn alloc_free(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
let mut chunk = None;
|
let mut chunk = None;
|
||||||
self.alloc_map_mut(addr, |interval, item| {
|
self.alloc_map_mut(addr, |interval, item| {
|
||||||
chunk = Some(*interval);
|
chunk = Some(*interval);
|
||||||
@ -518,11 +519,11 @@ impl AsanGiovese {
|
|||||||
if let Some(ck) = chunk {
|
if let Some(ck) = chunk {
|
||||||
if ck.start != addr {
|
if ck.start != addr {
|
||||||
// Free not the start of the chunk
|
// Free not the start of the chunk
|
||||||
self.report_or_crash(emulator, pc, AsanError::BadFree(addr, Some(ck)));
|
self.report_or_crash(qemu, pc, AsanError::BadFree(addr, Some(ck)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Free of wild ptr
|
// Free of wild ptr
|
||||||
self.report_or_crash(emulator, pc, AsanError::BadFree(addr, None));
|
self.report_or_crash(qemu, pc, AsanError::BadFree(addr, None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,16 +597,16 @@ impl AsanGiovese {
|
|||||||
self.alloc_insert(pc, start, end);
|
self.alloc_insert(pc, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deallocation(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
pub fn deallocation(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
self.alloc_free(emulator, pc, addr);
|
self.alloc_free(qemu, pc, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapshot(&mut self, emu: &Emulator) {
|
pub fn snapshot(&mut self, qemu: Qemu) {
|
||||||
if self.snapshot_shadow {
|
if self.snapshot_shadow {
|
||||||
let set = self.dirty_shadow.lock().unwrap();
|
let set = self.dirty_shadow.lock().unwrap();
|
||||||
|
|
||||||
for &page in &*set {
|
for &page in &*set {
|
||||||
let data = Self::get_shadow_page(emu, page).to_vec();
|
let data = Self::get_shadow_page(&qemu, page).to_vec();
|
||||||
self.saved_shadow.insert(page, data);
|
self.saved_shadow.insert(page, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +615,7 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rollback(&mut self, emu: &Emulator, detect_leaks: bool) -> AsanRollback {
|
pub fn rollback(&mut self, qemu: Qemu, detect_leaks: bool) -> AsanRollback {
|
||||||
let mut leaks = vec![];
|
let mut leaks = vec![];
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -637,10 +638,10 @@ impl AsanGiovese {
|
|||||||
for &page in &*set {
|
for &page in &*set {
|
||||||
let original = self.saved_shadow.get(&page);
|
let original = self.saved_shadow.get(&page);
|
||||||
if let Some(data) = original {
|
if let Some(data) = original {
|
||||||
let cur = Self::get_shadow_page(emu, page);
|
let cur = Self::get_shadow_page(&qemu, page);
|
||||||
cur.copy_from_slice(data);
|
cur.copy_from_slice(data);
|
||||||
} else {
|
} else {
|
||||||
Self::unpoison_page(emu, page);
|
Self::unpoison_page(qemu, page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,8 +656,8 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
for interval in leaks {
|
for interval in leaks {
|
||||||
self.report(
|
self.report(
|
||||||
emu,
|
qemu,
|
||||||
emu.read_reg(Regs::Pc).unwrap(),
|
qemu.read_reg(Regs::Pc).unwrap(),
|
||||||
AsanError::MemLeak(interval),
|
AsanError::MemLeak(interval),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -667,10 +668,10 @@ impl AsanGiovese {
|
|||||||
|
|
||||||
static mut ASAN_INITED: bool = false;
|
static mut ASAN_INITED: bool = false;
|
||||||
|
|
||||||
pub fn init_with_asan(
|
pub fn init_qemu_with_asan(
|
||||||
args: &mut Vec<String>,
|
args: &mut Vec<String>,
|
||||||
env: &mut [(String, String)],
|
env: &mut [(String, String)],
|
||||||
) -> Result<(Emulator, Pin<Box<AsanGiovese>>), EmuError> {
|
) -> Result<(Qemu, Pin<Box<AsanGiovese>>), EmuError> {
|
||||||
let current = env::current_exe().unwrap();
|
let current = env::current_exe().unwrap();
|
||||||
let asan_lib = fs::canonicalize(current)
|
let asan_lib = fs::canonicalize(current)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -716,10 +717,10 @@ pub fn init_with_asan(
|
|||||||
ASAN_INITED = true;
|
ASAN_INITED = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let emu = Emulator::new(args, env)?;
|
let qemu = Qemu::init(args, env)?;
|
||||||
let rt = AsanGiovese::new(&emu);
|
let rt = AsanGiovese::new(qemu);
|
||||||
|
|
||||||
Ok((emu, rt))
|
Ok((qemu, rt))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum QemuAsanOptions {
|
pub enum QemuAsanOptions {
|
||||||
@ -756,7 +757,7 @@ impl QemuAsanHelper {
|
|||||||
filter: QemuInstrumentationAddressRangeFilter,
|
filter: QemuInstrumentationAddressRangeFilter,
|
||||||
options: QemuAsanOptions,
|
options: QemuAsanOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)");
|
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),
|
||||||
@ -780,7 +781,7 @@ impl QemuAsanHelper {
|
|||||||
error_callback: AsanErrorCallback,
|
error_callback: AsanErrorCallback,
|
||||||
options: QemuAsanOptions,
|
options: QemuAsanOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)");
|
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),
|
||||||
@ -825,107 +826,95 @@ impl QemuAsanHelper {
|
|||||||
self.rt.allocation(pc, start, end);
|
self.rt.allocation(pc, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dealloc(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
pub fn dealloc(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
self.rt.deallocation(emulator, pc, addr);
|
self.rt.deallocation(qemu, pc, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_poisoned(&self, emulator: &Emulator, addr: GuestAddr, size: usize) -> bool {
|
pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool {
|
||||||
AsanGiovese::is_invalid_access(emulator, addr, size)
|
AsanGiovese::is_invalid_access(qemu, addr, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_1(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
pub fn read_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_1(emulator, addr) {
|
if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) {
|
||||||
|
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) {
|
||||||
self.rt
|
self.rt
|
||||||
.report_or_crash(emulator, pc, AsanError::Read(addr, 1));
|
.report_or_crash(qemu, pc, AsanError::Read(addr, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_2(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
pub fn write_1(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_2(emulator, addr) {
|
if self.enabled() && AsanGiovese::is_invalid_access_1(qemu, addr) {
|
||||||
|
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if self.enabled() && AsanGiovese::is_invalid_access(qemu, addr, size) {
|
||||||
self.rt
|
self.rt
|
||||||
.report_or_crash(emulator, pc, AsanError::Read(addr, 2));
|
.report_or_crash(qemu, pc, AsanError::Write(addr, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_4(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
pub fn poison(&mut self, qemu: Qemu, addr: GuestAddr, size: usize, poison: PoisonKind) {
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_4(emulator, addr) {
|
self.rt.poison(qemu, addr, size, poison.into());
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Read(addr, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_8(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_8(emulator, addr) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Read(addr, 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_n(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access(emulator, addr, size) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Read(addr, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_1(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_1(emulator, addr) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Write(addr, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_2(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_2(emulator, addr) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Write(addr, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_4(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_4(emulator, addr) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Write(addr, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_8(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_8(emulator, addr) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Write(addr, 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_n(&mut self, emulator: &Emulator, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access(emulator, addr, size) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(emulator, pc, AsanError::Write(addr, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poison(
|
|
||||||
&mut self,
|
|
||||||
emulator: &Emulator,
|
|
||||||
addr: GuestAddr,
|
|
||||||
size: usize,
|
|
||||||
poison: PoisonKind,
|
|
||||||
) {
|
|
||||||
self.rt.poison(emulator, addr, size, poison.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
pub fn unpoison(&mut self, emulator: &Emulator, addr: GuestAddr, size: usize) {
|
pub fn unpoison(&mut self, qemu: Qemu, addr: GuestAddr, size: usize) {
|
||||||
AsanGiovese::unpoison(emulator, addr, size);
|
AsanGiovese::unpoison(qemu, addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, emulator: &Emulator) -> AsanRollback {
|
pub fn reset(&mut self, qemu: Qemu) -> AsanRollback {
|
||||||
self.rt.rollback(emulator, self.detect_leaks)
|
self.rt.rollback(qemu, self.detect_leaks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuAsanHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
for QemuAsanHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
&self.filter
|
&self.filter
|
||||||
}
|
}
|
||||||
@ -987,23 +976,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
|
fn pre_exec(&mut self, qemu: Qemu, _input: &S::Input) {
|
||||||
if self.empty {
|
if self.empty {
|
||||||
self.rt.snapshot(emulator);
|
self.rt.snapshot(qemu);
|
||||||
self.empty = false;
|
self.empty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec<OT>(
|
fn post_exec<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator: &Emulator,
|
qemu: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
exit_kind: &mut ExitKind,
|
exit_kind: &mut ExitKind,
|
||||||
) where
|
) where
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
{
|
{
|
||||||
if self.reset(emulator) == AsanRollback::HasLeaks {
|
if self.reset(qemu) == AsanRollback::HasLeaks {
|
||||||
*exit_kind = ExitKind::Crash;
|
*exit_kind = ExitKind::Crash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1014,10 +1003,10 @@ where
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emu = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
let pc: GuestAddr = emu.read_reg(Regs::Pc).unwrap();
|
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
h.rt.report(&emu, pc, AsanError::Signal(target_sig));
|
h.rt.report(qemu, pc, AsanError::Signal(target_sig));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_readwrite_asan<QT, S>(
|
pub fn gen_readwrite_asan<QT, S>(
|
||||||
@ -1047,9 +1036,9 @@ pub fn trace_read1_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_1(&emulator, id as GuestAddr, addr);
|
h.read_1(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read2_asan<QT, S>(
|
pub fn trace_read2_asan<QT, S>(
|
||||||
@ -1061,9 +1050,9 @@ pub fn trace_read2_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_2(&emulator, id as GuestAddr, addr);
|
h.read_2(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read4_asan<QT, S>(
|
pub fn trace_read4_asan<QT, S>(
|
||||||
@ -1075,9 +1064,9 @@ pub fn trace_read4_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_4(&emulator, id as GuestAddr, addr);
|
h.read_4(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read8_asan<QT, S>(
|
pub fn trace_read8_asan<QT, S>(
|
||||||
@ -1089,9 +1078,9 @@ pub fn trace_read8_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_8(&emulator, id as GuestAddr, addr);
|
h.read_8(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_read_n_asan<QT, S>(
|
pub fn trace_read_n_asan<QT, S>(
|
||||||
@ -1104,9 +1093,9 @@ pub fn trace_read_n_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_n(&emulator, id as GuestAddr, addr, size);
|
h.read_n(qemu, id as GuestAddr, addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write1_asan<QT, S>(
|
pub fn trace_write1_asan<QT, S>(
|
||||||
@ -1118,9 +1107,9 @@ pub fn trace_write1_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_1(&emulator, id as GuestAddr, addr);
|
h.write_1(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write2_asan<QT, S>(
|
pub fn trace_write2_asan<QT, S>(
|
||||||
@ -1132,9 +1121,9 @@ pub fn trace_write2_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_2(&emulator, id as GuestAddr, addr);
|
h.write_2(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write4_asan<QT, S>(
|
pub fn trace_write4_asan<QT, S>(
|
||||||
@ -1146,9 +1135,9 @@ pub fn trace_write4_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_4(&emulator, id as GuestAddr, addr);
|
h.write_4(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write8_asan<QT, S>(
|
pub fn trace_write8_asan<QT, S>(
|
||||||
@ -1160,9 +1149,9 @@ pub fn trace_write8_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_8(&emulator, id as GuestAddr, addr);
|
h.write_8(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_write_n_asan<QT, S>(
|
pub fn trace_write_n_asan<QT, S>(
|
||||||
@ -1175,9 +1164,9 @@ pub fn trace_write_n_asan<QT, S>(
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_n(&emulator, id as GuestAddr, addr, size);
|
h.read_n(qemu, id as GuestAddr, addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_write_asan_snapshot<QT, S>(
|
pub fn gen_write_asan_snapshot<QT, S>(
|
||||||
@ -1208,9 +1197,9 @@ pub fn trace_write1_asan_snapshot<QT, S>(
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_1(&emulator, id as GuestAddr, addr);
|
h.write_1(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(addr, 1);
|
h.access(addr, 1);
|
||||||
@ -1226,9 +1215,9 @@ pub fn trace_write2_asan_snapshot<QT, S>(
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_2(&emulator, id as GuestAddr, addr);
|
h.write_2(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(addr, 2);
|
h.access(addr, 2);
|
||||||
@ -1244,9 +1233,9 @@ pub fn trace_write4_asan_snapshot<QT, S>(
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_4(&emulator, id as GuestAddr, addr);
|
h.write_4(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(addr, 4);
|
h.access(addr, 4);
|
||||||
@ -1262,9 +1251,9 @@ pub fn trace_write8_asan_snapshot<QT, S>(
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.write_8(&emulator, id as GuestAddr, addr);
|
h.write_8(qemu, id as GuestAddr, addr);
|
||||||
}
|
}
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(addr, 8);
|
h.access(addr, 8);
|
||||||
@ -1281,9 +1270,9 @@ pub fn trace_write_n_asan_snapshot<QT, S>(
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if id != 0 {
|
if id != 0 {
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
h.read_n(&emulator, id as GuestAddr, addr, size);
|
h.read_n(qemu, id as GuestAddr, addr, size);
|
||||||
}
|
}
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(addr, size);
|
h.access(addr, size);
|
||||||
@ -1308,16 +1297,16 @@ where
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if sys_num == QASAN_FAKESYS_NR {
|
if sys_num == QASAN_FAKESYS_NR {
|
||||||
let emulator = hooks.emulator().clone();
|
let qemu = *hooks.qemu();
|
||||||
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuAsanHelper>().unwrap();
|
||||||
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
|
match QasanAction::try_from(a0).expect("Invalid QASan action number") {
|
||||||
QasanAction::CheckLoad => {
|
QasanAction::CheckLoad => {
|
||||||
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
|
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
h.read_n(&emulator, pc, a1, a2 as usize);
|
h.read_n(qemu, pc, a1, a2 as usize);
|
||||||
}
|
}
|
||||||
QasanAction::CheckStore => {
|
QasanAction::CheckStore => {
|
||||||
let pc: GuestAddr = emulator.read_reg(Regs::Pc).unwrap();
|
let pc: GuestAddr = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
h.write_n(&emulator, pc, a1, a2 as usize);
|
h.write_n(qemu, pc, a1, a2 as usize);
|
||||||
}
|
}
|
||||||
QasanAction::Enable => {
|
QasanAction::Enable => {
|
||||||
h.set_enabled(true);
|
h.set_enabled(true);
|
||||||
@ -1358,9 +1347,9 @@ fn load_file_section<'input, 'arena, Endian: addr2line::gimli::Endianity>(
|
|||||||
|
|
||||||
#[allow(clippy::unnecessary_cast)]
|
#[allow(clippy::unnecessary_cast)]
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn asan_report(rt: &AsanGiovese, emu: &Emulator, pc: GuestAddr, err: AsanError) {
|
pub fn asan_report(rt: &AsanGiovese, qemu: Qemu, pc: GuestAddr, err: AsanError) {
|
||||||
let mut regions = std::collections::HashMap::new();
|
let mut regions = std::collections::HashMap::new();
|
||||||
for region in emu.mappings() {
|
for region in qemu.mappings() {
|
||||||
if let Some(path) = region.path() {
|
if let Some(path) = region.path() {
|
||||||
let start = region.start();
|
let start = region.start();
|
||||||
let end = region.end();
|
let end = region.end();
|
||||||
@ -1562,6 +1551,9 @@ pub fn asan_report(rt: &AsanGiovese, emu: &Emulator, pc: GuestAddr, err: AsanErr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fix pc in case it is not synced (in hooks)
|
// fix pc in case it is not synced (in hooks)
|
||||||
emu.write_reg(Regs::Pc, pc).unwrap();
|
qemu.write_reg(Regs::Pc, pc).unwrap();
|
||||||
eprint!("Context:\n{}", emu.current_cpu().unwrap().display_context());
|
eprint!(
|
||||||
|
"Context:\n{}",
|
||||||
|
qemu.current_cpu().unwrap().display_context()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
95
libafl_qemu/src/breakpoint.rs
Normal file
95
libafl_qemu/src/breakpoint.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
|
|
||||||
|
use crate::{command::Command, Qemu};
|
||||||
|
|
||||||
|
// TODO: distinguish breakpoints with IDs instead of addresses to avoid collisions.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Breakpoint {
|
||||||
|
addr: GuestAddr,
|
||||||
|
cmd: Option<Command>,
|
||||||
|
disable_on_trigger: bool,
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Breakpoint {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.addr.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Breakpoint {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.addr == other.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Breakpoint {}
|
||||||
|
|
||||||
|
impl Display for Breakpoint {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Breakpoint @vaddr 0x{:x}", self.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<GuestAddr> for Breakpoint {
|
||||||
|
fn borrow(&self) -> &GuestAddr {
|
||||||
|
&self.addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Breakpoint {
|
||||||
|
// Emu will return with the breakpoint as exit reason.
|
||||||
|
#[must_use]
|
||||||
|
pub fn without_command(addr: GuestAddr, disable_on_trigger: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
addr,
|
||||||
|
cmd: None,
|
||||||
|
disable_on_trigger,
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emu will execute the command when it meets the breakpoint.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_command(addr: GuestAddr, cmd: Command, disable_on_trigger: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
addr,
|
||||||
|
cmd: Some(cmd),
|
||||||
|
disable_on_trigger,
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn addr(&self) -> GuestAddr {
|
||||||
|
self.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable(&mut self, qemu: &Qemu) {
|
||||||
|
if !self.enabled {
|
||||||
|
qemu.set_breakpoint(self.addr);
|
||||||
|
self.enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable(&mut self, qemu: &Qemu) {
|
||||||
|
if self.enabled {
|
||||||
|
qemu.remove_breakpoint(self.addr.into());
|
||||||
|
self.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger(&mut self, qemu: &Qemu) -> Option<&Command> {
|
||||||
|
if self.disable_on_trigger {
|
||||||
|
self.disable(qemu);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cmd.as_ref()
|
||||||
|
}
|
||||||
|
}
|
@ -7,17 +7,18 @@ use libafl::{
|
|||||||
observers::{stacktrace::BacktraceObserver, ObserversTuple},
|
observers::{stacktrace::BacktraceObserver, ObserversTuple},
|
||||||
};
|
};
|
||||||
use libafl_bolts::{tuples::MatchFirstType, Named};
|
use libafl_bolts::{tuples::MatchFirstType, Named};
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
capstone,
|
capstone,
|
||||||
emu::{ArchExtras, Emulator},
|
emu::ArchExtras,
|
||||||
helper::{
|
helper::{
|
||||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
GuestAddr,
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait CallTraceCollector: 'static + Debug {
|
pub trait CallTraceCollector: 'static + Debug {
|
||||||
@ -42,7 +43,7 @@ pub trait CallTraceCollector: 'static + Debug {
|
|||||||
QT: QemuHelperTuple<S>;
|
QT: QemuHelperTuple<S>;
|
||||||
|
|
||||||
// Frowarded from the `QemuCallTracerHelper`
|
// Frowarded from the `QemuCallTracerHelper`
|
||||||
fn pre_exec<I>(&mut self, _emulator: &Emulator, _input: &I)
|
fn pre_exec<I>(&mut self, _qemu: Qemu, _input: &I)
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
@ -50,7 +51,7 @@ pub trait CallTraceCollector: 'static + Debug {
|
|||||||
|
|
||||||
fn post_exec<OT, S>(
|
fn post_exec<OT, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -82,13 +83,13 @@ pub trait CallTraceCollectorTuple: 'static + MatchFirstType + Debug {
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>;
|
QT: QemuHelperTuple<S>;
|
||||||
|
|
||||||
fn pre_exec_all<I>(&mut self, _emulator: &Emulator, input: &I)
|
fn pre_exec_all<I>(&mut self, _qemu: Qemu, input: &I)
|
||||||
where
|
where
|
||||||
I: Input;
|
I: Input;
|
||||||
|
|
||||||
fn post_exec_all<OT, S>(
|
fn post_exec_all<OT, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -122,7 +123,7 @@ impl CallTraceCollectorTuple for () {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_all<I>(&mut self, _emulator: &Emulator, _input: &I)
|
fn pre_exec_all<I>(&mut self, _qemu: Qemu, _input: &I)
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
@ -130,7 +131,7 @@ impl CallTraceCollectorTuple for () {
|
|||||||
|
|
||||||
fn post_exec_all<OT, S>(
|
fn post_exec_all<OT, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_emulator: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -190,17 +191,17 @@ where
|
|||||||
self.1.on_ret_all(hooks, state, pc, ret_addr);
|
self.1.on_ret_all(hooks, state, pc, ret_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_all<I>(&mut self, emulator: &Emulator, input: &I)
|
fn pre_exec_all<I>(&mut self, qemu: Qemu, input: &I)
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
self.0.pre_exec(emulator, input);
|
self.0.pre_exec(qemu, input);
|
||||||
self.1.pre_exec_all(emulator, input);
|
self.1.pre_exec_all(qemu, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec_all<OT, S>(
|
fn post_exec_all<OT, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator: &Emulator,
|
qemu: Qemu,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
exit_kind: &mut ExitKind,
|
exit_kind: &mut ExitKind,
|
||||||
@ -208,8 +209,8 @@ where
|
|||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
self.0.post_exec(emulator, input, observers, exit_kind);
|
self.0.post_exec(qemu, input, observers, exit_kind);
|
||||||
self.1.post_exec_all(emulator, input, observers, exit_kind);
|
self.1.post_exec_all(qemu, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ where
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let ret_addr: GuestAddr = hooks.emulator().read_return_address().unwrap();
|
let ret_addr: GuestAddr = hooks.qemu().read_return_address().unwrap();
|
||||||
|
|
||||||
// log::info!("RET @ 0x{:#x}", ret_addr);
|
// log::info!("RET @ 0x{:#x}", ret_addr);
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ where
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let emu = hooks.emulator();
|
let emu = hooks.qemu();
|
||||||
|
|
||||||
if let Some(h) = hooks.helpers().match_first_type::<Self>() {
|
if let Some(h) = hooks.helpers().match_first_type::<Self>() {
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
@ -383,9 +384,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCallTracerHelper<T>
|
impl<T, S> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
for QemuCallTracerHelper<T>
|
||||||
where
|
where
|
||||||
T: CallTraceCollectorTuple,
|
T: CallTraceCollectorTuple,
|
||||||
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
&self.filter
|
&self.filter
|
||||||
@ -412,16 +415,13 @@ where
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec(&mut self, emulator: &Emulator, input: &S::Input) {
|
fn pre_exec(&mut self, qemu: Qemu, input: &S::Input) {
|
||||||
self.collectors
|
self.collectors.as_mut().unwrap().pre_exec_all(qemu, input);
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.pre_exec_all(emulator, input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec<OT>(
|
fn post_exec<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator: &Emulator,
|
qemu: Qemu,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
exit_kind: &mut ExitKind,
|
exit_kind: &mut ExitKind,
|
||||||
@ -431,7 +431,7 @@ where
|
|||||||
self.collectors
|
self.collectors
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.post_exec_all(emulator, input, observers, exit_kind);
|
.post_exec_all(qemu, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,7 +498,7 @@ impl CallTraceCollector for OnCrashBacktraceCollector {
|
|||||||
self.callstack_hash ^= ret_addr as u64;
|
self.callstack_hash ^= ret_addr as u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec<I>(&mut self, _emulator: &Emulator, _input: &I)
|
fn pre_exec<I>(&mut self, _qemu: Qemu, _input: &I)
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
@ -507,7 +507,7 @@ impl CallTraceCollector for OnCrashBacktraceCollector {
|
|||||||
|
|
||||||
fn post_exec<OT, S>(
|
fn post_exec<OT, S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
exit_kind: &mut ExitKind,
|
exit_kind: &mut ExitKind,
|
||||||
@ -602,7 +602,7 @@ impl CallTraceCollector for FullBacktraceCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec<I>(&mut self, _emulator: &Emulator, _input: &I)
|
fn pre_exec<I>(&mut self, _qemu: Qemu, _input: &I)
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
use capstone::{arch::BuildsCapstone, Capstone, InsnDetail};
|
use capstone::{arch::BuildsCapstone, Capstone, InsnDetail};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl::{inputs::UsesInput, state::HasMetadata};
|
use libafl::{inputs::UsesInput, state::HasMetadata};
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
pub use libafl_targets::{
|
pub use libafl_targets::{
|
||||||
cmps::{
|
cmps::{
|
||||||
__libafl_targets_cmplog_instructions, __libafl_targets_cmplog_routines, CMPLOG_ENABLED,
|
__libafl_targets_cmplog_instructions, __libafl_targets_cmplog_routines, CMPLOG_ENABLED,
|
||||||
@ -11,18 +12,13 @@ pub use libafl_targets::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
use crate::{
|
use crate::{capstone, emu::ArchExtras, CallingConvention, Qemu};
|
||||||
capstone,
|
|
||||||
emu::{ArchExtras, Emulator},
|
|
||||||
CallingConvention,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
helper::{
|
helper::{
|
||||||
hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
GuestAddr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -70,7 +66,9 @@ impl Default for QemuCmpLogHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmpLogHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
for QemuCmpLogHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
&self.filter
|
&self.filter
|
||||||
}
|
}
|
||||||
@ -246,12 +244,12 @@ impl QemuCmpLogRoutinesHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let emu = Emulator::get().unwrap();
|
let qemu = Qemu::get().unwrap();
|
||||||
|
|
||||||
let a0: GuestAddr = emu
|
let a0: GuestAddr = qemu
|
||||||
.read_function_argument(CallingConvention::Cdecl, 0)
|
.read_function_argument(CallingConvention::Cdecl, 0)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let a1: GuestAddr = emu
|
let a1: GuestAddr = qemu
|
||||||
.read_function_argument(CallingConvention::Cdecl, 1)
|
.read_function_argument(CallingConvention::Cdecl, 1)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
@ -262,7 +260,7 @@ impl QemuCmpLogRoutinesHelper {
|
|||||||
// if !emu.access_ok(VerifyAccess::Read, a0, 0x20) || !emu.access_ok(VerifyAccess::Read, a1, 0x20) { return; }
|
// if !emu.access_ok(VerifyAccess::Read, a0, 0x20) || !emu.access_ok(VerifyAccess::Read, a1, 0x20) { return; }
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
__libafl_targets_cmplog_routines(k as usize, emu.g2h(a0), emu.g2h(a1));
|
__libafl_targets_cmplog_routines(k as usize, qemu.g2h(a0), qemu.g2h(a1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,21 +287,21 @@ impl QemuCmpLogRoutinesHelper {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let emu = hooks.emulator();
|
let qemu = hooks.qemu();
|
||||||
|
|
||||||
if let Some(h) = hooks.helpers().match_first_type::<Self>() {
|
if let Some(h) = hooks.helpers().match_first_type::<Self>() {
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut code = {
|
let mut code = {
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
unsafe {
|
unsafe {
|
||||||
std::slice::from_raw_parts(emu.g2h(pc), 512)
|
std::slice::from_raw_parts(qemu.g2h(pc), 512)
|
||||||
}
|
}
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
&mut [0; 512]
|
&mut [0; 512]
|
||||||
};
|
};
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.read_mem(pc, code)
|
qemu.read_mem(pc, code)
|
||||||
}; // TODO handle faults
|
}; // TODO handle faults
|
||||||
|
|
||||||
let mut iaddr = pc;
|
let mut iaddr = pc;
|
||||||
@ -318,7 +316,7 @@ impl QemuCmpLogRoutinesHelper {
|
|||||||
match u32::from(detail.0) {
|
match u32::from(detail.0) {
|
||||||
capstone::InsnGroupType::CS_GRP_CALL => {
|
capstone::InsnGroupType::CS_GRP_CALL => {
|
||||||
let k = (hash_me(pc.into())) & (CMPLOG_MAP_W as u64 - 1);
|
let k = (hash_me(pc.into())) & (CMPLOG_MAP_W as u64 - 1);
|
||||||
emu.set_hook(k, insn.address() as GuestAddr, Self::on_call, false);
|
qemu.set_hook(k, insn.address() as GuestAddr, Self::on_call, false);
|
||||||
}
|
}
|
||||||
capstone::InsnGroupType::CS_GRP_RET
|
capstone::InsnGroupType::CS_GRP_RET
|
||||||
| capstone::InsnGroupType::CS_GRP_INVALID
|
| capstone::InsnGroupType::CS_GRP_INVALID
|
||||||
@ -335,11 +333,11 @@ impl QemuCmpLogRoutinesHelper {
|
|||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
unsafe {
|
unsafe {
|
||||||
code = std::slice::from_raw_parts(emu.g2h(iaddr), 512);
|
code = std::slice::from_raw_parts(qemu.g2h(iaddr), 512);
|
||||||
}
|
}
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.read_mem(pc, code);
|
qemu.read_mem(pc, code);
|
||||||
} // TODO handle faults
|
} // TODO handle faults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,7 +347,9 @@ impl QemuCmpLogRoutinesHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmpLogRoutinesHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
for QemuCmpLogRoutinesHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
&self.filter
|
&self.filter
|
||||||
}
|
}
|
||||||
|
660
libafl_qemu/src/command.rs
Normal file
660
libafl_qemu/src/command.rs
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
|
||||||
|
use enum_map::Enum;
|
||||||
|
use libafl::{
|
||||||
|
executors::ExitKind,
|
||||||
|
inputs::{BytesInput, HasBytesVec},
|
||||||
|
state::{HasExecutions, State},
|
||||||
|
};
|
||||||
|
use libafl_qemu_sys::{GuestPhysAddr, GuestVirtAddr};
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
use crate::QemuInstrumentationPagingFilter;
|
||||||
|
use crate::{
|
||||||
|
executor::QemuExecutorState, sync_backdoor::SyncBackdoorError, EmuExitHandler, Emulator,
|
||||||
|
GuestAddrKind, GuestReg, HandlerError, HasInstrumentationFilter, InnerHandlerResult, IsFilter,
|
||||||
|
IsSnapshotManager, Qemu, QemuHelperTuple, QemuInstrumentationAddressRangeFilter, Regs,
|
||||||
|
StdEmuExitHandler, StdInstrumentationFilter, CPU,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const VERSION: u64 = bindings::LIBAFL_EXIT_VERSION_NUMBER as u64;
|
||||||
|
|
||||||
|
mod bindings {
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(improper_ctypes)]
|
||||||
|
#![allow(unused_mut)]
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![allow(clippy::pedantic)]
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/backdoor_bindings.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TryFromPrimitive)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum NativeBackdoorCommand {
|
||||||
|
StartVirt = bindings::LibaflExit_LIBAFL_EXIT_START_VIRT.0 as u64, // Shortcut for Save + InputVirt
|
||||||
|
StartPhys = bindings::LibaflExit_LIBAFL_EXIT_START_PHYS.0 as u64, // Shortcut for Save + InputPhys
|
||||||
|
InputVirt = bindings::LibaflExit_LIBAFL_EXIT_INPUT_VIRT.0 as u64, // The address is a virtual address using the paging currently running in the VM.
|
||||||
|
InputPhys = bindings::LibaflExit_LIBAFL_EXIT_INPUT_PHYS.0 as u64, // The address is a physical address
|
||||||
|
End = bindings::LibaflExit_LIBAFL_EXIT_END.0 as u64, // Implies reloading of the target. The first argument gives the exit status.
|
||||||
|
Save = bindings::LibaflExit_LIBAFL_EXIT_SAVE.0 as u64, // Save the VM
|
||||||
|
Load = bindings::LibaflExit_LIBAFL_EXIT_LOAD.0 as u64, // Reload the target without ending the run?
|
||||||
|
Version = bindings::LibaflExit_LIBAFL_EXIT_VERSION.0 as u64, // Version of the bindings used in the target
|
||||||
|
VaddrFilterAllowRange = bindings::LibaflExit_LIBAFL_EXIT_VADDR_FILTER_ALLOW.0 as u64, // Allow given address range
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Enum, TryFromPrimitive)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum NativeExitKind {
|
||||||
|
Unknown = bindings::LibaflExitEndStatus_LIBAFL_EXIT_END_UNKNOWN.0 as u64, // Should not be used
|
||||||
|
Ok = bindings::LibaflExitEndStatus_LIBAFL_EXIT_END_OK.0 as u64, // Normal exit
|
||||||
|
Crash = bindings::LibaflExitEndStatus_LIBAFL_EXIT_END_CRASH.0 as u64, // Crash reported in the VM
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IsCommand<QT, S, E>
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
E: EmuExitHandler<QT, S>,
|
||||||
|
{
|
||||||
|
/// Used to know whether the command can be run during a backdoor, or if it is necessary to go out of
|
||||||
|
/// the QEMU VM to run the command.
|
||||||
|
fn usable_at_runtime(&self) -> bool;
|
||||||
|
|
||||||
|
/// Command handler.
|
||||||
|
/// - `input`: The input for the current emulator run.
|
||||||
|
/// - `ret_reg`: The register in which the guest return value should be written, if any.
|
||||||
|
/// Returns
|
||||||
|
/// - `InnerHandlerResult`: How the high-level handler should behave
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, E>,
|
||||||
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
input: &BytesInput,
|
||||||
|
ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub type PagingFilterCommand = FilterCommand<QemuInstrumentationPagingFilter>;
|
||||||
|
|
||||||
|
pub type AddressRangeFilterCommand = FilterCommand<QemuInstrumentationAddressRangeFilter>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Command {
|
||||||
|
SaveCommand(SaveCommand),
|
||||||
|
LoadCommand(LoadCommand),
|
||||||
|
InputCommand(InputCommand),
|
||||||
|
StartCommand(StartCommand),
|
||||||
|
EndCommand(EndCommand),
|
||||||
|
VersionCommand(VersionCommand),
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
PagingFilterCommand(PagingFilterCommand),
|
||||||
|
AddressRangeFilterCommand(AddressRangeFilterCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with enum_dispatch implementation
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for Command
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Command::SaveCommand(cmd) => {
|
||||||
|
<SaveCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||||
|
}
|
||||||
|
Command::LoadCommand(cmd) => {
|
||||||
|
<LoadCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||||
|
}
|
||||||
|
Command::InputCommand(cmd) => {
|
||||||
|
<InputCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||||
|
}
|
||||||
|
Command::StartCommand(cmd) => {
|
||||||
|
<StartCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||||
|
}
|
||||||
|
Command::EndCommand(cmd) => {
|
||||||
|
<EndCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||||
|
}
|
||||||
|
Command::VersionCommand(cmd) => {
|
||||||
|
<VersionCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(cmd)
|
||||||
|
}
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
Command::PagingFilterCommand(cmd) => {
|
||||||
|
<PagingFilterCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::usable_at_runtime(
|
||||||
|
cmd,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Command::AddressRangeFilterCommand(cmd) => <AddressRangeFilterCommand as IsCommand<
|
||||||
|
QT,
|
||||||
|
S,
|
||||||
|
StdEmuExitHandler<SM>,
|
||||||
|
>>::usable_at_runtime(cmd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
input: &BytesInput,
|
||||||
|
ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
match self {
|
||||||
|
Command::SaveCommand(cmd) => {
|
||||||
|
<SaveCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||||
|
cmd,
|
||||||
|
emu,
|
||||||
|
qemu_executor_state,
|
||||||
|
input,
|
||||||
|
ret_reg,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Command::LoadCommand(cmd) => {
|
||||||
|
<LoadCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||||
|
cmd,
|
||||||
|
emu,
|
||||||
|
qemu_executor_state,
|
||||||
|
input,
|
||||||
|
ret_reg,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Command::InputCommand(cmd) => <InputCommand as IsCommand<
|
||||||
|
QT,
|
||||||
|
S,
|
||||||
|
StdEmuExitHandler<SM>,
|
||||||
|
>>::run(
|
||||||
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
|
),
|
||||||
|
Command::StartCommand(cmd) => <StartCommand as IsCommand<
|
||||||
|
QT,
|
||||||
|
S,
|
||||||
|
StdEmuExitHandler<SM>,
|
||||||
|
>>::run(
|
||||||
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
|
),
|
||||||
|
Command::EndCommand(cmd) => {
|
||||||
|
<EndCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||||
|
cmd,
|
||||||
|
emu,
|
||||||
|
qemu_executor_state,
|
||||||
|
input,
|
||||||
|
ret_reg,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Command::VersionCommand(cmd) => <VersionCommand as IsCommand<
|
||||||
|
QT,
|
||||||
|
S,
|
||||||
|
StdEmuExitHandler<SM>,
|
||||||
|
>>::run(
|
||||||
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
|
),
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
Command::PagingFilterCommand(cmd) => <PagingFilterCommand as IsCommand<
|
||||||
|
QT,
|
||||||
|
S,
|
||||||
|
StdEmuExitHandler<SM>,
|
||||||
|
>>::run(
|
||||||
|
cmd, emu, qemu_executor_state, input, ret_reg
|
||||||
|
),
|
||||||
|
Command::AddressRangeFilterCommand(cmd) => {
|
||||||
|
<AddressRangeFilterCommand as IsCommand<QT, S, StdEmuExitHandler<SM>>>::run(
|
||||||
|
cmd,
|
||||||
|
emu,
|
||||||
|
qemu_executor_state,
|
||||||
|
input,
|
||||||
|
ret_reg,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EmulatorMemoryChunk {
|
||||||
|
addr: GuestAddrKind,
|
||||||
|
size: GuestReg,
|
||||||
|
cpu: Option<CPU>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SaveCommand;
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for SaveCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
#[cfg(emulation_mode = "systemmode")] qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
#[cfg(not(emulation_mode = "systemmode"))] _qemu_executor_state: &mut QemuExecutorState<
|
||||||
|
QT,
|
||||||
|
S,
|
||||||
|
>,
|
||||||
|
_input: &BytesInput,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
|
||||||
|
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||||
|
emu_exit_handler
|
||||||
|
.set_snapshot_id(snapshot_id)
|
||||||
|
.map_err(|_| HandlerError::MultipleSnapshotDefinition)?;
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
{
|
||||||
|
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||||
|
|
||||||
|
let mut allowed_paging_ids = HashSet::new();
|
||||||
|
|
||||||
|
let current_paging_id = qemu.current_cpu().unwrap().current_paging_id().unwrap();
|
||||||
|
allowed_paging_ids.insert(current_paging_id);
|
||||||
|
|
||||||
|
let paging_filter =
|
||||||
|
HasInstrumentationFilter::<QemuInstrumentationPagingFilter, S>::filter_mut(
|
||||||
|
qemu_helpers,
|
||||||
|
);
|
||||||
|
|
||||||
|
*paging_filter = QemuInstrumentationPagingFilter::AllowList(allowed_paging_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LoadCommand;
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for LoadCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
_input: &BytesInput,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
|
||||||
|
let snapshot_id = emu_exit_handler
|
||||||
|
.snapshot_id()
|
||||||
|
.ok_or(HandlerError::SnapshotNotFound)?;
|
||||||
|
|
||||||
|
emu_exit_handler
|
||||||
|
.snapshot_manager_borrow_mut()
|
||||||
|
.restore(&snapshot_id, qemu)?;
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InputCommand {
|
||||||
|
location: EmulatorMemoryChunk,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for InputCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
input: &BytesInput,
|
||||||
|
ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
|
||||||
|
let ret_value = self.location.write(qemu, input.bytes());
|
||||||
|
|
||||||
|
if let Some(reg) = ret_reg {
|
||||||
|
qemu.write_reg(reg, ret_value).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StartCommand {
|
||||||
|
input_location: EmulatorMemoryChunk,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for StartCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
input: &BytesInput,
|
||||||
|
ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
let qemu = emu.qemu();
|
||||||
|
let snapshot_id = emu_exit_handler.snapshot_manager_borrow_mut().save(qemu);
|
||||||
|
|
||||||
|
emu_exit_handler
|
||||||
|
.set_snapshot_id(snapshot_id)
|
||||||
|
.map_err(|_| HandlerError::MultipleSnapshotDefinition)?;
|
||||||
|
|
||||||
|
emu_exit_handler
|
||||||
|
.set_input_location(self.input_location.clone(), ret_reg)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let ret_value = self.input_location.write(qemu, input.bytes());
|
||||||
|
|
||||||
|
if let Some(reg) = ret_reg {
|
||||||
|
qemu.write_reg(reg, ret_value).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EndCommand(Option<ExitKind>);
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for EndCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
_input: &BytesInput,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let emu_exit_handler = emu.exit_handler().borrow_mut();
|
||||||
|
|
||||||
|
let snapshot_id = emu_exit_handler
|
||||||
|
.snapshot_id()
|
||||||
|
.ok_or(HandlerError::SnapshotNotFound)?;
|
||||||
|
|
||||||
|
emu_exit_handler
|
||||||
|
.snapshot_manager_borrow_mut()
|
||||||
|
.restore(&snapshot_id, emu.qemu())?;
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::EndOfRun(self.0.unwrap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct VersionCommand(u64);
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for VersionCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
_qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
_input: &BytesInput,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let guest_version = self.0;
|
||||||
|
|
||||||
|
if VERSION == guest_version {
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
} else {
|
||||||
|
Err(HandlerError::SyncBackdoorError(
|
||||||
|
SyncBackdoorError::VersionDifference(guest_version),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FilterCommand<T>
|
||||||
|
where
|
||||||
|
T: IsFilter + Debug,
|
||||||
|
{
|
||||||
|
filter: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for PagingFilterCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
_input: &BytesInput,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||||
|
|
||||||
|
let paging_filter =
|
||||||
|
HasInstrumentationFilter::<QemuInstrumentationPagingFilter, S>::filter_mut(
|
||||||
|
qemu_helpers,
|
||||||
|
);
|
||||||
|
|
||||||
|
*paging_filter = self.filter.clone();
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SM, QT, S> IsCommand<QT, S, StdEmuExitHandler<SM>> for AddressRangeFilterCommand
|
||||||
|
where
|
||||||
|
SM: IsSnapshotManager,
|
||||||
|
QT: QemuHelperTuple<S> + StdInstrumentationFilter<S> + Debug,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
|
fn usable_at_runtime(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)] // TODO: refactor with correct type.
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_emu: &Emulator<QT, S, StdEmuExitHandler<SM>>,
|
||||||
|
qemu_executor_state: &mut QemuExecutorState<QT, S>,
|
||||||
|
_input: &BytesInput,
|
||||||
|
_ret_reg: Option<Regs>,
|
||||||
|
) -> Result<InnerHandlerResult, HandlerError> {
|
||||||
|
let qemu_helpers = qemu_executor_state.hooks_mut().helpers_mut();
|
||||||
|
|
||||||
|
let addr_range_filter =
|
||||||
|
HasInstrumentationFilter::<QemuInstrumentationAddressRangeFilter, S>::filter_mut(
|
||||||
|
qemu_helpers,
|
||||||
|
);
|
||||||
|
|
||||||
|
*addr_range_filter = self.filter.clone();
|
||||||
|
|
||||||
|
Ok(InnerHandlerResult::Continue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VersionCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(version: u64) -> Self {
|
||||||
|
Self(version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FilterCommand<T>
|
||||||
|
where
|
||||||
|
T: IsFilter + Debug,
|
||||||
|
{
|
||||||
|
pub fn new(filter: T) -> Self {
|
||||||
|
Self { filter }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: rewrite with display implementation for each command.
|
||||||
|
impl Display for Command {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Command::SaveCommand(_) => write!(f, "Save VM"),
|
||||||
|
Command::LoadCommand(_) => write!(f, "Reload VM"),
|
||||||
|
Command::InputCommand(input_command) => {
|
||||||
|
write!(f, "Set fuzzing input @{}", input_command.location.addr)
|
||||||
|
}
|
||||||
|
Command::StartCommand(start_command) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Start fuzzing with input @{}",
|
||||||
|
start_command.input_location.addr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Command::EndCommand(end_command) => write!(f, "Exit of kind {:?}", end_command.0),
|
||||||
|
Command::VersionCommand(version_command) => {
|
||||||
|
write!(f, "Client version: {}", version_command.0)
|
||||||
|
}
|
||||||
|
Command::AddressRangeFilterCommand(addr_range_filter) => {
|
||||||
|
write!(f, "Addr range filter: {:?}", addr_range_filter.filter,)
|
||||||
|
}
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
Command::PagingFilterCommand(paging_filter) => {
|
||||||
|
write!(f, "Addr range filter: {:?}", paging_filter.filter,)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(input_location: EmulatorMemoryChunk) -> Self {
|
||||||
|
Self { input_location }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(exit_kind: Option<ExitKind>) -> Self {
|
||||||
|
Self(exit_kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputCommand {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(location: EmulatorMemoryChunk) -> Self {
|
||||||
|
Self { location }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmulatorMemoryChunk {
|
||||||
|
#[must_use]
|
||||||
|
pub fn phys(addr: GuestPhysAddr, size: GuestReg, cpu: Option<CPU>) -> Self {
|
||||||
|
Self {
|
||||||
|
addr: GuestAddrKind::Physical(addr),
|
||||||
|
size,
|
||||||
|
cpu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn virt(addr: GuestVirtAddr, size: GuestReg, cpu: CPU) -> Self {
|
||||||
|
Self {
|
||||||
|
addr: GuestAddrKind::Virtual(addr),
|
||||||
|
size,
|
||||||
|
cpu: Some(cpu),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of bytes effectively written.
|
||||||
|
#[must_use]
|
||||||
|
pub fn write(&self, qemu: &Qemu, input: &[u8]) -> GuestReg {
|
||||||
|
let max_len: usize = self.size.try_into().unwrap();
|
||||||
|
|
||||||
|
let input_sliced = if input.len() > max_len {
|
||||||
|
&input[0..max_len]
|
||||||
|
} else {
|
||||||
|
input
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.addr {
|
||||||
|
GuestAddrKind::Physical(hwaddr) => unsafe {
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
{
|
||||||
|
// For now the default behaviour is to fall back to virtual addresses
|
||||||
|
qemu.write_mem(hwaddr.try_into().unwrap(), input_sliced);
|
||||||
|
}
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
{
|
||||||
|
qemu.write_phys_mem(hwaddr, input_sliced);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GuestAddrKind::Virtual(vaddr) => unsafe {
|
||||||
|
self.cpu
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.write_mem(vaddr.try_into().unwrap(), input_sliced);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
input_sliced.len().try_into().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InputCommand {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} (0x{:x} max nb bytes)",
|
||||||
|
self.location.addr, self.location.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -4,18 +4,18 @@ use hashbrown::{hash_map::Entry, HashMap};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, state::HasMetadata,
|
executors::ExitKind, inputs::UsesInput, observers::ObserversTuple, state::HasMetadata,
|
||||||
};
|
};
|
||||||
|
use libafl_qemu_sys::{GuestAddr, GuestUsize};
|
||||||
use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
|
use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{GuestAddr, GuestUsize},
|
|
||||||
helper::{
|
helper::{
|
||||||
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
},
|
},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
Emulator,
|
Qemu,
|
||||||
};
|
};
|
||||||
|
|
||||||
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
|
static DRCOV_IDS: Mutex<Option<Vec<u64>>> = Mutex::new(None);
|
||||||
@ -78,7 +78,11 @@ impl QemuDrCovHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuDrCovHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
for QemuDrCovHelper
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
&self.filter
|
&self.filter
|
||||||
}
|
}
|
||||||
@ -103,11 +107,11 @@ where
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {}
|
fn pre_exec(&mut self, _qemu: Qemu, _input: &S::Input) {}
|
||||||
|
|
||||||
fn post_exec<OT>(
|
fn post_exec<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -200,8 +204,7 @@ pub fn gen_unique_block_ids<QT, S>(
|
|||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
) -> Option<u64>
|
) -> Option<u64>
|
||||||
where
|
where
|
||||||
S: HasMetadata,
|
S: UsesInput + HasMetadata,
|
||||||
S: UsesInput,
|
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let drcov_helper = hooks
|
let drcov_helper = hooks
|
||||||
@ -255,8 +258,7 @@ pub fn gen_block_lengths<QT, S>(
|
|||||||
pc: GuestAddr,
|
pc: GuestAddr,
|
||||||
block_length: GuestUsize,
|
block_length: GuestUsize,
|
||||||
) where
|
) where
|
||||||
S: HasMetadata,
|
S: UsesInput + HasMetadata,
|
||||||
S: UsesInput,
|
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let drcov_helper = hooks
|
let drcov_helper = hooks
|
||||||
@ -276,9 +278,8 @@ pub fn gen_block_lengths<QT, S>(
|
|||||||
|
|
||||||
pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<QT, S>, _state: Option<&mut S>, id: u64)
|
pub fn exec_trace_block<QT, S>(hooks: &mut QemuHooks<QT, S>, _state: Option<&mut S>, id: u64)
|
||||||
where
|
where
|
||||||
S: HasMetadata,
|
|
||||||
S: UsesInput,
|
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
{
|
{
|
||||||
if hooks
|
if hooks
|
||||||
.helpers()
|
.helpers()
|
||||||
|
@ -2,14 +2,18 @@ use std::{cell::UnsafeCell, cmp::max};
|
|||||||
|
|
||||||
use hashbrown::{hash_map::Entry, HashMap};
|
use hashbrown::{hash_map::Entry, HashMap};
|
||||||
use libafl::{inputs::UsesInput, state::HasMetadata};
|
use libafl::{inputs::UsesInput, state::HasMetadata};
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
use libafl_qemu_sys::GuestPhysAddr;
|
||||||
pub use libafl_targets::{
|
pub use libafl_targets::{
|
||||||
edges_map_mut_ptr, edges_map_mut_slice, edges_max_num, std_edges_map_observer, EDGES_MAP,
|
edges_map_mut_ptr, edges_map_mut_slice, edges_max_num, std_edges_map_observer, EDGES_MAP,
|
||||||
EDGES_MAP_PTR, EDGES_MAP_PTR_NUM, EDGES_MAP_SIZE, MAX_EDGES_NUM,
|
EDGES_MAP_PTR, EDGES_MAP_PTR_NUM, EDGES_MAP_SIZE, MAX_EDGES_NUM,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
use crate::helper::QemuInstrumentationPagingFilter;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::GuestAddr,
|
|
||||||
helper::{
|
helper::{
|
||||||
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple,
|
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple,
|
||||||
QemuInstrumentationAddressRangeFilter,
|
QemuInstrumentationAddressRangeFilter,
|
||||||
@ -17,8 +21,6 @@ use crate::{
|
|||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
IsFilter,
|
IsFilter,
|
||||||
};
|
};
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
|
||||||
use crate::{helper::QemuInstrumentationPagingFilter, GuestPhysAddr};
|
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
any(not(feature = "serdeany_autoreg"), miri),
|
any(not(feature = "serdeany_autoreg"), miri),
|
||||||
@ -130,7 +132,9 @@ impl Default for QemuEdgeCoverageHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuEdgeCoverageHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
for QemuEdgeCoverageHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
&self.address_filter
|
&self.address_filter
|
||||||
}
|
}
|
||||||
@ -141,7 +145,9 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuEdg
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCoverageHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationPagingFilter, S>
|
||||||
|
for QemuEdgeCoverageHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
||||||
&self.paging_filter
|
&self.paging_filter
|
||||||
}
|
}
|
||||||
@ -277,7 +283,7 @@ impl Default for QemuEdgeCoverageChildHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
for QemuEdgeCoverageChildHelper
|
for QemuEdgeCoverageChildHelper
|
||||||
{
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
@ -290,7 +296,9 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCoverageChildHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationPagingFilter, S>
|
||||||
|
for QemuEdgeCoverageChildHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
||||||
&self.paging_filter
|
&self.paging_filter
|
||||||
}
|
}
|
||||||
@ -302,8 +310,7 @@ impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCover
|
|||||||
|
|
||||||
impl<S> QemuHelper<S> for QemuEdgeCoverageChildHelper
|
impl<S> QemuHelper<S> for QemuEdgeCoverageChildHelper
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput + HasMetadata,
|
||||||
S: HasMetadata,
|
|
||||||
{
|
{
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
@ -330,6 +337,7 @@ where
|
|||||||
pub struct QemuEdgeCoverageClassicHelper {
|
pub struct QemuEdgeCoverageClassicHelper {
|
||||||
address_filter: QemuInstrumentationAddressRangeFilter,
|
address_filter: QemuInstrumentationAddressRangeFilter,
|
||||||
use_hitcounts: bool,
|
use_hitcounts: bool,
|
||||||
|
use_jit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -338,23 +346,29 @@ pub struct QemuEdgeCoverageClassicHelper {
|
|||||||
address_filter: QemuInstrumentationAddressRangeFilter,
|
address_filter: QemuInstrumentationAddressRangeFilter,
|
||||||
paging_filter: QemuInstrumentationPagingFilter,
|
paging_filter: QemuInstrumentationPagingFilter,
|
||||||
use_hitcounts: bool,
|
use_hitcounts: bool,
|
||||||
|
use_jit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
impl QemuEdgeCoverageClassicHelper {
|
impl QemuEdgeCoverageClassicHelper {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
|
pub fn new(address_filter: QemuInstrumentationAddressRangeFilter, use_jit: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
address_filter,
|
address_filter,
|
||||||
use_hitcounts: true,
|
use_hitcounts: true,
|
||||||
|
use_jit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
|
pub fn without_hitcounts(
|
||||||
|
address_filter: QemuInstrumentationAddressRangeFilter,
|
||||||
|
use_jit: bool,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
address_filter,
|
address_filter,
|
||||||
use_hitcounts: false,
|
use_hitcounts: false,
|
||||||
|
use_jit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,11 +384,13 @@ impl QemuEdgeCoverageClassicHelper {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
address_filter: QemuInstrumentationAddressRangeFilter,
|
address_filter: QemuInstrumentationAddressRangeFilter,
|
||||||
paging_filter: QemuInstrumentationPagingFilter,
|
paging_filter: QemuInstrumentationPagingFilter,
|
||||||
|
use_jit: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
address_filter,
|
address_filter,
|
||||||
paging_filter,
|
paging_filter,
|
||||||
use_hitcounts: true,
|
use_hitcounts: true,
|
||||||
|
use_jit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,11 +398,13 @@ impl QemuEdgeCoverageClassicHelper {
|
|||||||
pub fn without_hitcounts(
|
pub fn without_hitcounts(
|
||||||
address_filter: QemuInstrumentationAddressRangeFilter,
|
address_filter: QemuInstrumentationAddressRangeFilter,
|
||||||
paging_filter: QemuInstrumentationPagingFilter,
|
paging_filter: QemuInstrumentationPagingFilter,
|
||||||
|
use_jit: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
address_filter,
|
address_filter,
|
||||||
paging_filter,
|
paging_filter,
|
||||||
use_hitcounts: false,
|
use_hitcounts: false,
|
||||||
|
use_jit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +417,7 @@ impl QemuEdgeCoverageClassicHelper {
|
|||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
impl Default for QemuEdgeCoverageClassicHelper {
|
impl Default for QemuEdgeCoverageClassicHelper {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(QemuInstrumentationAddressRangeFilter::None)
|
Self::new(QemuInstrumentationAddressRangeFilter::None, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,11 +427,12 @@ impl Default for QemuEdgeCoverageClassicHelper {
|
|||||||
Self::new(
|
Self::new(
|
||||||
QemuInstrumentationAddressRangeFilter::None,
|
QemuInstrumentationAddressRangeFilter::None,
|
||||||
QemuInstrumentationPagingFilter::None,
|
QemuInstrumentationPagingFilter::None,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
for QemuEdgeCoverageClassicHelper
|
for QemuEdgeCoverageClassicHelper
|
||||||
{
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
|
||||||
@ -426,7 +445,9 @@ impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCoverageClassicHelper {
|
impl<S: UsesInput> HasInstrumentationFilter<QemuInstrumentationPagingFilter, S>
|
||||||
|
for QemuEdgeCoverageClassicHelper
|
||||||
|
{
|
||||||
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
fn filter(&self) -> &QemuInstrumentationPagingFilter {
|
||||||
&self.paging_filter
|
&self.paging_filter
|
||||||
}
|
}
|
||||||
@ -436,10 +457,10 @@ impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> for QemuEdgeCover
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::collapsible_else_if)]
|
||||||
impl<S> QemuHelper<S> for QemuEdgeCoverageClassicHelper
|
impl<S> QemuHelper<S> for QemuEdgeCoverageClassicHelper
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput + HasMetadata,
|
||||||
S: HasMetadata,
|
|
||||||
{
|
{
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
const HOOKS_DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
@ -448,11 +469,40 @@ where
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if self.use_hitcounts {
|
if self.use_hitcounts {
|
||||||
|
if self.use_jit {
|
||||||
|
let hook_id = hooks.blocks(
|
||||||
|
Hook::Function(gen_hashed_block_ids::<QT, S>),
|
||||||
|
Hook::Empty,
|
||||||
|
Hook::Empty,
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libafl_qemu_sys::libafl_qemu_block_hook_set_jit(
|
||||||
|
hook_id.0,
|
||||||
|
Some(libafl_qemu_sys::libafl_jit_trace_block_hitcount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
hooks.blocks(
|
hooks.blocks(
|
||||||
Hook::Function(gen_hashed_block_ids::<QT, S>),
|
Hook::Function(gen_hashed_block_ids::<QT, S>),
|
||||||
Hook::Empty,
|
Hook::Empty,
|
||||||
Hook::Raw(trace_block_transition_hitcount),
|
Hook::Raw(trace_block_transition_hitcount),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if self.use_jit {
|
||||||
|
let hook_id = hooks.blocks(
|
||||||
|
Hook::Function(gen_hashed_block_ids::<QT, S>),
|
||||||
|
Hook::Empty,
|
||||||
|
Hook::Empty,
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
libafl_qemu_sys::libafl_qemu_block_hook_set_jit(
|
||||||
|
hook_id.0,
|
||||||
|
Some(libafl_qemu_sys::libafl_jit_trace_block_single),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
hooks.blocks(
|
hooks.blocks(
|
||||||
Hook::Function(gen_hashed_block_ids::<QT, S>),
|
Hook::Function(gen_hashed_block_ids::<QT, S>),
|
||||||
@ -462,6 +512,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
thread_local!(static PREV_LOC : UnsafeCell<u64> = const { UnsafeCell::new(0) });
|
thread_local!(static PREV_LOC : UnsafeCell<u64> = const { UnsafeCell::new(0) });
|
||||||
|
|
||||||
@ -472,8 +523,7 @@ pub fn gen_unique_edge_ids<QT, S>(
|
|||||||
dest: GuestAddr,
|
dest: GuestAddr,
|
||||||
) -> Option<u64>
|
) -> Option<u64>
|
||||||
where
|
where
|
||||||
S: HasMetadata,
|
S: UsesInput + HasMetadata,
|
||||||
S: UsesInput,
|
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
|
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
|
||||||
@ -487,9 +537,9 @@ where
|
|||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
let paging_id = hooks
|
let paging_id = hooks
|
||||||
.emulator()
|
.qemu()
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.map(|cpu| cpu.get_current_paging_id())
|
.map(|cpu| cpu.current_paging_id())
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
||||||
@ -557,9 +607,9 @@ where
|
|||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
let paging_id = hooks
|
let paging_id = hooks
|
||||||
.emulator()
|
.qemu()
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.map(|cpu| cpu.get_current_paging_id())
|
.map(|cpu| cpu.current_paging_id())
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) {
|
||||||
@ -624,9 +674,9 @@ where
|
|||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
let paging_id = hooks
|
let paging_id = hooks
|
||||||
.emulator()
|
.qemu()
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.map(|cpu| cpu.get_current_paging_id())
|
.map(|cpu| cpu.current_paging_id())
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
if !h.must_instrument(pc, paging_id) {
|
if !h.must_instrument(pc, paging_id) {
|
||||||
|
@ -4,8 +4,7 @@ use std::{fs::File, io::Read, ops::Range, path::Path, str};
|
|||||||
|
|
||||||
use goblin::elf::{header::ET_DYN, Elf};
|
use goblin::elf::{header::ET_DYN, Elf};
|
||||||
use libafl::Error;
|
use libafl::Error;
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
use crate::GuestAddr;
|
|
||||||
|
|
||||||
pub struct EasyElf<'a> {
|
pub struct EasyElf<'a> {
|
||||||
elf: Elf<'a>,
|
elf: Elf<'a>,
|
||||||
|
File diff suppressed because it is too large
Load Diff
451
libafl_qemu/src/emu/systemmode.rs
Normal file
451
libafl_qemu/src/emu/systemmode.rs
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ffi::{c_void, CStr, CString},
|
||||||
|
fmt::Debug,
|
||||||
|
mem::MaybeUninit,
|
||||||
|
ptr::null_mut,
|
||||||
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use libafl::state::{HasExecutions, State};
|
||||||
|
use libafl_qemu_sys::{
|
||||||
|
libafl_load_qemu_snapshot, libafl_qemu_current_paging_id, libafl_save_qemu_snapshot,
|
||||||
|
qemu_cleanup, qemu_main_loop, vm_start, GuestAddr, GuestPhysAddr, GuestVirtAddr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
emu::{libafl_page_from_addr, IsSnapshotManager},
|
||||||
|
EmuExitHandler, Emulator, MemAccessInfo, Qemu, QemuExitReason, QemuExitReasonError,
|
||||||
|
QemuHelperTuple, SnapshotId, SnapshotManagerError, CPU,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl SnapshotId {
|
||||||
|
fn gen_unique_id() -> SnapshotId {
|
||||||
|
static UNIQUE_ID: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
let unique_id = UNIQUE_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
||||||
|
SnapshotId {
|
||||||
|
id: unique_id.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner(&self) -> u64 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SnapshotManager {
|
||||||
|
Qemu(QemuSnapshotManager),
|
||||||
|
Fast(FastSnapshotManager),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsSnapshotManager for SnapshotManager {
|
||||||
|
fn save(&mut self, qemu: &Qemu) -> SnapshotId {
|
||||||
|
match self {
|
||||||
|
SnapshotManager::Qemu(qemu_sm) => qemu_sm.save(qemu),
|
||||||
|
SnapshotManager::Fast(fast_sm) => fast_sm.save(qemu),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(
|
||||||
|
&mut self,
|
||||||
|
snapshot_id: &SnapshotId,
|
||||||
|
qemu: &Qemu,
|
||||||
|
) -> Result<(), SnapshotManagerError> {
|
||||||
|
match self {
|
||||||
|
SnapshotManager::Qemu(qemu_sm) => qemu_sm.restore(snapshot_id, qemu),
|
||||||
|
SnapshotManager::Fast(fast_sm) => fast_sm.restore(snapshot_id, qemu),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type FastSnapshotPtr = *mut libafl_qemu_sys::SyxSnapshot;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FastSnapshotManager {
|
||||||
|
snapshots: HashMap<SnapshotId, FastSnapshotPtr>,
|
||||||
|
check_memory_consistency: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FastSnapshotManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FastSnapshotManager {
|
||||||
|
pub fn new(check_memory_consistency: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
snapshots: HashMap::new(),
|
||||||
|
check_memory_consistency,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get(&self, id: &SnapshotId) -> FastSnapshotPtr {
|
||||||
|
self.snapshots.get(id).unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct QemuSnapshotManager {
|
||||||
|
is_sync: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuSnapshotManager {
|
||||||
|
pub fn new(is_sync: bool) -> Self {
|
||||||
|
Self { is_sync }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snapshot_id_to_name(&self, snapshot_id: &SnapshotId) -> String {
|
||||||
|
format!("__libafl_qemu_snapshot_{}", snapshot_id.inner())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsSnapshotManager for QemuSnapshotManager {
|
||||||
|
fn save(&mut self, qemu: &Qemu) -> SnapshotId {
|
||||||
|
let snapshot_id = SnapshotId::gen_unique_id();
|
||||||
|
qemu.save_snapshot(
|
||||||
|
self.snapshot_id_to_name(&snapshot_id).as_str(),
|
||||||
|
self.is_sync,
|
||||||
|
);
|
||||||
|
snapshot_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(
|
||||||
|
&mut self,
|
||||||
|
snapshot_id: &SnapshotId,
|
||||||
|
qemu: &Qemu,
|
||||||
|
) -> Result<(), SnapshotManagerError> {
|
||||||
|
qemu.load_snapshot(self.snapshot_id_to_name(snapshot_id).as_str(), self.is_sync);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsSnapshotManager for FastSnapshotManager {
|
||||||
|
fn save(&mut self, qemu: &Qemu) -> SnapshotId {
|
||||||
|
let snapshot_id = SnapshotId::gen_unique_id();
|
||||||
|
self.snapshots
|
||||||
|
.insert(snapshot_id, qemu.create_fast_snapshot(true));
|
||||||
|
snapshot_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restore(
|
||||||
|
&mut self,
|
||||||
|
snapshot_id: &SnapshotId,
|
||||||
|
qemu: &Qemu,
|
||||||
|
) -> Result<(), SnapshotManagerError> {
|
||||||
|
let fast_snapshot_ptr = self
|
||||||
|
.snapshots
|
||||||
|
.get(snapshot_id)
|
||||||
|
.ok_or(SnapshotManagerError::SnapshotIdNotFound(
|
||||||
|
snapshot_id.clone(),
|
||||||
|
))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
qemu.restore_fast_snapshot(fast_snapshot_ptr);
|
||||||
|
|
||||||
|
if self.check_memory_consistency {
|
||||||
|
let nb_inconsistencies = qemu.check_fast_snapshot_memory_consistency(fast_snapshot_ptr);
|
||||||
|
|
||||||
|
if nb_inconsistencies > 0 {
|
||||||
|
return Err(SnapshotManagerError::MemoryInconsistencies(
|
||||||
|
nb_inconsistencies,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DeviceSnapshotFilter {
|
||||||
|
All,
|
||||||
|
AllowList(Vec<String>),
|
||||||
|
DenyList(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceSnapshotFilter {
|
||||||
|
fn enum_id(&self) -> libafl_qemu_sys::DeviceSnapshotKind {
|
||||||
|
match self {
|
||||||
|
DeviceSnapshotFilter::All => libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||||
|
DeviceSnapshotFilter::AllowList(_) => {
|
||||||
|
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALLOWLIST
|
||||||
|
}
|
||||||
|
DeviceSnapshotFilter::DenyList(_) => {
|
||||||
|
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_DENYLIST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn devices(&self, v: &mut Vec<*mut i8>) -> *mut *mut i8 {
|
||||||
|
v.clear();
|
||||||
|
match self {
|
||||||
|
DeviceSnapshotFilter::All => null_mut(),
|
||||||
|
DeviceSnapshotFilter::AllowList(l) | DeviceSnapshotFilter::DenyList(l) => {
|
||||||
|
for name in l {
|
||||||
|
v.push(name.as_bytes().as_ptr() as *mut i8);
|
||||||
|
}
|
||||||
|
v.as_mut_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) extern "C" fn qemu_cleanup_atexit() {
|
||||||
|
unsafe {
|
||||||
|
qemu_cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CPU {
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_phys_addr(&self, vaddr: GuestAddr) -> Option<GuestPhysAddr> {
|
||||||
|
unsafe {
|
||||||
|
let page = libafl_page_from_addr(vaddr);
|
||||||
|
let mut attrs = MaybeUninit::<libafl_qemu_sys::MemTxAttrs>::uninit();
|
||||||
|
let paddr = libafl_qemu_sys::cpu_get_phys_page_attrs_debug(
|
||||||
|
self.ptr,
|
||||||
|
page as GuestVirtAddr,
|
||||||
|
attrs.as_mut_ptr(),
|
||||||
|
);
|
||||||
|
if paddr == (-1i64 as GuestPhysAddr) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(paddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_phys_addr_tlb(
|
||||||
|
&self,
|
||||||
|
vaddr: GuestAddr,
|
||||||
|
info: MemAccessInfo,
|
||||||
|
is_store: bool,
|
||||||
|
) -> Option<GuestPhysAddr> {
|
||||||
|
unsafe {
|
||||||
|
let pminfo = libafl_qemu_sys::make_plugin_meminfo(
|
||||||
|
info.oi,
|
||||||
|
if is_store {
|
||||||
|
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_W
|
||||||
|
} else {
|
||||||
|
libafl_qemu_sys::qemu_plugin_mem_rw_QEMU_PLUGIN_MEM_R
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let phwaddr = libafl_qemu_sys::qemu_plugin_get_hwaddr(pminfo, vaddr as GuestVirtAddr);
|
||||||
|
if phwaddr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(libafl_qemu_sys::qemu_plugin_hwaddr_phys_addr(phwaddr) as GuestPhysAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn current_paging_id(&self) -> Option<GuestPhysAddr> {
|
||||||
|
let paging_id = unsafe { libafl_qemu_current_paging_id(self.ptr) };
|
||||||
|
|
||||||
|
if paging_id == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(paging_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a value to a guest address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This will write to a translated guest address (using `g2h`).
|
||||||
|
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||||
|
/// This may only be safely used for valid guest addresses!
|
||||||
|
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||||
|
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||||
|
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||||
|
self.ptr,
|
||||||
|
addr as GuestVirtAddr,
|
||||||
|
buf.as_ptr() as *mut _,
|
||||||
|
buf.len(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a value from a guest address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This will read from a translated guest address (using `g2h`).
|
||||||
|
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||||
|
/// This may only be safely used for valid guest addresses!
|
||||||
|
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||||
|
// TODO use gdbstub's target_cpu_memory_rw_debug
|
||||||
|
libafl_qemu_sys::cpu_memory_rw_debug(
|
||||||
|
self.ptr,
|
||||||
|
addr as GuestVirtAddr,
|
||||||
|
buf.as_mut_ptr() as *mut _,
|
||||||
|
buf.len(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn page_size(&self) -> usize {
|
||||||
|
unsafe { libafl_qemu_sys::qemu_target_page_size() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
impl Qemu {
|
||||||
|
/// Write a value to a phsical guest address, including ROM areas.
|
||||||
|
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
||||||
|
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||||
|
paddr,
|
||||||
|
buf.as_ptr() as *mut _,
|
||||||
|
buf.len() as u64,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a value from a physical guest address.
|
||||||
|
pub unsafe fn read_phys_mem(&self, paddr: GuestPhysAddr, buf: &mut [u8]) {
|
||||||
|
libafl_qemu_sys::cpu_physical_memory_rw(
|
||||||
|
paddr,
|
||||||
|
buf.as_mut_ptr() as *mut _,
|
||||||
|
buf.len() as u64,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function will run the emulator until the next breakpoint / sync exit, or until finish.
|
||||||
|
/// It is a low-level function and simply kicks QEMU.
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Should, in general, be safe to call.
|
||||||
|
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||||
|
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitReasonError> {
|
||||||
|
vm_start();
|
||||||
|
qemu_main_loop();
|
||||||
|
|
||||||
|
self.post_run()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
||||||
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
|
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_snapshot(&self, name: &str, sync: bool) {
|
||||||
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
|
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshotPtr {
|
||||||
|
unsafe {
|
||||||
|
libafl_qemu_sys::syx_snapshot_new(
|
||||||
|
track,
|
||||||
|
true,
|
||||||
|
libafl_qemu_sys::DeviceSnapshotKind_DEVICE_SNAPSHOT_ALL,
|
||||||
|
null_mut(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn create_fast_snapshot_filter(
|
||||||
|
&self,
|
||||||
|
track: bool,
|
||||||
|
device_filter: &DeviceSnapshotFilter,
|
||||||
|
) -> FastSnapshotPtr {
|
||||||
|
let mut v = vec![];
|
||||||
|
unsafe {
|
||||||
|
libafl_qemu_sys::syx_snapshot_new(
|
||||||
|
track,
|
||||||
|
true,
|
||||||
|
device_filter.enum_id(),
|
||||||
|
device_filter.devices(&mut v),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_fast_snapshot(&self, snapshot: FastSnapshotPtr) {
|
||||||
|
unsafe { libafl_qemu_sys::syx_snapshot_root_restore(snapshot) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 {
|
||||||
|
unsafe { libafl_qemu_sys::syx_snapshot_check_memory_consistency(snapshot) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_devices(&self) -> Vec<String> {
|
||||||
|
let mut r = vec![];
|
||||||
|
unsafe {
|
||||||
|
let devices = libafl_qemu_sys::device_list_all();
|
||||||
|
if devices.is_null() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ptr = devices;
|
||||||
|
while !(*ptr).is_null() {
|
||||||
|
let c_str: &CStr = CStr::from_ptr(*ptr);
|
||||||
|
let name = c_str.to_str().unwrap().to_string();
|
||||||
|
r.push(name);
|
||||||
|
|
||||||
|
ptr = ptr.add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
libc::free(devices as *mut c_void);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<QT, S, E> Emulator<QT, S, E>
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
E: EmuExitHandler<QT, S>,
|
||||||
|
{
|
||||||
|
/// Write a value to a phsical guest address, including ROM areas.
|
||||||
|
pub unsafe fn write_phys_mem(&self, paddr: GuestPhysAddr, buf: &[u8]) {
|
||||||
|
self.qemu.write_phys_mem(paddr, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a value from a physical guest address.
|
||||||
|
pub unsafe fn read_phys_mem(&self, paddr: GuestPhysAddr, buf: &mut [u8]) {
|
||||||
|
self.qemu.read_phys_mem(paddr, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
||||||
|
self.qemu.save_snapshot(name, sync)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_snapshot(&self, name: &str, sync: bool) {
|
||||||
|
self.qemu.load_snapshot(name, sync)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshotPtr {
|
||||||
|
self.qemu.create_fast_snapshot(track)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn create_fast_snapshot_filter(
|
||||||
|
&self,
|
||||||
|
track: bool,
|
||||||
|
device_filter: &DeviceSnapshotFilter,
|
||||||
|
) -> FastSnapshotPtr {
|
||||||
|
self.qemu.create_fast_snapshot_filter(track, device_filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_fast_snapshot(&self, snapshot: FastSnapshotPtr) {
|
||||||
|
self.qemu.restore_fast_snapshot(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_fast_snapshot_memory_consistency(&self, snapshot: FastSnapshotPtr) -> u64 {
|
||||||
|
self.qemu.check_fast_snapshot_memory_consistency(snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_devices(&self) -> Vec<String> {
|
||||||
|
self.qemu.list_devices()
|
||||||
|
}
|
||||||
|
}
|
491
libafl_qemu/src/emu/usermode.rs
Normal file
491
libafl_qemu/src/emu/usermode.rs
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
use core::{ffi::c_void, mem::MaybeUninit, ptr::copy_nonoverlapping};
|
||||||
|
use std::{cell::OnceCell, slice::from_raw_parts, str::from_utf8_unchecked};
|
||||||
|
|
||||||
|
use libafl_qemu_sys::{
|
||||||
|
exec_path, free_self_maps, guest_base, libafl_dump_core_hook, libafl_force_dfl, libafl_get_brk,
|
||||||
|
libafl_load_addr, libafl_maps_next, libafl_qemu_run, libafl_set_brk, mmap_next_start,
|
||||||
|
read_self_maps, strlen, GuestAddr, GuestUsize, MapInfo, MmapPerms, VerifyAccess,
|
||||||
|
};
|
||||||
|
use libc::c_int;
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
emu::{HasExecutions, State},
|
||||||
|
sync_backdoor::SyncBackdoorError,
|
||||||
|
EmuExitHandler, Emulator, HookData, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, Qemu,
|
||||||
|
QemuExitReason, QemuExitReasonError, QemuHelperTuple, SyscallHookResult, CPU,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum HandlerError {
|
||||||
|
EmuExitReasonError(QemuExitReasonError),
|
||||||
|
SyncBackdoorError(SyncBackdoorError),
|
||||||
|
MultipleInputDefinition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||||
|
pub struct GuestMaps {
|
||||||
|
orig_c_iter: *const c_void,
|
||||||
|
c_iter: *const c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consider a private new only for Emulator
|
||||||
|
impl GuestMaps {
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
unsafe {
|
||||||
|
let maps = read_self_maps();
|
||||||
|
Self {
|
||||||
|
orig_c_iter: maps,
|
||||||
|
c_iter: maps,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for GuestMaps {
|
||||||
|
type Item = MapInfo;
|
||||||
|
|
||||||
|
#[allow(clippy::uninit_assumed_init)]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.c_iter.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let mut ret = MaybeUninit::uninit();
|
||||||
|
self.c_iter = libafl_maps_next(self.c_iter, ret.as_mut_ptr());
|
||||||
|
if self.c_iter.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ret.assume_init())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
#[pymethods]
|
||||||
|
impl GuestMaps {
|
||||||
|
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
fn __next__(mut slf: PyRefMut<Self>) -> Option<PyObject> {
|
||||||
|
Python::with_gil(|py| slf.next().map(|x| x.into_py(py)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GuestMaps {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
free_self_maps(self.orig_c_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CPU {
|
||||||
|
/// Write a value to a guest address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This will write to a translated guest address (using `g2h`).
|
||||||
|
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||||
|
/// This may only be safely used for valid guest addresses!
|
||||||
|
pub unsafe fn write_mem(&self, addr: GuestAddr, buf: &[u8]) {
|
||||||
|
let host_addr = Qemu::get().unwrap().g2h(addr);
|
||||||
|
copy_nonoverlapping(buf.as_ptr(), host_addr, buf.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a value from a guest address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This will read from a translated guest address (using `g2h`).
|
||||||
|
/// It just adds `guest_base` and writes to that location, without checking the bounds.
|
||||||
|
/// This may only be safely used for valid guest addresses!
|
||||||
|
pub unsafe fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) {
|
||||||
|
let host_addr = Qemu::get().unwrap().g2h(addr);
|
||||||
|
copy_nonoverlapping(host_addr, buf.as_mut_ptr(), buf.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn page_size(&self) -> usize {
|
||||||
|
thread_local! {
|
||||||
|
static PAGE_SIZE: OnceCell<usize> = const { OnceCell::new() };
|
||||||
|
}
|
||||||
|
|
||||||
|
PAGE_SIZE.with(|s| {
|
||||||
|
*s.get_or_init(|| {
|
||||||
|
unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) }
|
||||||
|
.try_into()
|
||||||
|
.expect("Invalid page size")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
impl Qemu {
|
||||||
|
#[must_use]
|
||||||
|
pub fn mappings(&self) -> GuestMaps {
|
||||||
|
GuestMaps::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||||
|
unsafe { (addr as usize + guest_base) as *mut T }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
||||||
|
unsafe { (addr as usize - guest_base) as GuestAddr }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> bool {
|
||||||
|
self.current_cpu()
|
||||||
|
.unwrap_or_else(|| self.cpu_from_index(0))
|
||||||
|
.access_ok(kind, addr, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force_dfl(&self) {
|
||||||
|
unsafe {
|
||||||
|
libafl_force_dfl = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function will run the emulator until the next breakpoint, or until finish.
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Should, in general, be safe to call.
|
||||||
|
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||||
|
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitReasonError> {
|
||||||
|
libafl_qemu_run();
|
||||||
|
|
||||||
|
self.post_run()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn binary_path<'a>(&self) -> &'a str {
|
||||||
|
unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn load_addr(&self) -> GuestAddr {
|
||||||
|
unsafe { libafl_load_addr() as GuestAddr }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_brk(&self) -> GuestAddr {
|
||||||
|
unsafe { libafl_get_brk() as GuestAddr }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_brk(&self, brk: GuestAddr) {
|
||||||
|
unsafe { libafl_set_brk(brk.into()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_mmap_start(&self) -> GuestAddr {
|
||||||
|
unsafe { mmap_next_start }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mmap_start(&self, start: GuestAddr) {
|
||||||
|
unsafe { mmap_next_start = start };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
fn mmap(
|
||||||
|
self,
|
||||||
|
addr: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
perms: MmapPerms,
|
||||||
|
flags: c_int,
|
||||||
|
) -> Result<GuestAddr, ()> {
|
||||||
|
let res = unsafe {
|
||||||
|
libafl_qemu_sys::target_mmap(addr, size as GuestUsize, perms.into(), flags, -1, 0)
|
||||||
|
};
|
||||||
|
if res <= 0 {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(res as GuestAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_private(
|
||||||
|
&self,
|
||||||
|
addr: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
perms: MmapPerms,
|
||||||
|
) -> Result<GuestAddr, String> {
|
||||||
|
self.mmap(addr, size, perms, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS)
|
||||||
|
.map_err(|()| format!("Failed to map {addr}"))
|
||||||
|
.map(|addr| addr as GuestAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_fixed(
|
||||||
|
&self,
|
||||||
|
addr: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
perms: MmapPerms,
|
||||||
|
) -> Result<GuestAddr, String> {
|
||||||
|
self.mmap(
|
||||||
|
addr,
|
||||||
|
size,
|
||||||
|
perms,
|
||||||
|
libc::MAP_FIXED | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
|
||||||
|
)
|
||||||
|
.map_err(|()| format!("Failed to map {addr}"))
|
||||||
|
.map(|addr| addr as GuestAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
||||||
|
let res = unsafe {
|
||||||
|
libafl_qemu_sys::target_mprotect(addr.into(), size as GuestUsize, perms.into())
|
||||||
|
};
|
||||||
|
if res == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!("Failed to mprotect {addr}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
||||||
|
if unsafe { libafl_qemu_sys::target_munmap(addr.into(), size as GuestUsize) } == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!("Failed to unmap {addr}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn add_pre_syscall_hook<T: Into<HookData>>(
|
||||||
|
&self,
|
||||||
|
data: T,
|
||||||
|
callback: extern "C" fn(
|
||||||
|
T,
|
||||||
|
i32,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> SyscallHookResult,
|
||||||
|
) -> PreSyscallHookId {
|
||||||
|
unsafe {
|
||||||
|
let data: u64 = data.into().0;
|
||||||
|
let callback: extern "C" fn(
|
||||||
|
u64,
|
||||||
|
i32,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> libafl_qemu_sys::syshook_ret = core::mem::transmute(callback);
|
||||||
|
let num = libafl_qemu_sys::libafl_add_pre_syscall_hook(Some(callback), data);
|
||||||
|
PreSyscallHookId(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn add_post_syscall_hook<T: Into<HookData>>(
|
||||||
|
&self,
|
||||||
|
data: T,
|
||||||
|
callback: extern "C" fn(
|
||||||
|
T,
|
||||||
|
GuestAddr,
|
||||||
|
i32,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> GuestAddr,
|
||||||
|
) -> PostSyscallHookId {
|
||||||
|
unsafe {
|
||||||
|
let data: u64 = data.into().0;
|
||||||
|
let callback: extern "C" fn(
|
||||||
|
u64,
|
||||||
|
GuestAddr,
|
||||||
|
i32,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> GuestAddr = core::mem::transmute(callback);
|
||||||
|
let num = libafl_qemu_sys::libafl_add_post_syscall_hook(Some(callback), data);
|
||||||
|
PostSyscallHookId(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_new_thread_hook<T: Into<HookData>>(
|
||||||
|
&self,
|
||||||
|
data: T,
|
||||||
|
callback: extern "C" fn(T, tid: u32) -> bool,
|
||||||
|
) -> NewThreadHookId {
|
||||||
|
unsafe {
|
||||||
|
let data: u64 = data.into().0;
|
||||||
|
let callback: extern "C" fn(u64, u32) -> bool = core::mem::transmute(callback);
|
||||||
|
let num = libafl_qemu_sys::libafl_add_new_thread_hook(Some(callback), data);
|
||||||
|
NewThreadHookId(num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) {
|
||||||
|
unsafe {
|
||||||
|
libafl_dump_core_hook = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<QT, S, E> Emulator<QT, S, E>
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
E: EmuExitHandler<QT, S>,
|
||||||
|
{
|
||||||
|
/// This function gets the memory mappings from the emulator.
|
||||||
|
#[must_use]
|
||||||
|
pub fn mappings(&self) -> GuestMaps {
|
||||||
|
self.qemu.mappings()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn g2h<T>(&self, addr: GuestAddr) -> *mut T {
|
||||||
|
self.qemu.g2h(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn h2g<T>(&self, addr: *const T) -> GuestAddr {
|
||||||
|
self.qemu.h2g(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> bool {
|
||||||
|
self.qemu.access_ok(kind, addr, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force_dfl(&self) {
|
||||||
|
self.qemu.force_dfl();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn binary_path<'a>(&self) -> &'a str {
|
||||||
|
self.qemu.binary_path()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn load_addr(&self) -> GuestAddr {
|
||||||
|
self.qemu.load_addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_brk(&self) -> GuestAddr {
|
||||||
|
self.qemu.get_brk()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_brk(&self, brk: GuestAddr) {
|
||||||
|
self.qemu.set_brk(brk);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_mmap_start(&self) -> GuestAddr {
|
||||||
|
self.qemu.get_mmap_start()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mmap_start(&self, start: GuestAddr) {
|
||||||
|
self.qemu.set_mmap_start(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_private(
|
||||||
|
&self,
|
||||||
|
addr: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
perms: MmapPerms,
|
||||||
|
) -> Result<GuestAddr, String> {
|
||||||
|
self.qemu.map_private(addr, size, perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_fixed(
|
||||||
|
&self,
|
||||||
|
addr: GuestAddr,
|
||||||
|
size: usize,
|
||||||
|
perms: MmapPerms,
|
||||||
|
) -> Result<GuestAddr, String> {
|
||||||
|
self.qemu.map_fixed(addr, size, perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mprotect(&self, addr: GuestAddr, size: usize, perms: MmapPerms) -> Result<(), String> {
|
||||||
|
self.qemu.mprotect(addr, size, perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmap(&self, addr: GuestAddr, size: usize) -> Result<(), String> {
|
||||||
|
self.qemu.unmap(addr, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn add_pre_syscall_hook<T: Into<HookData>>(
|
||||||
|
&self,
|
||||||
|
data: T,
|
||||||
|
callback: extern "C" fn(
|
||||||
|
T,
|
||||||
|
i32,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> SyscallHookResult,
|
||||||
|
) -> PreSyscallHookId {
|
||||||
|
self.qemu.add_pre_syscall_hook(data, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn add_post_syscall_hook<T: Into<HookData>>(
|
||||||
|
&self,
|
||||||
|
data: T,
|
||||||
|
callback: extern "C" fn(
|
||||||
|
T,
|
||||||
|
GuestAddr,
|
||||||
|
i32,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> GuestAddr,
|
||||||
|
) -> PostSyscallHookId {
|
||||||
|
self.qemu.add_post_syscall_hook(data, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_new_thread_hook<T: Into<HookData>>(
|
||||||
|
&self,
|
||||||
|
data: T,
|
||||||
|
callback: extern "C" fn(T, tid: u32) -> bool,
|
||||||
|
) -> NewThreadHookId {
|
||||||
|
self.qemu.add_new_thread_hook(data, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn set_crash_hook(&self, callback: extern "C" fn(i32)) {
|
||||||
|
self.qemu.set_crash_hook(callback);
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ use libafl_bolts::os::unix_signals::{siginfo_t, ucontext_t, Signal};
|
|||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl_bolts::shmem::ShMemProvider;
|
use libafl_bolts::shmem::ShMemProvider;
|
||||||
|
|
||||||
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
|
use crate::{helper::QemuHelperTuple, hooks::QemuHooks, Qemu};
|
||||||
|
|
||||||
/// A version of `QemuExecutor` with a state accessible from the harness.
|
/// A version of `QemuExecutor` with a state accessible from the harness.
|
||||||
pub mod stateful;
|
pub mod stateful;
|
||||||
@ -149,7 +149,7 @@ where
|
|||||||
unsafe {
|
unsafe {
|
||||||
libafl::executors::inprocess::generic_inproc_crash_handler::<E, EM, OF, Z>();
|
libafl::executors::inprocess::generic_inproc_crash_handler::<E, EM, OF, Z>();
|
||||||
}
|
}
|
||||||
if let Some(cpu) = hooks.emulator().current_cpu() {
|
if let Some(cpu) = hooks.qemu().current_cpu() {
|
||||||
eprint!("Context:\n{}", cpu.display_context());
|
eprint!("Context:\n{}", cpu.display_context());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -172,8 +172,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn emulator(&self) -> &Emulator {
|
pub fn qemu(&self) -> &Qemu {
|
||||||
self.hooks.emulator()
|
self.hooks.qemu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,8 +246,8 @@ where
|
|||||||
self.state.hooks_mut()
|
self.state.hooks_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator {
|
pub fn emulator(&self) -> &Qemu {
|
||||||
self.state.emulator()
|
self.state.qemu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ where
|
|||||||
S: State + HasExecutions + HasCorpus + HasSolutions,
|
S: State + HasExecutions + HasCorpus + HasSolutions,
|
||||||
QT: QemuHelperTuple<S> + Debug,
|
QT: QemuHelperTuple<S> + Debug,
|
||||||
{
|
{
|
||||||
fn pre_exec<E, EM, OF, Z>(&mut self, input: &E::Input, emu: &Emulator)
|
fn pre_exec<E, EM, OF, Z>(&mut self, input: &E::Input, qemu: Qemu)
|
||||||
where
|
where
|
||||||
E: Executor<EM, Z, State = S>,
|
E: Executor<EM, Z, State = S>,
|
||||||
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
EM: EventFirer<State = S> + EventRestarter<State = S>,
|
||||||
@ -267,13 +267,13 @@ where
|
|||||||
self.hooks.helpers().first_exec_all(self.hooks);
|
self.hooks.helpers().first_exec_all(self.hooks);
|
||||||
self.first_exec = false;
|
self.first_exec = false;
|
||||||
}
|
}
|
||||||
self.hooks.helpers_mut().pre_exec_all(emu, input);
|
self.hooks.helpers_mut().pre_exec_all(qemu, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec<E, EM, OT, OF, Z>(
|
fn post_exec<E, EM, OT, OF, Z>(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &E::Input,
|
input: &E::Input,
|
||||||
emu: &Emulator,
|
qemu: Qemu,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
exit_kind: &mut ExitKind,
|
exit_kind: &mut ExitKind,
|
||||||
) where
|
) where
|
||||||
@ -285,7 +285,7 @@ where
|
|||||||
{
|
{
|
||||||
self.hooks
|
self.hooks
|
||||||
.helpers_mut()
|
.helpers_mut()
|
||||||
.post_exec_all(emu, input, observers, exit_kind);
|
.post_exec_all(qemu, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,12 +306,12 @@ where
|
|||||||
mgr: &mut EM,
|
mgr: &mut EM,
|
||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
let emu = Emulator::get().unwrap();
|
let qemu = Qemu::get().unwrap();
|
||||||
self.state.pre_exec::<Self, EM, OF, Z>(input, &emu);
|
self.state.pre_exec::<Self, EM, OF, Z>(input, qemu);
|
||||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.state.post_exec::<Self, EM, OT, OF, Z>(
|
self.state.post_exec::<Self, EM, OT, OF, Z>(
|
||||||
input,
|
input,
|
||||||
&emu,
|
qemu,
|
||||||
self.inner.observers_mut(),
|
self.inner.observers_mut(),
|
||||||
&mut exit_kind,
|
&mut exit_kind,
|
||||||
);
|
);
|
||||||
@ -449,8 +449,8 @@ where
|
|||||||
self.state.hooks
|
self.state.hooks
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator {
|
pub fn qemu(&self) -> &Qemu {
|
||||||
self.state.hooks.emulator()
|
self.state.hooks.qemu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,15 +474,15 @@ where
|
|||||||
mgr: &mut EM,
|
mgr: &mut EM,
|
||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
let emu = Emulator::get().unwrap();
|
let qemu = *self.state.hooks.qemu();
|
||||||
if self.state.first_exec {
|
if self.state.first_exec {
|
||||||
self.state.hooks.helpers().first_exec_all(self.state.hooks);
|
self.state.hooks.helpers().first_exec_all(self.state.hooks);
|
||||||
self.state.first_exec = false;
|
self.state.first_exec = false;
|
||||||
}
|
}
|
||||||
self.state.hooks.helpers_mut().pre_exec_all(&emu, input);
|
self.state.hooks.helpers_mut().pre_exec_all(qemu, input);
|
||||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.state.hooks.helpers_mut().post_exec_all(
|
self.state.hooks.helpers_mut().post_exec_all(
|
||||||
&emu,
|
qemu,
|
||||||
input,
|
input,
|
||||||
self.inner.observers_mut(),
|
self.inner.observers_mut(),
|
||||||
&mut exit_kind,
|
&mut exit_kind,
|
||||||
|
@ -22,9 +22,7 @@ use libafl::{
|
|||||||
use crate::executor::inproc_qemu_crash_handler;
|
use crate::executor::inproc_qemu_crash_handler;
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT};
|
use crate::executor::{inproc_qemu_timeout_handler, BREAK_ON_TMOUT};
|
||||||
use crate::{
|
use crate::{executor::QemuExecutorState, helper::QemuHelperTuple, hooks::QemuHooks, Qemu};
|
||||||
emu::Emulator, executor::QemuExecutorState, helper::QemuHelperTuple, hooks::QemuHooks,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct StatefulQemuExecutor<'a, H, OT, QT, S>
|
pub struct StatefulQemuExecutor<'a, H, OT, QT, S>
|
||||||
where
|
where
|
||||||
@ -134,8 +132,8 @@ where
|
|||||||
self.inner.exposed_executor_state_mut().hooks_mut()
|
self.inner.exposed_executor_state_mut().hooks_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator {
|
pub fn emulator(&self) -> &Qemu {
|
||||||
self.inner.exposed_executor_state().emulator()
|
self.inner.exposed_executor_state().qemu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,16 +154,16 @@ where
|
|||||||
mgr: &mut EM,
|
mgr: &mut EM,
|
||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
let emu = Emulator::get().unwrap();
|
let qemu = Qemu::get().unwrap();
|
||||||
self.inner
|
self.inner
|
||||||
.exposed_executor_state_mut()
|
.exposed_executor_state_mut()
|
||||||
.pre_exec::<Self, EM, OF, Z>(input, &emu);
|
.pre_exec::<Self, EM, OF, Z>(input, qemu);
|
||||||
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
let mut exit_kind = self.inner.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.inner
|
self.inner
|
||||||
.exposed_executor_state
|
.exposed_executor_state
|
||||||
.post_exec::<Self, EM, OT, OF, Z>(
|
.post_exec::<Self, EM, OT, OF, Z>(
|
||||||
input,
|
input,
|
||||||
&emu,
|
qemu,
|
||||||
self.inner.inner.observers_mut(),
|
self.inner.inner.observers_mut(),
|
||||||
&mut exit_kind,
|
&mut exit_kind,
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
use core::{fmt::Debug, ops::Range};
|
use core::{fmt::Debug, ops::Range};
|
||||||
use std::{collections::HashSet, hash};
|
use std::{collections::HashSet, hash::BuildHasher};
|
||||||
|
|
||||||
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
|
||||||
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
|
||||||
|
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{hooks::QemuHooks, Qemu};
|
||||||
emu::{Emulator, GuestAddr},
|
|
||||||
hooks::QemuHooks,
|
|
||||||
GuestPhysAddr,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A helper for `libafl_qemu`.
|
/// A helper for `libafl_qemu`.
|
||||||
// TODO remove 'static when specialization will be stable
|
// TODO remove 'static when specialization will be stable
|
||||||
@ -30,11 +27,11 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {}
|
fn pre_exec(&mut self, _qemu: Qemu, _input: &S::Input) {}
|
||||||
|
|
||||||
fn post_exec<OT>(
|
fn post_exec<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -58,11 +55,11 @@ where
|
|||||||
where
|
where
|
||||||
QT: QemuHelperTuple<S>;
|
QT: QemuHelperTuple<S>;
|
||||||
|
|
||||||
fn pre_exec_all(&mut self, _emulator: &Emulator, input: &S::Input);
|
fn pre_exec_all(&mut self, _qemu: Qemu, input: &S::Input);
|
||||||
|
|
||||||
fn post_exec_all<OT>(
|
fn post_exec_all<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -88,11 +85,11 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &S::Input) {}
|
fn pre_exec_all(&mut self, _qemu: Qemu, _input: &S::Input) {}
|
||||||
|
|
||||||
fn post_exec_all<OT>(
|
fn post_exec_all<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
_emulator: &Emulator,
|
_qemu: Qemu,
|
||||||
_input: &S::Input,
|
_input: &S::Input,
|
||||||
_observers: &mut OT,
|
_observers: &mut OT,
|
||||||
_exit_kind: &mut ExitKind,
|
_exit_kind: &mut ExitKind,
|
||||||
@ -102,6 +99,21 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Head, F, S> HasInstrumentationFilter<F, S> for (Head, ())
|
||||||
|
where
|
||||||
|
Head: QemuHelper<S> + HasInstrumentationFilter<F, S>,
|
||||||
|
S: UsesInput,
|
||||||
|
F: IsFilter,
|
||||||
|
{
|
||||||
|
fn filter(&self) -> &F {
|
||||||
|
self.0.filter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_mut(&mut self) -> &mut F {
|
||||||
|
self.0.filter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Head, Tail, S> QemuHelperTuple<S> for (Head, Tail)
|
impl<Head, Tail, S> QemuHelperTuple<S> for (Head, Tail)
|
||||||
where
|
where
|
||||||
Head: QemuHelper<S>,
|
Head: QemuHelper<S>,
|
||||||
@ -126,27 +138,27 @@ where
|
|||||||
self.1.first_exec_all(hooks);
|
self.1.first_exec_all(hooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_all(&mut self, emulator: &Emulator, input: &S::Input) {
|
fn pre_exec_all(&mut self, qemu: Qemu, input: &S::Input) {
|
||||||
self.0.pre_exec(emulator, input);
|
self.0.pre_exec(qemu, input);
|
||||||
self.1.pre_exec_all(emulator, input);
|
self.1.pre_exec_all(qemu, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec_all<OT>(
|
fn post_exec_all<OT>(
|
||||||
&mut self,
|
&mut self,
|
||||||
emulator: &Emulator,
|
qemu: Qemu,
|
||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
observers: &mut OT,
|
observers: &mut OT,
|
||||||
exit_kind: &mut ExitKind,
|
exit_kind: &mut ExitKind,
|
||||||
) where
|
) where
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
{
|
{
|
||||||
self.0.post_exec(emulator, input, observers, exit_kind);
|
self.0.post_exec(qemu, input, observers, exit_kind);
|
||||||
self.1.post_exec_all(emulator, input, observers, exit_kind);
|
self.1.post_exec_all(qemu, input, observers, exit_kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum QemuFilterList<T: IsFilter + Debug> {
|
pub enum QemuFilterList<T: IsFilter + Debug + Clone> {
|
||||||
AllowList(T),
|
AllowList(T),
|
||||||
DenyList(T),
|
DenyList(T),
|
||||||
None,
|
None,
|
||||||
@ -154,7 +166,7 @@ pub enum QemuFilterList<T: IsFilter + Debug> {
|
|||||||
|
|
||||||
impl<T> IsFilter for QemuFilterList<T>
|
impl<T> IsFilter for QemuFilterList<T>
|
||||||
where
|
where
|
||||||
T: IsFilter,
|
T: IsFilter + Clone,
|
||||||
{
|
{
|
||||||
type FilterParameter = T::FilterParameter;
|
type FilterParameter = T::FilterParameter;
|
||||||
|
|
||||||
@ -169,7 +181,10 @@ where
|
|||||||
|
|
||||||
pub type QemuInstrumentationPagingFilter = QemuFilterList<HashSet<GuestPhysAddr>>;
|
pub type QemuInstrumentationPagingFilter = QemuFilterList<HashSet<GuestPhysAddr>>;
|
||||||
|
|
||||||
impl<H: hash::BuildHasher> IsFilter for HashSet<GuestPhysAddr, H> {
|
impl<H> IsFilter for HashSet<GuestPhysAddr, H>
|
||||||
|
where
|
||||||
|
H: BuildHasher,
|
||||||
|
{
|
||||||
type FilterParameter = Option<GuestPhysAddr>;
|
type FilterParameter = Option<GuestPhysAddr>;
|
||||||
|
|
||||||
fn allowed(&self, paging_id: Self::FilterParameter) -> bool {
|
fn allowed(&self, paging_id: Self::FilterParameter) -> bool {
|
||||||
@ -192,7 +207,7 @@ impl IsFilter for Vec<Range<GuestAddr>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasInstrumentationFilter<F>
|
pub trait HasInstrumentationFilter<F, S>
|
||||||
where
|
where
|
||||||
F: IsFilter,
|
F: IsFilter,
|
||||||
{
|
{
|
||||||
@ -200,12 +215,43 @@ where
|
|||||||
|
|
||||||
fn filter_mut(&mut self) -> &mut F;
|
fn filter_mut(&mut self) -> &mut F;
|
||||||
|
|
||||||
fn update_filter(&mut self, filter: F, emu: &Emulator) {
|
fn update_filter(&mut self, filter: F, emu: &Qemu) {
|
||||||
*self.filter_mut() = filter;
|
*self.filter_mut() = filter;
|
||||||
emu.flush_jit();
|
emu.flush_jit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
pub trait StdInstrumentationFilter<S: UsesInput>:
|
||||||
|
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub trait StdInstrumentationFilter<S: UsesInput>:
|
||||||
|
HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter, S>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
impl<Head, S> StdInstrumentationFilter<S> for (Head, ())
|
||||||
|
where
|
||||||
|
Head: QemuHelper<S>
|
||||||
|
+ HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>
|
||||||
|
+ HasInstrumentationFilter<QemuInstrumentationPagingFilter, S>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
impl<Head, S> StdInstrumentationFilter<S> for (Head, ())
|
||||||
|
where
|
||||||
|
Head: QemuHelper<S> + HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter, S>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
pub trait IsFilter: Debug {
|
pub trait IsFilter: Debug {
|
||||||
type FilterParameter;
|
type FilterParameter;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -64,19 +64,19 @@ pub enum Regs {
|
|||||||
Pktcnthi = 51,
|
Pktcnthi = 51,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::R0,
|
BackdoorArgs::Ret => Regs::R0,
|
||||||
SyncBackdoorArgs::Cmd => Regs::R0,
|
BackdoorArgs::Cmd => Regs::R0,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::R1,
|
BackdoorArgs::Arg1 => Regs::R1,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::R2,
|
BackdoorArgs::Arg2 => Regs::R2,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::R3,
|
BackdoorArgs::Arg3 => Regs::R3,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::R4,
|
BackdoorArgs::Arg4 => Regs::R4,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::R5,
|
BackdoorArgs::Arg5 => Regs::R5,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::R6,
|
BackdoorArgs::Arg6 => Regs::R6,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
//! The high-level hooks
|
//! The high-level hooks
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
use core::ptr::addr_of_mut;
|
||||||
use core::{
|
use core::{
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
mem::transmute,
|
mem::transmute,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr::{self, addr_of, addr_of_mut},
|
ptr::{self, addr_of},
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
@ -15,15 +17,17 @@ use libafl::{
|
|||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
state::NopState,
|
state::NopState,
|
||||||
};
|
};
|
||||||
|
use libafl_qemu_sys::{FatPtr, GuestAddr, GuestUsize};
|
||||||
|
|
||||||
pub use crate::emu::SyscallHookResult;
|
pub use crate::emu::SyscallHookResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{Emulator, FatPtr, MemAccessInfo, SKIP_EXEC_HOOK},
|
emu::{MemAccessInfo, Qemu, SKIP_EXEC_HOOK},
|
||||||
helper::QemuHelperTuple,
|
helper::QemuHelperTuple,
|
||||||
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, GuestAddr, GuestUsize, HookId,
|
BackdoorHookId, BlockHookId, CmpHookId, EdgeHookId, HookId, InstructionHookId, ReadHookId,
|
||||||
InstructionHookId, NewThreadHookId, PostSyscallHookId, PreSyscallHookId, ReadHookId,
|
|
||||||
WriteHookId,
|
WriteHookId,
|
||||||
};
|
};
|
||||||
|
#[cfg(emulation_mode = "usermode")]
|
||||||
|
use crate::{NewThreadHookId, PostSyscallHookId, PreSyscallHookId};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// all kinds of hooks
|
// all kinds of hooks
|
||||||
@ -386,7 +390,7 @@ where
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
helpers: QT,
|
helpers: QT,
|
||||||
emulator: Emulator,
|
qemu: Qemu,
|
||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +402,7 @@ where
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("QemuHooks")
|
f.debug_struct("QemuHooks")
|
||||||
.field("helpers", &self.helpers)
|
.field("helpers", &self.helpers)
|
||||||
.field("emulator", &self.emulator)
|
.field("emulator", &self.qemu)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,8 +412,8 @@ where
|
|||||||
QT: QemuHelperTuple<NopState<I>>,
|
QT: QemuHelperTuple<NopState<I>>,
|
||||||
NopState<I>: UsesInput<Input = I>,
|
NopState<I>: UsesInput<Input = I>,
|
||||||
{
|
{
|
||||||
pub fn reproducer(emulator: Emulator, helpers: QT) -> Box<Self> {
|
pub fn reproducer(qemu: Qemu, helpers: QT) -> Box<Self> {
|
||||||
Self::new(emulator, helpers)
|
Self::new(qemu, helpers)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repro_run<H>(&mut self, harness: &mut H, input: &I) -> ExitKind
|
pub fn repro_run<H>(&mut self, harness: &mut H, input: &I) -> ExitKind
|
||||||
@ -422,12 +426,12 @@ where
|
|||||||
FIRST_EXEC = false;
|
FIRST_EXEC = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.helpers.pre_exec_all(&self.emulator, input);
|
self.helpers.pre_exec_all(self.qemu, input);
|
||||||
|
|
||||||
let mut exit_kind = harness(input);
|
let mut exit_kind = harness(input);
|
||||||
|
|
||||||
self.helpers
|
self.helpers
|
||||||
.post_exec_all(&self.emulator, input, &mut (), &mut exit_kind);
|
.post_exec_all(self.qemu, input, &mut (), &mut exit_kind);
|
||||||
|
|
||||||
exit_kind
|
exit_kind
|
||||||
}
|
}
|
||||||
@ -438,7 +442,7 @@ where
|
|||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
pub fn new(emulator: Emulator, helpers: QT) -> Box<Self> {
|
pub fn new(qemu: Qemu, helpers: QT) -> Box<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert!(
|
assert!(
|
||||||
!HOOKS_IS_INITIALIZED,
|
!HOOKS_IS_INITIALIZED,
|
||||||
@ -447,9 +451,9 @@ where
|
|||||||
HOOKS_IS_INITIALIZED = true;
|
HOOKS_IS_INITIALIZED = true;
|
||||||
}
|
}
|
||||||
// re-translate blocks with hooks
|
// re-translate blocks with hooks
|
||||||
emulator.flush_jit();
|
qemu.flush_jit();
|
||||||
let slf = Box::new(Self {
|
let slf = Box::new(Self {
|
||||||
emulator,
|
qemu,
|
||||||
helpers,
|
helpers,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
});
|
});
|
||||||
@ -476,8 +480,8 @@ where
|
|||||||
self.helpers.match_first_type_mut::<T>()
|
self.helpers.match_first_type_mut::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulator(&self) -> &Emulator {
|
pub fn qemu(&self) -> &Qemu {
|
||||||
&self.emulator
|
&self.qemu
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn helpers(&self) -> &QT {
|
pub fn helpers(&self) -> &QT {
|
||||||
@ -503,7 +507,7 @@ where
|
|||||||
Hook::Closure(c) => self.instruction_closure(addr, c, invalidate_block),
|
Hook::Closure(c) => self.instruction_closure(addr, c, invalidate_block),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
self.emulator.set_hook(z, addr, r, invalidate_block)
|
self.qemu.set_hook(z, addr, r, invalidate_block)
|
||||||
}
|
}
|
||||||
Hook::Empty => InstructionHookId(0), // TODO error type
|
Hook::Empty => InstructionHookId(0), // TODO error type
|
||||||
}
|
}
|
||||||
@ -516,7 +520,7 @@ where
|
|||||||
invalidate_block: bool,
|
invalidate_block: bool,
|
||||||
) -> InstructionHookId {
|
) -> InstructionHookId {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator.set_hook(
|
self.qemu.set_hook(
|
||||||
transmute(hook),
|
transmute(hook),
|
||||||
addr,
|
addr,
|
||||||
func_generic_hook_wrapper::<QT, S>,
|
func_generic_hook_wrapper::<QT, S>,
|
||||||
@ -534,7 +538,7 @@ where
|
|||||||
unsafe {
|
unsafe {
|
||||||
let fat: FatPtr = transmute(hook);
|
let fat: FatPtr = transmute(hook);
|
||||||
GENERIC_HOOKS.push(Box::pin((InstructionHookId(0), fat)));
|
GENERIC_HOOKS.push(Box::pin((InstructionHookId(0), fat)));
|
||||||
let id = self.emulator.set_hook(
|
let id = self.qemu.set_hook(
|
||||||
&mut GENERIC_HOOKS
|
&mut GENERIC_HOOKS
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -596,7 +600,7 @@ where
|
|||||||
post_gen: HookRepr::Empty,
|
post_gen: HookRepr::Empty,
|
||||||
execs: [hook_to_repr!(execution_hook)],
|
execs: [hook_to_repr!(execution_hook)],
|
||||||
}));
|
}));
|
||||||
let id = self.emulator.add_edge_hooks(
|
let id = self.qemu.add_edge_hooks(
|
||||||
EDGE_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
EDGE_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
||||||
gen,
|
gen,
|
||||||
exec,
|
exec,
|
||||||
@ -655,7 +659,7 @@ where
|
|||||||
post_gen: hook_to_repr!(post_generation_hook),
|
post_gen: hook_to_repr!(post_generation_hook),
|
||||||
execs: [hook_to_repr!(execution_hook)],
|
execs: [hook_to_repr!(execution_hook)],
|
||||||
}));
|
}));
|
||||||
let id = self.emulator.add_block_hooks(
|
let id = self.qemu.add_block_hooks(
|
||||||
BLOCK_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
BLOCK_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
||||||
gen,
|
gen,
|
||||||
postgen,
|
postgen,
|
||||||
@ -759,7 +763,7 @@ where
|
|||||||
hook_to_repr!(execution_hook_n),
|
hook_to_repr!(execution_hook_n),
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
let id = self.emulator.add_read_hooks(
|
let id = self.qemu.add_read_hooks(
|
||||||
READ_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
READ_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
||||||
gen,
|
gen,
|
||||||
exec1,
|
exec1,
|
||||||
@ -871,7 +875,7 @@ where
|
|||||||
hook_to_repr!(execution_hook_n),
|
hook_to_repr!(execution_hook_n),
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
let id = self.emulator.add_write_hooks(
|
let id = self.qemu.add_write_hooks(
|
||||||
WRITE_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
WRITE_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
||||||
gen,
|
gen,
|
||||||
exec1,
|
exec1,
|
||||||
@ -957,7 +961,7 @@ where
|
|||||||
hook_to_repr!(execution_hook_8),
|
hook_to_repr!(execution_hook_8),
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
let id = self.emulator.add_cmp_hooks(
|
let id = self.qemu.add_cmp_hooks(
|
||||||
CMP_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
CMP_HOOKS.last_mut().unwrap().as_mut().get_unchecked_mut(),
|
||||||
gen,
|
gen,
|
||||||
exec1,
|
exec1,
|
||||||
@ -988,7 +992,7 @@ where
|
|||||||
Hook::Closure(c) => self.backdoor_closure(c),
|
Hook::Closure(c) => self.backdoor_closure(c),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
self.emulator.add_backdoor_hook(z, r)
|
self.qemu.add_backdoor_hook(z, r)
|
||||||
}
|
}
|
||||||
Hook::Empty => BackdoorHookId(0), // TODO error type
|
Hook::Empty => BackdoorHookId(0), // TODO error type
|
||||||
}
|
}
|
||||||
@ -999,7 +1003,7 @@ where
|
|||||||
hook: fn(&mut Self, Option<&mut S>, pc: GuestAddr),
|
hook: fn(&mut Self, Option<&mut S>, pc: GuestAddr),
|
||||||
) -> BackdoorHookId {
|
) -> BackdoorHookId {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator
|
self.qemu
|
||||||
.add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::<QT, S>)
|
.add_backdoor_hook(transmute(hook), func_backdoor_hook_wrapper::<QT, S>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1011,7 +1015,7 @@ where
|
|||||||
unsafe {
|
unsafe {
|
||||||
let fat: FatPtr = transmute(hook);
|
let fat: FatPtr = transmute(hook);
|
||||||
BACKDOOR_HOOKS.push(Box::pin((BackdoorHookId(0), fat)));
|
BACKDOOR_HOOKS.push(Box::pin((BackdoorHookId(0), fat)));
|
||||||
let id = self.emulator.add_backdoor_hook(
|
let id = self.qemu.add_backdoor_hook(
|
||||||
&mut BACKDOOR_HOOKS
|
&mut BACKDOOR_HOOKS
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1082,7 +1086,7 @@ where
|
|||||||
Hook::Closure(c) => self.syscalls_closure(c),
|
Hook::Closure(c) => self.syscalls_closure(c),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
self.emulator.add_pre_syscall_hook(z, r)
|
self.qemu.add_pre_syscall_hook(z, r)
|
||||||
}
|
}
|
||||||
Hook::Empty => PreSyscallHookId(0), // TODO error type
|
Hook::Empty => PreSyscallHookId(0), // TODO error type
|
||||||
}
|
}
|
||||||
@ -1107,7 +1111,7 @@ where
|
|||||||
) -> SyscallHookResult,
|
) -> SyscallHookResult,
|
||||||
) -> PreSyscallHookId {
|
) -> PreSyscallHookId {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator
|
self.qemu
|
||||||
.add_pre_syscall_hook(transmute(hook), func_pre_syscall_hook_wrapper::<QT, S>)
|
.add_pre_syscall_hook(transmute(hook), func_pre_syscall_hook_wrapper::<QT, S>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1135,7 +1139,7 @@ where
|
|||||||
unsafe {
|
unsafe {
|
||||||
let fat: FatPtr = transmute(hook);
|
let fat: FatPtr = transmute(hook);
|
||||||
PRE_SYSCALL_HOOKS.push(Box::pin((PreSyscallHookId(0), fat)));
|
PRE_SYSCALL_HOOKS.push(Box::pin((PreSyscallHookId(0), fat)));
|
||||||
let id = self.emulator.add_pre_syscall_hook(
|
let id = self.qemu.add_pre_syscall_hook(
|
||||||
&mut PRE_SYSCALL_HOOKS
|
&mut PRE_SYSCALL_HOOKS
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1209,7 +1213,7 @@ where
|
|||||||
Hook::Closure(c) => self.after_syscalls_closure(c),
|
Hook::Closure(c) => self.after_syscalls_closure(c),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
self.emulator.add_post_syscall_hook(z, r)
|
self.qemu.add_post_syscall_hook(z, r)
|
||||||
}
|
}
|
||||||
Hook::Empty => PostSyscallHookId(0), // TODO error type
|
Hook::Empty => PostSyscallHookId(0), // TODO error type
|
||||||
}
|
}
|
||||||
@ -1235,7 +1239,7 @@ where
|
|||||||
) -> GuestAddr,
|
) -> GuestAddr,
|
||||||
) -> PostSyscallHookId {
|
) -> PostSyscallHookId {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator
|
self.qemu
|
||||||
.add_post_syscall_hook(transmute(hook), func_post_syscall_hook_wrapper::<QT, S>)
|
.add_post_syscall_hook(transmute(hook), func_post_syscall_hook_wrapper::<QT, S>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1264,7 +1268,7 @@ where
|
|||||||
unsafe {
|
unsafe {
|
||||||
let fat: FatPtr = transmute(hook);
|
let fat: FatPtr = transmute(hook);
|
||||||
POST_SYSCALL_HOOKS.push(Box::pin((PostSyscallHookId(0), fat)));
|
POST_SYSCALL_HOOKS.push(Box::pin((PostSyscallHookId(0), fat)));
|
||||||
let id = self.emulator.add_post_syscall_hook(
|
let id = self.qemu.add_post_syscall_hook(
|
||||||
&mut POST_SYSCALL_HOOKS
|
&mut POST_SYSCALL_HOOKS
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1297,7 +1301,7 @@ where
|
|||||||
Hook::Closure(c) => self.thread_creation_closure(c),
|
Hook::Closure(c) => self.thread_creation_closure(c),
|
||||||
Hook::Raw(r) => {
|
Hook::Raw(r) => {
|
||||||
let z: *const () = ptr::null::<()>();
|
let z: *const () = ptr::null::<()>();
|
||||||
self.emulator.add_new_thread_hook(z, r)
|
self.qemu.add_new_thread_hook(z, r)
|
||||||
}
|
}
|
||||||
Hook::Empty => NewThreadHookId(0), // TODO error type
|
Hook::Empty => NewThreadHookId(0), // TODO error type
|
||||||
}
|
}
|
||||||
@ -1309,7 +1313,7 @@ where
|
|||||||
hook: fn(&mut Self, Option<&mut S>, tid: u32) -> bool,
|
hook: fn(&mut Self, Option<&mut S>, tid: u32) -> bool,
|
||||||
) -> NewThreadHookId {
|
) -> NewThreadHookId {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator
|
self.qemu
|
||||||
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<QT, S>)
|
.add_new_thread_hook(transmute(hook), func_new_thread_hook_wrapper::<QT, S>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1322,7 +1326,7 @@ where
|
|||||||
unsafe {
|
unsafe {
|
||||||
let fat: FatPtr = transmute(hook);
|
let fat: FatPtr = transmute(hook);
|
||||||
NEW_THREAD_HOOKS.push(Box::pin((NewThreadHookId(0), fat)));
|
NEW_THREAD_HOOKS.push(Box::pin((NewThreadHookId(0), fat)));
|
||||||
let id = self.emulator.add_new_thread_hook(
|
let id = self.qemu.add_new_thread_hook(
|
||||||
&mut NEW_THREAD_HOOKS
|
&mut NEW_THREAD_HOOKS
|
||||||
.last_mut()
|
.last_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1344,7 +1348,7 @@ where
|
|||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub fn crash_function(&self, hook: fn(&mut Self, target_signal: i32)) {
|
pub fn crash_function(&self, hook: fn(&mut Self, target_signal: i32)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator.set_crash_hook(crash_hook_wrapper::<QT, S>);
|
self.qemu.set_crash_hook(crash_hook_wrapper::<QT, S>);
|
||||||
CRASH_HOOKS.push(HookRepr::Function(hook as *const libc::c_void));
|
CRASH_HOOKS.push(HookRepr::Function(hook as *const libc::c_void));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1352,7 +1356,7 @@ where
|
|||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub fn crash_closure(&self, hook: Box<dyn FnMut(&mut Self, i32)>) {
|
pub fn crash_closure(&self, hook: Box<dyn FnMut(&mut Self, i32)>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.emulator.set_crash_hook(crash_hook_wrapper::<QT, S>);
|
self.qemu.set_crash_hook(crash_hook_wrapper::<QT, S>);
|
||||||
CRASH_HOOKS.push(HookRepr::Closure(transmute(hook)));
|
CRASH_HOOKS.push(HookRepr::Closure(transmute(hook)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86::*;
|
pub use syscall_numbers::x86::*;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention, GuestAddr};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention, GuestAddr};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -25,19 +25,19 @@ pub enum Regs {
|
|||||||
Eflags = 9,
|
Eflags = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::Eax,
|
BackdoorArgs::Ret => Regs::Eax,
|
||||||
SyncBackdoorArgs::Cmd => Regs::Eax,
|
BackdoorArgs::Cmd => Regs::Eax,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::Edi,
|
BackdoorArgs::Arg1 => Regs::Edi,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::Esi,
|
BackdoorArgs::Arg2 => Regs::Esi,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::Edx,
|
BackdoorArgs::Arg3 => Regs::Edx,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::Ebx,
|
BackdoorArgs::Arg4 => Regs::Ebx,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::Ecx,
|
BackdoorArgs::Arg5 => Regs::Ecx,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::Ebp,
|
BackdoorArgs::Arg6 => Regs::Ebp,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,14 @@ use std::{ffi::CStr, fmt::Display, fs, os::raw::c_char, path::Path};
|
|||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl::{inputs::UsesInput, Error};
|
use libafl::{inputs::UsesInput, Error};
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
use crate::SYS_execve;
|
use crate::SYS_execve;
|
||||||
use crate::{
|
use crate::{
|
||||||
elf::EasyElf, emu::ArchExtras, CallingConvention, Emulator, GuestAddr, Hook, QemuHelper,
|
elf::EasyElf, emu::ArchExtras, CallingConvention, Hook, Qemu, QemuHelper, QemuHelperTuple,
|
||||||
QemuHelperTuple, QemuHooks, SyscallHookResult,
|
QemuHooks, SyscallHookResult,
|
||||||
};
|
};
|
||||||
#[cfg(cpu_target = "hexagon")]
|
#[cfg(cpu_target = "hexagon")]
|
||||||
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
|
/// Hexagon syscalls are not currently supported by the `syscalls` crate, so we just paste this here for now.
|
||||||
@ -211,8 +212,8 @@ impl QemuInjectionHelper {
|
|||||||
id: usize,
|
id: usize,
|
||||||
parameter: u8,
|
parameter: u8,
|
||||||
) {
|
) {
|
||||||
let emu = hooks.emulator();
|
let qemu = hooks.qemu();
|
||||||
let reg: GuestAddr = emu
|
let reg: GuestAddr = qemu
|
||||||
.current_cpu()
|
.current_cpu()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read_function_argument(CallingConvention::Cdecl, parameter)
|
.read_function_argument(CallingConvention::Cdecl, parameter)
|
||||||
@ -266,10 +267,10 @@ where
|
|||||||
where
|
where
|
||||||
QT: QemuHelperTuple<S>,
|
QT: QemuHelperTuple<S>,
|
||||||
{
|
{
|
||||||
let emu = hooks.emulator();
|
let qemu = *hooks.qemu();
|
||||||
let mut libs: Vec<LibInfo> = Vec::new();
|
let mut libs: Vec<LibInfo> = Vec::new();
|
||||||
|
|
||||||
for region in emu.mappings() {
|
for region in qemu.mappings() {
|
||||||
if let Some(path) = region.path().map(ToOwned::to_owned) {
|
if let Some(path) = region.path().map(ToOwned::to_owned) {
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
LibInfo::add_unique(
|
LibInfo::add_unique(
|
||||||
@ -300,7 +301,7 @@ where
|
|||||||
vec![func_pc]
|
vec![func_pc]
|
||||||
} else {
|
} else {
|
||||||
libs.iter()
|
libs.iter()
|
||||||
.filter_map(|lib| find_function(emu, &lib.name, name, lib.off).unwrap())
|
.filter_map(|lib| find_function(qemu, &lib.name, name, lib.off).unwrap())
|
||||||
.map(|func_pc| {
|
.map(|func_pc| {
|
||||||
log::info!("Injections: Function {name} found at {func_pc:#x}",);
|
log::info!("Injections: Function {name} found at {func_pc:#x}",);
|
||||||
func_pc
|
func_pc
|
||||||
@ -393,7 +394,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn find_function(
|
fn find_function(
|
||||||
emu: &Emulator,
|
qemu: Qemu,
|
||||||
file: &str,
|
file: &str,
|
||||||
function: &str,
|
function: &str,
|
||||||
loadaddr: GuestAddr,
|
loadaddr: GuestAddr,
|
||||||
@ -403,7 +404,7 @@ fn find_function(
|
|||||||
let offset = if loadaddr > 0 {
|
let offset = if loadaddr > 0 {
|
||||||
loadaddr
|
loadaddr
|
||||||
} else {
|
} else {
|
||||||
emu.load_addr()
|
qemu.load_addr()
|
||||||
};
|
};
|
||||||
Ok(elf.resolve_symbol(function, offset))
|
Ok(elf.resolve_symbol(function, offset))
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ pub use snapshot::QemuSnapshotHelper;
|
|||||||
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
|
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
|
||||||
pub mod asan;
|
pub mod asan;
|
||||||
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
|
#[cfg(all(emulation_mode = "usermode", not(cpu_target = "hexagon")))]
|
||||||
pub use asan::{init_with_asan, QemuAsanHelper};
|
pub use asan::{init_qemu_with_asan, QemuAsanHelper};
|
||||||
|
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub mod calls;
|
pub mod calls;
|
||||||
@ -111,6 +111,8 @@ pub use executor::QemuForkExecutor;
|
|||||||
pub mod emu;
|
pub mod emu;
|
||||||
pub use emu::*;
|
pub use emu::*;
|
||||||
|
|
||||||
|
pub mod breakpoint;
|
||||||
|
pub mod command;
|
||||||
pub mod sync_backdoor;
|
pub mod sync_backdoor;
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -155,7 +157,7 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
|||||||
m.add_class::<emu::MapInfo>()?;
|
m.add_class::<emu::MapInfo>()?;
|
||||||
m.add_class::<emu::GuestMaps>()?;
|
m.add_class::<emu::GuestMaps>()?;
|
||||||
m.add_class::<emu::SyscallHookResult>()?;
|
m.add_class::<emu::SyscallHookResult>()?;
|
||||||
m.add_class::<emu::pybind::Emulator>()?;
|
m.add_class::<emu::pybind::Qemu>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::mips::*;
|
pub use syscall_numbers::mips::*;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -49,19 +49,19 @@ pub enum Regs {
|
|||||||
Pc = 37,
|
Pc = 37,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::V0,
|
BackdoorArgs::Ret => Regs::V0,
|
||||||
SyncBackdoorArgs::Cmd => Regs::V0,
|
BackdoorArgs::Cmd => Regs::V0,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::A0,
|
BackdoorArgs::Arg1 => Regs::A0,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::A1,
|
BackdoorArgs::Arg2 => Regs::A1,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::A2,
|
BackdoorArgs::Arg3 => Regs::A2,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::A3,
|
BackdoorArgs::Arg4 => Regs::A3,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::T0,
|
BackdoorArgs::Arg5 => Regs::T0,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::T1,
|
BackdoorArgs::Arg6 => Regs::T1,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::powerpc::*;
|
pub use syscall_numbers::powerpc::*;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
/// Registers for the MIPS instruction set.
|
/// Registers for the MIPS instruction set.
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
@ -88,19 +88,19 @@ pub enum Regs {
|
|||||||
Fpscr = 70,
|
Fpscr = 70,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::R3,
|
BackdoorArgs::Ret => Regs::R3,
|
||||||
SyncBackdoorArgs::Cmd => Regs::R0,
|
BackdoorArgs::Cmd => Regs::R0,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::R3,
|
BackdoorArgs::Arg1 => Regs::R3,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::R4,
|
BackdoorArgs::Arg2 => Regs::R4,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::R5,
|
BackdoorArgs::Arg3 => Regs::R5,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::R6,
|
BackdoorArgs::Arg4 => Regs::R6,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::R7,
|
BackdoorArgs::Arg5 => Regs::R7,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::R8,
|
BackdoorArgs::Arg6 => Regs::R8,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use libafl::{inputs::UsesInput, state::HasMetadata};
|
use libafl::{inputs::UsesInput, state::HasMetadata};
|
||||||
|
use libafl_qemu_sys::{GuestAddr, MmapPerms};
|
||||||
use meminterval::{Interval, IntervalTree};
|
use meminterval::{Interval, IntervalTree};
|
||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
@ -23,18 +24,18 @@ use crate::SYS_mmap2;
|
|||||||
use crate::SYS_newfstatat;
|
use crate::SYS_newfstatat;
|
||||||
use crate::{
|
use crate::{
|
||||||
asan::QemuAsanHelper,
|
asan::QemuAsanHelper,
|
||||||
emu::{Emulator, MmapPerms, SyscallHookResult},
|
emu::SyscallHookResult,
|
||||||
helper::{QemuHelper, QemuHelperTuple},
|
helper::{QemuHelper, QemuHelperTuple},
|
||||||
hooks::{Hook, QemuHooks},
|
hooks::{Hook, QemuHooks},
|
||||||
GuestAddr, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap,
|
Qemu, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap, SYS_munmap,
|
||||||
SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO use the functions provided by Emulator
|
// TODO use the functions provided by Qemu
|
||||||
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
|
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
|
||||||
pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1);
|
pub const SNAPSHOT_PAGE_MASK: GuestAddr = !(SNAPSHOT_PAGE_SIZE as GuestAddr - 1);
|
||||||
|
|
||||||
pub type StopExecutionCallback = Box<dyn FnMut(&mut QemuSnapshotHelper, &Emulator)>;
|
pub type StopExecutionCallback = Box<dyn FnMut(&mut QemuSnapshotHelper, &Qemu)>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SnapshotPageInfo {
|
pub struct SnapshotPageInfo {
|
||||||
@ -136,11 +137,11 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::uninit_assumed_init)]
|
#[allow(clippy::uninit_assumed_init)]
|
||||||
pub fn snapshot(&mut self, emulator: &Emulator) {
|
pub fn snapshot(&mut self, qemu: Qemu) {
|
||||||
self.brk = emulator.get_brk();
|
self.brk = qemu.get_brk();
|
||||||
self.mmap_start = emulator.get_mmap_start();
|
self.mmap_start = qemu.get_mmap_start();
|
||||||
self.pages.clear();
|
self.pages.clear();
|
||||||
for map in emulator.mappings() {
|
for map in qemu.mappings() {
|
||||||
let mut addr = map.start();
|
let mut addr = map.start();
|
||||||
while addr < map.end() {
|
while addr < map.end() {
|
||||||
let mut info = SnapshotPageInfo {
|
let mut info = SnapshotPageInfo {
|
||||||
@ -149,11 +150,11 @@ impl QemuSnapshotHelper {
|
|||||||
private: map.is_priv(),
|
private: map.is_priv(),
|
||||||
data: None,
|
data: None,
|
||||||
};
|
};
|
||||||
if map.flags().is_r() {
|
if map.flags().readable() {
|
||||||
// TODO not just for R pages
|
// TODO not just for R pages
|
||||||
unsafe {
|
unsafe {
|
||||||
info.data = Some(Box::new(core::mem::zeroed()));
|
info.data = Some(Box::new(core::mem::zeroed()));
|
||||||
emulator.read_mem(addr, &mut info.data.as_mut().unwrap()[..]);
|
qemu.read_mem(addr, &mut info.data.as_mut().unwrap()[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.pages.insert(addr, info);
|
self.pages.insert(addr, info);
|
||||||
@ -208,7 +209,7 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, emulator: &Emulator) {
|
pub fn reset(&mut self, qemu: Qemu) {
|
||||||
{
|
{
|
||||||
let new_maps = self.new_maps.get_mut().unwrap();
|
let new_maps = self.new_maps.get_mut().unwrap();
|
||||||
|
|
||||||
@ -223,8 +224,8 @@ impl QemuSnapshotHelper {
|
|||||||
.tree
|
.tree
|
||||||
.query_mut(*page..(page + SNAPSHOT_PAGE_SIZE as GuestAddr))
|
.query_mut(*page..(page + SNAPSHOT_PAGE_SIZE as GuestAddr))
|
||||||
{
|
{
|
||||||
if !entry.value.perms.unwrap_or(MmapPerms::None).is_w() {
|
if !entry.value.perms.unwrap_or(MmapPerms::None).writable() {
|
||||||
drop(emulator.mprotect(
|
drop(qemu.mprotect(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
MmapPerms::ReadWrite,
|
MmapPerms::ReadWrite,
|
||||||
@ -239,7 +240,7 @@ impl QemuSnapshotHelper {
|
|||||||
return true; // Restore later
|
return true; // Restore later
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { emulator.write_mem(*page, &data[..]) };
|
unsafe { qemu.write_mem(*page, &data[..]) };
|
||||||
} else {
|
} else {
|
||||||
panic!("Cannot restored a dirty but unsaved page");
|
panic!("Cannot restored a dirty but unsaved page");
|
||||||
}
|
}
|
||||||
@ -249,7 +250,7 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.reset_maps(emulator);
|
self.reset_maps(qemu);
|
||||||
|
|
||||||
// This one is after that we remapped potential regions mapped at snapshot time but unmapped during execution
|
// This one is after that we remapped potential regions mapped at snapshot time but unmapped during execution
|
||||||
for acc in &mut self.accesses {
|
for acc in &mut self.accesses {
|
||||||
@ -259,9 +260,10 @@ impl QemuSnapshotHelper {
|
|||||||
.tree
|
.tree
|
||||||
.query_mut(*page..(page + SNAPSHOT_PAGE_SIZE as GuestAddr))
|
.query_mut(*page..(page + SNAPSHOT_PAGE_SIZE as GuestAddr))
|
||||||
{
|
{
|
||||||
if !entry.value.perms.unwrap_or(MmapPerms::None).is_w() && !entry.value.changed
|
if !entry.value.perms.unwrap_or(MmapPerms::None).writable()
|
||||||
|
&& !entry.value.changed
|
||||||
{
|
{
|
||||||
drop(emulator.mprotect(
|
drop(qemu.mprotect(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
MmapPerms::ReadWrite,
|
MmapPerms::ReadWrite,
|
||||||
@ -273,7 +275,7 @@ impl QemuSnapshotHelper {
|
|||||||
if let Some(info) = self.pages.get_mut(page) {
|
if let Some(info) = self.pages.get_mut(page) {
|
||||||
// TODO avoid duplicated memcpy
|
// TODO avoid duplicated memcpy
|
||||||
if let Some(data) = info.data.as_ref() {
|
if let Some(data) = info.data.as_ref() {
|
||||||
unsafe { emulator.write_mem(*page, &data[..]) };
|
unsafe { qemu.write_mem(*page, &data[..]) };
|
||||||
} else {
|
} else {
|
||||||
panic!("Cannot restored a dirty but unsaved page");
|
panic!("Cannot restored a dirty but unsaved page");
|
||||||
}
|
}
|
||||||
@ -284,7 +286,7 @@ impl QemuSnapshotHelper {
|
|||||||
|
|
||||||
for entry in self.maps.tree.query_mut(0..GuestAddr::MAX) {
|
for entry in self.maps.tree.query_mut(0..GuestAddr::MAX) {
|
||||||
if entry.value.changed {
|
if entry.value.changed {
|
||||||
drop(emulator.mprotect(
|
drop(qemu.mprotect(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
entry.value.perms.unwrap(),
|
entry.value.perms.unwrap(),
|
||||||
@ -293,8 +295,8 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator.set_brk(self.brk);
|
qemu.set_brk(self.brk);
|
||||||
emulator.set_mmap_start(self.mmap_start);
|
qemu.set_mmap_start(self.mmap_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_unmap_allowed(&mut self, start: GuestAddr, mut size: usize) -> bool {
|
pub fn is_unmap_allowed(&mut self, start: GuestAddr, mut size: usize) -> bool {
|
||||||
@ -331,8 +333,8 @@ impl QemuSnapshotHelper {
|
|||||||
|
|
||||||
if self.mmap_limit != 0 && total_size > self.mmap_limit {
|
if self.mmap_limit != 0 && total_size > self.mmap_limit {
|
||||||
let mut cb = self.stop_execution.take().unwrap();
|
let mut cb = self.stop_execution.take().unwrap();
|
||||||
let emu = Emulator::get().unwrap();
|
let qemu = Qemu::get().unwrap();
|
||||||
(cb)(self, &emu);
|
(cb)(self, &qemu);
|
||||||
self.stop_execution = Some(cb);
|
self.stop_execution = Some(cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,7 +427,7 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_maps(&mut self, emulator: &Emulator) {
|
pub fn reset_maps(&mut self, qemu: Qemu) {
|
||||||
let new_maps = self.new_maps.get_mut().unwrap();
|
let new_maps = self.new_maps.get_mut().unwrap();
|
||||||
|
|
||||||
for entry in self.maps.tree.query(0..GuestAddr::MAX) {
|
for entry in self.maps.tree.query(0..GuestAddr::MAX) {
|
||||||
@ -440,14 +442,14 @@ impl QemuSnapshotHelper {
|
|||||||
|
|
||||||
if found.is_empty() {
|
if found.is_empty() {
|
||||||
//panic!("A pre-snapshot memory region was unmapped");
|
//panic!("A pre-snapshot memory region was unmapped");
|
||||||
drop(emulator.map_fixed(
|
drop(qemu.map_fixed(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
entry.value.perms.unwrap(),
|
entry.value.perms.unwrap(),
|
||||||
));
|
));
|
||||||
} else if found.len() == 1 && found[0].0 == *entry.interval {
|
} else if found.len() == 1 && found[0].0 == *entry.interval {
|
||||||
if found[0].1 && found[0].2 != entry.value.perms {
|
if found[0].1 && found[0].2 != entry.value.perms {
|
||||||
drop(emulator.mprotect(
|
drop(qemu.mprotect(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
entry.value.perms.unwrap(),
|
entry.value.perms.unwrap(),
|
||||||
@ -455,7 +457,7 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO check for holes
|
// TODO check for holes
|
||||||
drop(emulator.mprotect(
|
drop(qemu.mprotect(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
entry.value.perms.unwrap(),
|
entry.value.perms.unwrap(),
|
||||||
@ -468,7 +470,7 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for entry in new_maps.tree.query(0..GuestAddr::MAX) {
|
for entry in new_maps.tree.query(0..GuestAddr::MAX) {
|
||||||
drop(emulator.unmap(
|
drop(qemu.unmap(
|
||||||
entry.interval.start,
|
entry.interval.start,
|
||||||
(entry.interval.end - entry.interval.start) as usize,
|
(entry.interval.end - entry.interval.start) as usize,
|
||||||
));
|
));
|
||||||
@ -510,11 +512,11 @@ where
|
|||||||
hooks.after_syscalls(Hook::Function(trace_mmap_snapshot::<QT, S>));
|
hooks.after_syscalls(Hook::Function(trace_mmap_snapshot::<QT, S>));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
|
fn pre_exec(&mut self, qemu: Qemu, _input: &S::Input) {
|
||||||
if self.empty {
|
if self.empty {
|
||||||
self.snapshot(emulator);
|
self.snapshot(qemu);
|
||||||
} else {
|
} else {
|
||||||
self.reset(emulator);
|
self.reset(qemu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,27 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use enum_map::{enum_map, Enum, EnumMap};
|
use enum_map::{enum_map, Enum, EnumMap};
|
||||||
use libafl::executors::ExitKind;
|
use libafl::{
|
||||||
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
|
executors::ExitKind,
|
||||||
|
state::{HasExecutions, State},
|
||||||
|
};
|
||||||
|
use libafl_qemu_sys::{GuestAddr, GuestPhysAddr, GuestVirtAddr};
|
||||||
|
use num_enum::TryFromPrimitiveError;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
get_sync_backdoor_arch_regs, Emulator, GuestAddrKind, GuestPhysAddr, GuestReg, GuestVirtAddr,
|
command::{
|
||||||
Regs,
|
Command, EmulatorMemoryChunk, EndCommand, FilterCommand, InputCommand, LoadCommand,
|
||||||
|
NativeBackdoorCommand, NativeExitKind, SaveCommand, StartCommand, VersionCommand,
|
||||||
|
},
|
||||||
|
get_backdoor_arch_regs, EmuExitHandler, Emulator, GuestReg, QemuHelperTuple,
|
||||||
|
QemuInstrumentationAddressRangeFilter, Regs, CPU,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SyncBackdoorError {
|
pub enum SyncBackdoorError {
|
||||||
UnknownCommand(GuestReg),
|
UnknownCommand(GuestReg),
|
||||||
RegError(String),
|
RegError(String),
|
||||||
|
VersionDifference(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for SyncBackdoorError {
|
impl From<String> for SyncBackdoorError {
|
||||||
@ -25,7 +34,7 @@ impl From<String> for SyncBackdoorError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Enum)]
|
#[derive(Debug, Clone, Enum)]
|
||||||
pub enum SyncBackdoorArgs {
|
pub enum BackdoorArgs {
|
||||||
Ret,
|
Ret,
|
||||||
Cmd,
|
Cmd,
|
||||||
Arg1,
|
Arg1,
|
||||||
@ -36,98 +45,18 @@ pub enum SyncBackdoorArgs {
|
|||||||
Arg6,
|
Arg6,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move in a separate header file to have a central definition of native definitions,
|
|
||||||
// reusable in targets directly.
|
|
||||||
#[derive(Debug, Clone, TryFromPrimitive)]
|
|
||||||
#[repr(u64)]
|
|
||||||
pub enum NativeSyncBackdoorCommand {
|
|
||||||
Save = 0, // Save the VM
|
|
||||||
Load = 1, // Reload the target without ending the run?
|
|
||||||
InputVirt = 2, // The address is a virtual address using the paging currently running in the VM.
|
|
||||||
InputPhys = 3, // The address is a physical address
|
|
||||||
End = 4, // Implies reloading of the target. The first argument gives the exit status.
|
|
||||||
StartVirt = 5, // Shortcut for Save + InputVirt
|
|
||||||
StartPhys = 6, // Shortcut for Save + InputPhys
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Enum, TryFromPrimitive)]
|
|
||||||
#[repr(u64)]
|
|
||||||
pub enum NativeExitKind {
|
|
||||||
Unknown = 0, // Should not be used
|
|
||||||
Ok = 1, // Normal exit
|
|
||||||
Crash = 2, // Crash reported in the VM
|
|
||||||
}
|
|
||||||
|
|
||||||
static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
static EMU_EXIT_KIND_MAP: OnceLock<EnumMap<NativeExitKind, Option<ExitKind>>> = OnceLock::new();
|
||||||
|
|
||||||
impl From<TryFromPrimitiveError<NativeSyncBackdoorCommand>> for SyncBackdoorError {
|
impl From<TryFromPrimitiveError<NativeBackdoorCommand>> for SyncBackdoorError {
|
||||||
fn from(error: TryFromPrimitiveError<NativeSyncBackdoorCommand>) -> Self {
|
fn from(error: TryFromPrimitiveError<NativeBackdoorCommand>) -> Self {
|
||||||
SyncBackdoorError::UnknownCommand(error.number.try_into().unwrap())
|
SyncBackdoorError::UnknownCommand(error.number.try_into().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct CommandInput {
|
|
||||||
addr: GuestAddrKind,
|
|
||||||
max_input_size: GuestReg,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandInput {
|
|
||||||
pub fn exec(&self, emu: &Emulator, backdoor: &SyncBackdoor, input: &[u8]) {
|
|
||||||
match self.addr {
|
|
||||||
GuestAddrKind::Physical(hwaddr) => unsafe {
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
|
||||||
{
|
|
||||||
// For now the default behaviour is to fall back to virtual addresses
|
|
||||||
emu.write_mem(hwaddr.try_into().unwrap(), input);
|
|
||||||
}
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
|
||||||
{
|
|
||||||
emu.write_phys_mem(hwaddr, input);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
GuestAddrKind::Virtual(vaddr) => unsafe {
|
|
||||||
emu.write_mem(vaddr.try_into().unwrap(), input);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
backdoor.ret(emu, input.len().try_into().unwrap()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for CommandInput {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{} ({:x} max nb bytes)", self.addr, self.max_input_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Command {
|
|
||||||
Save,
|
|
||||||
Load,
|
|
||||||
Input(CommandInput),
|
|
||||||
Start(CommandInput),
|
|
||||||
Exit(Option<ExitKind>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Command {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Command::Save => write!(f, "Save VM"),
|
|
||||||
Command::Load => write!(f, "Reload VM"),
|
|
||||||
Command::Input(command_input) => write!(f, "Set fuzzing input @{command_input}"),
|
|
||||||
Command::Start(command_input) => {
|
|
||||||
write!(f, "Start fuzzing with input @{command_input}")
|
|
||||||
}
|
|
||||||
Command::Exit(exit_kind) => write!(f, "Exit of kind {exit_kind:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SyncBackdoor {
|
pub struct SyncBackdoor {
|
||||||
command: Command,
|
command: Command,
|
||||||
arch_regs_map: &'static EnumMap<SyncBackdoorArgs, Regs>,
|
arch_regs_map: &'static EnumMap<BackdoorArgs, Regs>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncBackdoor {
|
impl SyncBackdoor {
|
||||||
@ -136,8 +65,13 @@ impl SyncBackdoor {
|
|||||||
&self.command
|
&self.command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ret(&self, emu: &Emulator, value: GuestReg) -> Result<(), SyncBackdoorError> {
|
pub fn ret(&self, cpu: &CPU, value: GuestReg) -> Result<(), SyncBackdoorError> {
|
||||||
Ok(emu.write_reg(self.arch_regs_map[SyncBackdoorArgs::Ret], value)?)
|
Ok(cpu.write_reg(self.arch_regs_map[BackdoorArgs::Ret], value)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn ret_reg(&self) -> Regs {
|
||||||
|
self.arch_regs_map[BackdoorArgs::Ret]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,54 +81,63 @@ impl Display for SyncBackdoor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&Emulator> for SyncBackdoor {
|
impl<QT, S, E> TryFrom<&Emulator<QT, S, E>> for SyncBackdoor
|
||||||
|
where
|
||||||
|
E: EmuExitHandler<QT, S>,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
S: State + HasExecutions,
|
||||||
|
{
|
||||||
type Error = SyncBackdoorError;
|
type Error = SyncBackdoorError;
|
||||||
|
|
||||||
fn try_from(emu: &Emulator) -> Result<Self, Self::Error> {
|
#[allow(clippy::too_many_lines)]
|
||||||
let arch_regs_map: &'static EnumMap<SyncBackdoorArgs, Regs> = get_sync_backdoor_arch_regs();
|
fn try_from(emu: &Emulator<QT, S, E>) -> Result<Self, Self::Error> {
|
||||||
let cmd_id: GuestReg =
|
let arch_regs_map: &'static EnumMap<BackdoorArgs, Regs> = get_backdoor_arch_regs();
|
||||||
emu.read_reg::<Regs, GuestReg>(arch_regs_map[SyncBackdoorArgs::Cmd])?;
|
let cmd_id: GuestReg = emu
|
||||||
|
.qemu()
|
||||||
|
.read_reg::<Regs, GuestReg>(arch_regs_map[BackdoorArgs::Cmd])?;
|
||||||
|
|
||||||
Ok(match u64::from(cmd_id).try_into()? {
|
Ok(match u64::from(cmd_id).try_into()? {
|
||||||
NativeSyncBackdoorCommand::Save => SyncBackdoor {
|
NativeBackdoorCommand::Save => SyncBackdoor {
|
||||||
command: Command::Save,
|
command: Command::SaveCommand(SaveCommand),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
},
|
},
|
||||||
NativeSyncBackdoorCommand::Load => SyncBackdoor {
|
NativeBackdoorCommand::Load => SyncBackdoor {
|
||||||
command: Command::Load,
|
command: Command::LoadCommand(LoadCommand),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
},
|
},
|
||||||
NativeSyncBackdoorCommand::InputVirt => {
|
NativeBackdoorCommand::InputVirt => {
|
||||||
let virt_addr: GuestVirtAddr =
|
let virt_addr: GuestVirtAddr =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
let max_input_size: GuestReg =
|
let max_input_size: GuestReg =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
SyncBackdoor {
|
SyncBackdoor {
|
||||||
command: Command::Input(CommandInput {
|
command: Command::InputCommand(InputCommand::new(EmulatorMemoryChunk::virt(
|
||||||
addr: GuestAddrKind::Virtual(virt_addr),
|
virt_addr,
|
||||||
max_input_size,
|
max_input_size,
|
||||||
}),
|
emu.qemu().current_cpu().unwrap().clone(),
|
||||||
|
))),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeSyncBackdoorCommand::InputPhys => {
|
NativeBackdoorCommand::InputPhys => {
|
||||||
let phys_addr: GuestPhysAddr =
|
let phys_addr: GuestPhysAddr =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
let max_input_size: GuestReg =
|
let max_input_size: GuestReg =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
SyncBackdoor {
|
SyncBackdoor {
|
||||||
command: Command::Input(CommandInput {
|
command: Command::InputCommand(InputCommand::new(EmulatorMemoryChunk::phys(
|
||||||
addr: GuestAddrKind::Physical(phys_addr),
|
phys_addr,
|
||||||
max_input_size,
|
max_input_size,
|
||||||
}),
|
Some(emu.qemu().current_cpu().unwrap().clone()),
|
||||||
|
))),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeSyncBackdoorCommand::End => {
|
NativeBackdoorCommand::End => {
|
||||||
let native_exit_kind: GuestReg =
|
let native_exit_kind: GuestReg =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
let native_exit_kind: Result<NativeExitKind, _> =
|
let native_exit_kind: Result<NativeExitKind, _> =
|
||||||
u64::from(native_exit_kind).try_into();
|
u64::from(native_exit_kind).try_into();
|
||||||
|
|
||||||
@ -209,35 +152,61 @@ impl TryFrom<&Emulator> for SyncBackdoor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
SyncBackdoor {
|
SyncBackdoor {
|
||||||
command: Command::Exit(exit_kind),
|
command: Command::EndCommand(EndCommand::new(exit_kind)),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeSyncBackdoorCommand::StartPhys => {
|
NativeBackdoorCommand::StartPhys => {
|
||||||
let input_phys_addr: GuestPhysAddr =
|
let input_phys_addr: GuestPhysAddr =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
let max_input_size: GuestReg =
|
let max_input_size: GuestReg =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
SyncBackdoor {
|
SyncBackdoor {
|
||||||
command: Command::Start(CommandInput {
|
command: Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::phys(
|
||||||
addr: GuestAddrKind::Physical(input_phys_addr),
|
input_phys_addr,
|
||||||
max_input_size,
|
max_input_size,
|
||||||
}),
|
Some(emu.qemu().current_cpu().unwrap().clone()),
|
||||||
|
))),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NativeSyncBackdoorCommand::StartVirt => {
|
NativeBackdoorCommand::StartVirt => {
|
||||||
let input_virt_addr: GuestVirtAddr =
|
let input_virt_addr: GuestVirtAddr =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg1])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
let max_input_size: GuestReg =
|
let max_input_size: GuestReg =
|
||||||
emu.read_reg(arch_regs_map[SyncBackdoorArgs::Arg2])?;
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
SyncBackdoor {
|
SyncBackdoor {
|
||||||
command: Command::Start(CommandInput {
|
command: Command::StartCommand(StartCommand::new(EmulatorMemoryChunk::virt(
|
||||||
addr: GuestAddrKind::Virtual(input_virt_addr),
|
input_virt_addr,
|
||||||
max_input_size,
|
max_input_size,
|
||||||
}),
|
emu.qemu().current_cpu().unwrap().clone(),
|
||||||
|
))),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeBackdoorCommand::Version => {
|
||||||
|
let client_version = emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::VersionCommand(VersionCommand::new(client_version)),
|
||||||
|
arch_regs_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NativeBackdoorCommand::VaddrFilterAllowRange => {
|
||||||
|
let vaddr_start: GuestAddr =
|
||||||
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg1])?;
|
||||||
|
let vaddr_end: GuestAddr =
|
||||||
|
emu.qemu().read_reg(arch_regs_map[BackdoorArgs::Arg2])?;
|
||||||
|
|
||||||
|
SyncBackdoor {
|
||||||
|
command: Command::AddressRangeFilterCommand(FilterCommand::new(
|
||||||
|
#[allow(clippy::single_range_in_vec_init)]
|
||||||
|
QemuInstrumentationAddressRangeFilter::AllowList(vec![
|
||||||
|
vaddr_start..vaddr_end,
|
||||||
|
]),
|
||||||
|
)),
|
||||||
arch_regs_map,
|
arch_regs_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use pyo3::prelude::*;
|
|||||||
pub use strum_macros::EnumIter;
|
pub use strum_macros::EnumIter;
|
||||||
pub use syscall_numbers::x86_64::*;
|
pub use syscall_numbers::x86_64::*;
|
||||||
|
|
||||||
use crate::{sync_backdoor::SyncBackdoorArgs, CallingConvention};
|
use crate::{sync_backdoor::BackdoorArgs, CallingConvention};
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
@ -33,19 +33,19 @@ pub enum Regs {
|
|||||||
Rflags = 17,
|
Rflags = 17,
|
||||||
}
|
}
|
||||||
|
|
||||||
static SYNC_BACKDOOR_ARCH_REGS: OnceLock<EnumMap<SyncBackdoorArgs, Regs>> = OnceLock::new();
|
static BACKDOOR_ARCH_REGS: OnceLock<EnumMap<BackdoorArgs, Regs>> = OnceLock::new();
|
||||||
|
|
||||||
pub fn get_sync_backdoor_arch_regs() -> &'static EnumMap<SyncBackdoorArgs, Regs> {
|
pub fn get_backdoor_arch_regs() -> &'static EnumMap<BackdoorArgs, Regs> {
|
||||||
SYNC_BACKDOOR_ARCH_REGS.get_or_init(|| {
|
BACKDOOR_ARCH_REGS.get_or_init(|| {
|
||||||
enum_map! {
|
enum_map! {
|
||||||
SyncBackdoorArgs::Ret => Regs::Rax,
|
BackdoorArgs::Ret => Regs::Rax,
|
||||||
SyncBackdoorArgs::Cmd => Regs::Rax,
|
BackdoorArgs::Cmd => Regs::Rax,
|
||||||
SyncBackdoorArgs::Arg1 => Regs::Rdi,
|
BackdoorArgs::Arg1 => Regs::Rdi,
|
||||||
SyncBackdoorArgs::Arg2 => Regs::Rsi,
|
BackdoorArgs::Arg2 => Regs::Rsi,
|
||||||
SyncBackdoorArgs::Arg3 => Regs::Rdx,
|
BackdoorArgs::Arg3 => Regs::Rdx,
|
||||||
SyncBackdoorArgs::Arg4 => Regs::R10,
|
BackdoorArgs::Arg4 => Regs::R10,
|
||||||
SyncBackdoorArgs::Arg5 => Regs::R8,
|
BackdoorArgs::Arg5 => Regs::R8,
|
||||||
SyncBackdoorArgs::Arg6 => Regs::R9,
|
BackdoorArgs::Arg6 => Regs::R9,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ use libafl_bolts::{
|
|||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
AsSlice,
|
AsSlice,
|
||||||
};
|
};
|
||||||
pub use libafl_qemu::emu::Emulator;
|
pub use libafl_qemu::emu::Qemu;
|
||||||
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
||||||
use libafl_qemu::QemuCmpLogHelper;
|
use libafl_qemu::QemuCmpLogHelper;
|
||||||
use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks};
|
use libafl_qemu::{edges, QemuEdgeCoverageHelper, QemuExecutor, QemuHooks};
|
||||||
@ -118,7 +118,7 @@ where
|
|||||||
{
|
{
|
||||||
/// Run the fuzzer
|
/// Run the fuzzer
|
||||||
#[allow(clippy::too_many_lines, clippy::similar_names)]
|
#[allow(clippy::too_many_lines, clippy::similar_names)]
|
||||||
pub fn run(&mut self, emulator: &Emulator) {
|
pub fn run(&mut self, qemu: &Qemu) {
|
||||||
let conf = match self.configuration.as_ref() {
|
let conf = match self.configuration.as_ref() {
|
||||||
Some(name) => EventConfig::from_name(name),
|
Some(name) => EventConfig::from_name(name),
|
||||||
None => EventConfig::AlwaysUnique,
|
None => EventConfig::AlwaysUnique,
|
||||||
@ -214,7 +214,7 @@ where
|
|||||||
|
|
||||||
if self.use_cmplog.unwrap_or(false) {
|
if self.use_cmplog.unwrap_or(false) {
|
||||||
let mut hooks = QemuHooks::new(
|
let mut hooks = QemuHooks::new(
|
||||||
emulator.clone(),
|
*qemu,
|
||||||
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
#[cfg(not(any(feature = "mips", feature = "hexagon")))]
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
QemuEdgeCoverageHelper::default(),
|
QemuEdgeCoverageHelper::default(),
|
||||||
@ -325,10 +325,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut hooks = QemuHooks::new(
|
let mut hooks =
|
||||||
emulator.clone(),
|
QemuHooks::new(*qemu, tuple_list!(QemuEdgeCoverageHelper::default()));
|
||||||
tuple_list!(QemuEdgeCoverageHelper::default()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut executor = QemuExecutor::new(
|
let mut executor = QemuExecutor::new(
|
||||||
&mut hooks,
|
&mut hooks,
|
||||||
@ -445,7 +443,7 @@ 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::emu::pybind::Emulator;
|
use libafl_qemu::emu::pybind::Qemu;
|
||||||
use pyo3::{prelude::*, types::PyBytes};
|
use pyo3::{prelude::*, types::PyBytes};
|
||||||
|
|
||||||
use crate::qemu;
|
use crate::qemu;
|
||||||
@ -492,7 +490,7 @@ pub mod pybind {
|
|||||||
|
|
||||||
/// Run the fuzzer
|
/// Run the fuzzer
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
pub fn run(&self, emulator: &Emulator, harness: PyObject) {
|
pub fn run(&self, qemu: &Qemu, 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())
|
||||||
@ -511,7 +509,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(&emulator.emu);
|
.run(&qemu.qemu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user