diff --git a/bindings/pylibafl/Cargo.toml b/bindings/pylibafl/Cargo.toml index 3300a4b8c1..ec5585ceeb 100644 --- a/bindings/pylibafl/Cargo.toml +++ b/bindings/pylibafl/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] pyo3 = { version = "0.15", features = ["extension-module"] } -libafl_qemu = { path = "../../libafl_qemu", version = "0.7", features = ["python"], features = ["x86_64"] } +libafl_qemu = { path = "../../libafl_qemu", version = "0.7", features = ["python"] } libafl_sugar = { path = "../../libafl_sugar", version = "0.7", features = ["python"] } [build-dependencies] diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index 6787ae9fc0..2cede7f60d 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -52,7 +52,7 @@ use libafl_qemu::{ edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, - emu, + emu::Emulator, filter_qemu_args, //snapshot::QemuSnapshotHelper, MmapPerms, @@ -66,10 +66,6 @@ pub fn main() { // Needed only on no_std //RegistryBuilder::register::(); - let args: Vec = env::args().collect(); - let env: Vec<(String, String)> = env::vars().collect(); - emu::init(&args, &env); - let res = match App::new("libafl_qemu_fuzzbench") .version("0.4.0") .author("AFLplusplus team") @@ -170,34 +166,37 @@ fn fuzz( logfile: PathBuf, timeout: Duration, ) -> Result<(), Error> { + env::remove_var("LD_LIBRARY_PATH"); + + let args: Vec = env::args().collect(); + let env: Vec<(String, String)> = env::vars().collect(); + let emu = Emulator::new(&args, &env); + let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file(emu::binary_path(), &mut elf_buffer)?; + let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?; let test_one_input_ptr = elf - .resolve_symbol("LLVMFuzzerTestOneInput", emu::load_addr()) + .resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr()) .expect("Symbol LLVMFuzzerTestOneInput not found"); println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr); - emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput - emu::run(); + emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput + unsafe { emu.run() }; - println!( - "Break at {:#x}", - emu::read_reg::<_, u64>(Regs::Rip).unwrap() - ); + println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap()); - let stack_ptr: u64 = emu::read_reg(Regs::Rsp).unwrap(); + let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap(); let mut ret_addr = [0u64]; - emu::read_mem(stack_ptr, &mut ret_addr); + unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; let ret_addr = ret_addr[0]; println!("Stack pointer = {:#x}", stack_ptr); println!("Return address = {:#x}", ret_addr); - emu::remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput - emu::set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr + emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput + emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr - let input_addr = emu::map_private(0, 4096, MmapPerms::ReadWrite).unwrap(); + let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap(); println!("Placing input at {:#x}", input_addr); let log = RefCell::new( @@ -309,20 +308,23 @@ fn fuzz( len = 4096; } - emu::write_mem(input_addr, buf); + unsafe { + emu.write_mem(input_addr, buf); - emu::write_reg(Regs::Rdi, input_addr).unwrap(); - emu::write_reg(Regs::Rsi, len).unwrap(); - emu::write_reg(Regs::Rip, test_one_input_ptr).unwrap(); - emu::write_reg(Regs::Rsp, stack_ptr).unwrap(); + emu.write_reg(Regs::Rdi, input_addr).unwrap(); + emu.write_reg(Regs::Rsi, len).unwrap(); + emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap(); + emu.write_reg(Regs::Rsp, stack_ptr).unwrap(); - emu::run(); + emu.run(); + } ExitKind::Ok }; let executor = QemuExecutor::new( &mut harness, + &emu, tuple_list!( QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new(), diff --git a/fuzzers/python_qemu/fuzzer.py b/fuzzers/python_qemu/fuzzer.py index 9e12e359ea..fd0245a497 100644 --- a/fuzzers/python_qemu/fuzzer.py +++ b/fuzzers/python_qemu/fuzzer.py @@ -6,38 +6,38 @@ import lief MAX_SIZE = 0x100 BINARY_PATH = './a.out' -qemu.init(['qemu-x86_64', BINARY_PATH], []) +emu = qemu.Emulator(['qemu-x86_64', BINARY_PATH], []) elf = lief.parse(BINARY_PATH) test_one_input = elf.get_function_address("LLVMFuzzerTestOneInput") if elf.is_pie: - test_one_input += qemu.load_addr() + test_one_input += emu.load_addr() print('LLVMFuzzerTestOneInput @ 0x%x' % test_one_input) -qemu.set_breakpoint(test_one_input) -qemu.run() +emu.set_breakpoint(test_one_input) +emu.run() -sp = qemu.read_reg(qemu.amd64.Rsp) +sp = emu.read_reg(qemu.regs.Rsp) print('SP = 0x%x' % sp) -retaddr = int.from_bytes(qemu.read_mem(sp, 8), 'little') +retaddr = int.from_bytes(emu.read_mem(sp, 8), 'little') print('RET = 0x%x' % retaddr) -inp = qemu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite) +inp = emu.map_private(0, MAX_SIZE, qemu.mmap.ReadWrite) assert(inp > 0) -qemu.remove_breakpoint(test_one_input) -qemu.set_breakpoint(retaddr) +emu.remove_breakpoint(test_one_input) +emu.set_breakpoint(retaddr) def harness(b): if len(b) > MAX_SIZE: b = b[:MAX_SIZE] - qemu.write_mem(inp, b) - qemu.write_reg(qemu.amd64.Rsi, len(b)) - qemu.write_reg(qemu.amd64.Rdi, inp) - qemu.write_reg(qemu.amd64.Rsp, sp) - qemu.write_reg(qemu.amd64.Rip, test_one_input) - qemu.run() + emu.write_mem(inp, b) + emu.write_reg(qemu.regs.Rsi, len(b)) + emu.write_reg(qemu.regs.Rdi, inp) + emu.write_reg(qemu.regs.Rsp, sp) + emu.write_reg(qemu.regs.Rip, test_one_input) + emu.run() fuzz = sugar.QemuBytesCoverageSugar(['./in'], './out', 3456, [0,1,2,3]) -fuzz.run(harness) +fuzz.run(emu, harness) diff --git a/fuzzers/qemu_launcher/libpng_harness b/fuzzers/qemu_launcher/libpng_harness deleted file mode 100755 index d431945aee..0000000000 Binary files a/fuzzers/qemu_launcher/libpng_harness and /dev/null differ diff --git a/fuzzers/qemu_launcher/src/fuzzer.rs b/fuzzers/qemu_launcher/src/fuzzer.rs index dece018469..e7a3927b2f 100644 --- a/fuzzers/qemu_launcher/src/fuzzer.rs +++ b/fuzzers/qemu_launcher/src/fuzzer.rs @@ -30,7 +30,18 @@ use libafl::{ Error, }; use libafl_qemu::{ - edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu, MmapPerms, QemuExecutor, Regs, + //asan::QemuAsanHelper, + cmplog, + cmplog::{CmpLogObserver, QemuCmpLogHelper}, + edges, + edges::QemuEdgeCoverageHelper, + elf::EasyElf, + emu::Emulator, + filter_qemu_args, + //snapshot::QemuSnapshotHelper, + MmapPerms, + QemuExecutor, + Regs, }; pub fn fuzz() { @@ -42,39 +53,37 @@ pub fn fuzz() { let objective_dir = PathBuf::from("./crashes"); // Initialize QEMU + env::remove_var("LD_LIBRARY_PATH"); let args: Vec = env::args().collect(); let env: Vec<(String, String)> = env::vars().collect(); - emu::init(&args, &env); + let emu = Emulator::new(&args, &env); let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file(emu::binary_path(), &mut elf_buffer).unwrap(); + let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap(); let test_one_input_ptr = elf - .resolve_symbol("LLVMFuzzerTestOneInput", emu::load_addr()) + .resolve_symbol("LLVMFuzzerTestOneInput", emu.load_addr()) .expect("Symbol LLVMFuzzerTestOneInput not found"); println!("LLVMFuzzerTestOneInput @ {:#x}", test_one_input_ptr); - emu::set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput - emu::run(); + emu.set_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput + unsafe { emu.run() }; - println!( - "Break at {:#x}", - emu::read_reg::<_, u64>(Regs::Rip).unwrap() - ); + println!("Break at {:#x}", emu.read_reg::<_, u64>(Regs::Rip).unwrap()); // Get the return address - let stack_ptr: u64 = emu::read_reg(Regs::Rsp).unwrap(); + let stack_ptr: u64 = emu.read_reg(Regs::Rsp).unwrap(); let mut ret_addr = [0u64]; - emu::read_mem(stack_ptr, &mut ret_addr); + unsafe { emu.read_mem(stack_ptr, &mut ret_addr) }; let ret_addr = ret_addr[0]; println!("Stack pointer = {:#x}", stack_ptr); println!("Return address = {:#x}", ret_addr); - emu::remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput - emu::set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr + emu.remove_breakpoint(test_one_input_ptr); // LLVMFuzzerTestOneInput + emu.set_breakpoint(ret_addr); // LLVMFuzzerTestOneInput ret addr - let input_addr = emu::map_private(0, 4096, MmapPerms::ReadWrite).unwrap(); + let input_addr = emu.map_private(0, 4096, MmapPerms::ReadWrite).unwrap(); println!("Placing input at {:#x}", input_addr); // The wrapped harness function, calling out to the LLVM-style harness @@ -87,14 +96,16 @@ pub fn fuzz() { len = 4096; } - emu::write_mem(input_addr, buf); + unsafe { + emu.write_mem(input_addr, buf); - emu::write_reg(Regs::Rdi, input_addr).unwrap(); - emu::write_reg(Regs::Rsi, len).unwrap(); - emu::write_reg(Regs::Rip, test_one_input_ptr).unwrap(); - emu::write_reg(Regs::Rsp, stack_ptr).unwrap(); + emu.write_reg(Regs::Rdi, input_addr).unwrap(); + emu.write_reg(Regs::Rsi, len).unwrap(); + emu.write_reg(Regs::Rip, test_one_input_ptr).unwrap(); + emu.write_reg(Regs::Rsp, stack_ptr).unwrap(); - emu::run(); + emu.run(); + } ExitKind::Ok }; @@ -149,6 +160,7 @@ pub fn fuzz() { // Create a QEMU in-process executor let executor = QemuExecutor::new( &mut harness, + &emu, // The QEMU helpers define common hooks like coverage tracking hooks tuple_list!(QemuEdgeCoverageHelper::new()), tuple_list!(edges_observer, time_observer), diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 874eebe1cc..a643145650 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -55,7 +55,7 @@ bincode = {version = "1.3", optional = true } static_assertions = "1.1.0" ctor = "0.1.20" num_enum = { version = "0.5.1", default-features = false } -typed-builder = "0.9.0" # Implement the builder pattern at compiletime +typed-builder = "0.9.1" # Implement the builder pattern at compiletime ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] } diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index fdd0b8fe09..0dae22b92e 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -3,7 +3,7 @@ use which::which; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "652155bab71357380420f90a944e463ba4bcd372"; +const QEMU_REVISION: &str = "fa2b9c4a25f548f15b3d1b1afcfdb75cc7165f9a"; fn build_dep_check(tools: &[&str]) { for tool in tools { @@ -36,34 +36,25 @@ fn main() { return; } - // Make sure we have at least and at most one architecutre feature set + // Make sure we have at most one architecutre feature set // Else, we default to `x86_64` - having a default makes CI easier :) assert_unique_feature!("arm", "aarch64", "i386", "i86_64"); - #[cfg(not(any( - feature = "arm", - feature = "aarch64", - feature = "i386", - feature = "x86_64" - )))] - println!( - "cargo:warning=No architecture feature enabled for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64" - ); - let cpu_target = if cfg!(feature = "clippy") { - // assume x86_64 for clippy - "x86_64" + let cpu_target = if cfg!(feature = "x86_64") { + "x86_64".to_string() } else if cfg!(feature = "arm") { - "arm" + "arm".to_string() } else if cfg!(feature = "aarch64") { - "aarch64" + "aarch64".to_string() } else if cfg!(feature = "i386") { - "368" + "i368".to_string() } else { - // if cfg!(feature = "x86_64") { - "x86_64" - /*} else { - panic!("No architecture feture enabled for libafl_qemu"); - */ + env::var("CPU_TARGET").unwrap_or_else(|_| { + println!( + "cargo:warning=No architecture feature enabled or CPU_TARGET env specified for libafl_qemu, supported: arm, aarch64, i386, x86_64 - defaulting to x86_64" + ); + "x86_64".to_string() + }) }; let jobs = env::var("CARGO_BUILD_JOBS"); @@ -112,8 +103,6 @@ fn main() { Command::new("git") .current_dir(&out_dir_path) .arg("clone") - .arg("--depth") - .arg("1") .arg(QEMU_URL) .status() .unwrap(); @@ -231,6 +220,7 @@ fn main() { for dir in &[ build_dir.join("libcommon.fa.p"), build_dir.join(&format!("libqemu-{}-linux-user.fa.p", cpu_target)), + build_dir.join("libcommon-user.fa.p"), //build_dir.join("libqemuutil.a.p"), //build_dir.join("libqom.fa.p"), //build_dir.join("libhwcore.fa.p"), diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index a1a0445864..a77e07c52f 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -3,8 +3,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::{env, fs, ptr}; use crate::{ - emu, - emu::SyscallHookResult, + emu::{Emulator, SyscallHookResult}, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, Regs, @@ -114,7 +113,7 @@ unsafe extern "C" fn asan_giovese_populate_context(ctx: *mut CallContext, _pc: u static mut ASAN_INITED: bool = false; -pub fn init_with_asan(args: &mut Vec, env: &mut [(String, String)]) -> i32 { +pub fn init_with_asan(args: &mut Vec, env: &mut [(String, String)]) -> Emulator { assert!(!args.is_empty()); let current = env::current_exe().unwrap(); let asan_lib = fs::canonicalize(¤t) @@ -160,7 +159,7 @@ pub fn init_with_asan(args: &mut Vec, env: &mut [(String, String)]) -> i asan_giovese_init(); ASAN_INITED = true; } - emu::init(args, env) + Emulator::new(args, env) } // TODO intrumentation filter @@ -172,7 +171,7 @@ pub struct QemuAsanHelper { impl QemuAsanHelper { #[must_use] pub fn new() -> Self { - assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just init(...)"); + assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)"); Self { enabled: true, filter: QemuInstrumentationFilter::None, @@ -202,7 +201,7 @@ impl QemuAsanHelper { } #[allow(clippy::unused_self)] - pub fn alloc(&mut self, start: u64, end: u64) { + pub fn alloc(&mut self, _emulator: &Emulator, start: u64, end: u64) { unsafe { let ctx: *const CallContext = libc::calloc(core::mem::size_of::(), 1) as *const _; @@ -211,188 +210,188 @@ impl QemuAsanHelper { } #[allow(clippy::unused_self)] - pub fn dealloc(&mut self, addr: u64) { + pub fn dealloc(&mut self, emulator: &Emulator, addr: u64) { unsafe { let ckinfo = asan_giovese_alloc_search(addr); if let Some(ck) = ckinfo.as_mut() { if ck.start != addr { // Free not the start of the chunk - asan_giovese_badfree(addr, emu::read_reg(Regs::Pc).unwrap_or(u64::MAX)); + asan_giovese_badfree(addr, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX)); } let ctx: *const CallContext = libc::calloc(core::mem::size_of::(), 1) as *const _; ck.free_ctx = ctx; } else { // Free of wild ptr - asan_giovese_badfree(addr, emu::read_reg(Regs::Pc).unwrap_or(u64::MAX)); + asan_giovese_badfree(addr, emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX)); } } } #[allow(clippy::unused_self)] #[must_use] - pub fn is_poisoned(&self, addr: u64, size: usize) -> bool { - unsafe { asan_giovese_loadN(emu::g2h(addr), size) != 0 } + pub fn is_poisoned(&self, emulator: &Emulator, addr: u64, size: usize) -> bool { + unsafe { asan_giovese_loadN(emulator.g2h(addr), size) != 0 } } - pub fn read_1(&mut self, addr: u64) { + pub fn read_1(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_load1(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_load1(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, addr, 1, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn read_2(&mut self, addr: u64) { + pub fn read_2(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_load2(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_load2(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, addr, 2, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn read_4(&mut self, addr: u64) { + pub fn read_4(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_load4(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_load4(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, addr, 4, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn read_8(&mut self, addr: u64) { + pub fn read_8(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_load8(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_load8(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 0, addr, 8, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn read_n(&mut self, addr: u64, size: usize) { + pub fn read_n(&mut self, emulator: &Emulator, addr: u64, size: usize) { unsafe { - if self.enabled() && asan_giovese_loadN(emu::g2h(addr), size) != 0 { + if self.enabled() && asan_giovese_loadN(emulator.g2h(addr), size) != 0 { asan_giovese_report_and_crash( 0, addr, size, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn write_1(&mut self, addr: u64) { + pub fn write_1(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_store1(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_store1(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, addr, 1, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn write_2(&mut self, addr: u64) { + pub fn write_2(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_store2(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_store2(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, addr, 2, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn write_4(&mut self, addr: u64) { + pub fn write_4(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_store4(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_store4(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, addr, 4, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn write_8(&mut self, addr: u64) { + pub fn write_8(&mut self, emulator: &Emulator, addr: u64) { unsafe { - if self.enabled() && asan_giovese_store8(emu::g2h(addr)) != 0 { + if self.enabled() && asan_giovese_store8(emulator.g2h(addr)) != 0 { asan_giovese_report_and_crash( 1, addr, 8, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } - pub fn write_n(&mut self, addr: u64, size: usize) { + pub fn write_n(&mut self, emulator: &Emulator, addr: u64, size: usize) { unsafe { - if self.enabled() && asan_giovese_storeN(emu::g2h(addr), size) != 0 { + if self.enabled() && asan_giovese_storeN(emulator.g2h(addr), size) != 0 { asan_giovese_report_and_crash( 1, addr, size, - emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Pc).unwrap_or(u64::MAX), 0, - emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + emulator.read_reg(Regs::Sp).unwrap_or(u64::MAX), ); } } } #[allow(clippy::unused_self)] - pub fn poison(&mut self, addr: u64, size: usize, poison: PoisonKind) { - unsafe { asan_giovese_poison_region(emu::g2h(addr), size, poison.into()) }; + pub fn poison(&mut self, emulator: &Emulator, addr: u64, size: usize, poison: PoisonKind) { + unsafe { asan_giovese_poison_region(emulator.g2h(addr), size, poison.into()) }; } #[allow(clippy::unused_self)] - pub fn unpoison(&mut self, addr: u64, size: usize) { - unsafe { asan_giovese_unpoison_region(emu::g2h(addr), size) }; + pub fn unpoison(&mut self, emulator: &Emulator, addr: u64, size: usize) { + unsafe { asan_giovese_unpoison_region(emulator.g2h(addr), size) }; } #[allow(clippy::unused_self)] @@ -435,13 +434,14 @@ where executor.hook_syscalls(qasan_fake_syscall::); } - fn post_exec(&mut self, _input: &I) { + fn post_exec(&mut self, _emulator: &Emulator, _input: &I) { self.reset(); } } // TODO add pc to generation hooks pub fn gen_readwrite_asan( + _emulator: &Emulator, helpers: &mut QT, _state: &mut S, pc: u64, @@ -459,43 +459,64 @@ where } } -pub fn trace_read1_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_read1_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.read_1(addr); + h.read_1(emulator, addr); } -pub fn trace_read2_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_read2_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.read_2(addr); + h.read_2(emulator, addr); } -pub fn trace_read4_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_read4_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.read_4(addr); + h.read_4(emulator, addr); } -pub fn trace_read8_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_read8_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.read_8(addr); + h.read_8(emulator, addr); } pub fn trace_read_n_asan( + emulator: &Emulator, helpers: &mut QT, _state: &mut S, _id: u64, @@ -506,46 +527,67 @@ pub fn trace_read_n_asan( QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.read_n(addr, size); + h.read_n(emulator, addr, size); } -pub fn trace_write1_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write1_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.write_1(addr); + h.write_1(emulator, addr); } -pub fn trace_write2_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write2_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.write_2(addr); + h.write_2(emulator, addr); } -pub fn trace_write4_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write4_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.write_4(addr); + h.write_4(emulator, addr); } -pub fn trace_write8_asan(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write8_asan( + emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.write_8(addr); + h.write_8(emulator, addr); } pub fn trace_write_n_asan( + emulator: &Emulator, helpers: &mut QT, _state: &mut S, _id: u64, @@ -556,11 +598,12 @@ pub fn trace_write_n_asan( QT: QemuHelperTuple, { let h = helpers.match_first_type_mut::().unwrap(); - h.read_n(addr, size); + h.read_n(emulator, addr, size); } #[allow(clippy::too_many_arguments)] pub fn qasan_fake_syscall( + emulator: &Emulator, helpers: &mut QT, _state: &mut S, sys_num: i32, @@ -582,30 +625,35 @@ where let mut r = 0; match QasanAction::try_from(a0).expect("Invalid QASan action number") { QasanAction::CheckLoad => { - h.read_n(a1, a2 as usize); + h.read_n(emulator, a1, a2 as usize); } QasanAction::CheckStore => { - h.write_n(a1, a2 as usize); + h.write_n(emulator, a1, a2 as usize); } QasanAction::Poison => { - h.poison(a1, a2 as usize, PoisonKind::try_from(a3 as u8).unwrap()); + h.poison( + emulator, + a1, + a2 as usize, + PoisonKind::try_from(a3 as u8).unwrap(), + ); } QasanAction::UserPoison => { - h.poison(a1, a2 as usize, PoisonKind::User); + h.poison(emulator, a1, a2 as usize, PoisonKind::User); } QasanAction::UnPoison => { - h.unpoison(a1, a2 as usize); + h.unpoison(emulator, a1, a2 as usize); } QasanAction::IsPoison => { - if h.is_poisoned(a1, a2 as usize) { + if h.is_poisoned(emulator, a1, a2 as usize) { r = 1; } } QasanAction::Alloc => { - h.alloc(a1, a2); + h.alloc(emulator, a1, a2); } QasanAction::Dealloc => { - h.dealloc(a1); + h.dealloc(emulator, a1); } QasanAction::Enable => { h.set_enabled(true); diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index 223c5c028c..e3c1f11d72 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -6,7 +6,7 @@ pub use libafl_targets::{ use serde::{Deserialize, Serialize}; use crate::{ - emu, + emu::Emulator, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; @@ -70,14 +70,15 @@ where QT: QemuHelperTuple, { executor.hook_cmp_generation(gen_unique_cmp_ids::); - emu::set_exec_cmp8_hook(trace_cmp8_cmplog); - emu::set_exec_cmp4_hook(trace_cmp4_cmplog); - emu::set_exec_cmp2_hook(trace_cmp2_cmplog); - emu::set_exec_cmp1_hook(trace_cmp1_cmplog); + executor.emulator().set_exec_cmp8_hook(trace_cmp8_cmplog); + executor.emulator().set_exec_cmp4_hook(trace_cmp4_cmplog); + executor.emulator().set_exec_cmp2_hook(trace_cmp2_cmplog); + executor.emulator().set_exec_cmp1_hook(trace_cmp1_cmplog); } } pub fn gen_unique_cmp_ids( + _emulator: &Emulator, helpers: &mut QT, state: &mut S, pc: u64, diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 4c5d76c98e..c38a59f283 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use std::{cell::UnsafeCell, cmp::max}; use crate::{ - emu, + emu::Emulator, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; @@ -69,7 +69,7 @@ where QT: QemuHelperTuple, { executor.hook_edge_generation(gen_unique_edge_ids::); - emu::set_exec_edge_hook(trace_edge_hitcount); + executor.emulator().set_exec_edge_hook(trace_edge_hitcount); } } @@ -83,6 +83,7 @@ fn hash_me(mut x: u64) -> u64 { } pub fn gen_unique_edge_ids( + _emulator: &Emulator, helpers: &mut QT, state: &mut S, src: u64, @@ -128,6 +129,7 @@ where } pub fn gen_hashed_edge_ids( + _emulator: &Emulator, helpers: &mut QT, _state: &mut S, src: u64, @@ -157,11 +159,21 @@ pub extern "C" fn trace_edge_single(id: u64) { } } -pub fn gen_addr_block_ids(_helpers: &mut QT, _state: &mut S, pc: u64) -> Option { +pub fn gen_addr_block_ids( + _emulator: &Emulator, + _helpers: &mut QT, + _state: &mut S, + pc: u64, +) -> Option { Some(pc) } -pub fn gen_hashed_block_ids(_helpers: &mut QT, _state: &mut S, pc: u64) -> Option { +pub fn gen_hashed_block_ids( + _emulator: &Emulator, + _helpers: &mut QT, + _state: &mut S, + pc: u64, +) -> Option { Some(hash_me(pc)) } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index bf28803fa0..81ba5e842d 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -226,31 +226,10 @@ extern "C" { static mut libafl_exec_cmp_hook8: unsafe extern "C" fn(u64, u64, u64); static mut libafl_gen_cmp_hook: unsafe extern "C" fn(u64, u32) -> u64; - static mut libafl_syscall_hook: + static mut libafl_pre_syscall_hook: unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult; -} - -#[allow(clippy::must_use_candidate, clippy::similar_names)] -pub fn init(args: &[String], env: &[(String, String)]) -> i32 { - assert!(!args.is_empty()); - let args: Vec = args.iter().map(|x| x.clone() + "\0").collect(); - let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); - assert!(argv.len() < i32::MAX as usize); - let env_strs: Vec = env - .iter() - .map(|(k, v)| format!("{}={}\0", &k, &v)) - .collect(); - let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect(); - envp.push(null()); - #[allow(clippy::cast_possible_wrap)] - let argc = argv.len() as i32; - unsafe { - qemu_user_init( - argc, - argv.as_ptr() as *const *const u8, - envp.as_ptr() as *const *const u8, - ) - } + static mut libafl_post_syscall_hook: + unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64; } #[cfg_attr(feature = "python", pyclass(unsendable))] @@ -259,9 +238,10 @@ pub struct GuestMaps { c_iter: *const c_void, } -impl Default for GuestMaps { +// Consider a private new only for Emulator +impl GuestMaps { #[must_use] - fn default() -> Self { + pub(crate) fn new() -> Self { unsafe { let maps = read_self_maps(); Self { @@ -272,24 +252,6 @@ impl Default for GuestMaps { } } -#[cfg(feature = "python")] -#[pymethods] -impl GuestMaps { - #[new] - #[must_use] - pub fn new() -> Self { - Self::default() - } -} - -#[cfg(not(feature = "python"))] -impl GuestMaps { - #[must_use] - pub fn new() -> Self { - Self::default() - } -} - impl Iterator for GuestMaps { type Item = MapInfo; @@ -329,280 +291,494 @@ impl Drop for GuestMaps { } } -pub fn write_mem(addr: u64, buf: &[T]) { - let host_addr = g2h(addr); - unsafe { +static mut EMULATOR_IS_INITIALIZED: bool = false; + +pub struct Emulator { + _private: (), +} + +#[allow(clippy::unused_self)] +impl Emulator { + #[allow(clippy::must_use_candidate, clippy::similar_names)] + pub fn new(args: &[String], env: &[(String, String)]) -> Emulator { + unsafe { + assert!(!EMULATOR_IS_INITIALIZED); + } + assert!(!args.is_empty()); + let args: Vec = args.iter().map(|x| x.clone() + "\0").collect(); + let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); + assert!(argv.len() < i32::MAX as usize); + let env_strs: Vec = env + .iter() + .map(|(k, v)| format!("{}={}\0", &k, &v)) + .collect(); + let mut envp: Vec<*const u8> = env_strs.iter().map(|x| x.as_bytes().as_ptr()).collect(); + envp.push(null()); + #[allow(clippy::cast_possible_wrap)] + let argc = argv.len() as i32; + unsafe { + qemu_user_init( + argc, + argv.as_ptr() as *const *const u8, + envp.as_ptr() as *const *const u8, + ); + EMULATOR_IS_INITIALIZED = true; + } + Emulator { _private: () } + } + + #[must_use] + pub(crate) fn new_empty() -> Emulator { + Emulator { _private: () } + } + + #[must_use] + pub fn mappings(&self) -> GuestMaps { + GuestMaps::new() + } + + pub unsafe fn write_mem(&self, addr: u64, buf: &[T]) { + let host_addr = self.g2h(addr); copy_nonoverlapping( buf.as_ptr() as *const _ as *const u8, host_addr, buf.len() * size_of::(), ); } -} -pub fn read_mem(addr: u64, buf: &mut [T]) { - let host_addr = g2h(addr); - unsafe { + pub unsafe fn read_mem(&self, addr: u64, buf: &mut [T]) { + let host_addr = self.g2h(addr); copy_nonoverlapping( host_addr as *const u8, buf.as_mut_ptr() as *mut _ as *mut u8, buf.len() * size_of::(), ); } -} -#[must_use] -pub fn num_regs() -> i32 { - unsafe { libafl_qemu_num_regs() } -} - -pub fn write_reg(reg: R, val: T) -> Result<(), String> -where - T: Num + PartialOrd + Copy, - R: Into, -{ - let reg = reg.into(); - let success = unsafe { libafl_qemu_write_reg(reg, &val as *const _ as *const u8) }; - if success == 0 { - Err(format!("Failed to write to register {}", reg)) - } else { - Ok(()) + #[must_use] + pub fn num_regs(&self) -> i32 { + unsafe { libafl_qemu_num_regs() } } -} -pub fn read_reg(reg: R) -> Result -where - T: Num + PartialOrd + Copy, - R: Into, -{ - let reg = reg.into(); - let mut val = T::zero(); - let success = unsafe { libafl_qemu_read_reg(reg, &mut val as *mut _ as *mut u8) }; - if success == 0 { - Err(format!("Failed to read register {}", reg)) - } else { - Ok(val) + pub fn write_reg(&self, reg: R, val: T) -> Result<(), String> + where + T: Num + PartialOrd + Copy, + R: Into, + { + let reg = reg.into(); + let success = unsafe { libafl_qemu_write_reg(reg, &val as *const _ as *const u8) }; + if success == 0 { + Err(format!("Failed to write to register {}", reg)) + } else { + Ok(()) + } } -} -pub fn set_breakpoint(addr: u64) { - unsafe { - libafl_qemu_set_breakpoint(addr); + pub fn read_reg(&self, reg: R) -> Result + where + T: Num + PartialOrd + Copy, + R: Into, + { + let reg = reg.into(); + let mut val = T::zero(); + let success = unsafe { libafl_qemu_read_reg(reg, &mut val as *mut _ as *mut u8) }; + if success == 0 { + Err(format!("Failed to read register {}", reg)) + } else { + Ok(val) + } } -} -pub fn remove_breakpoint(addr: u64) { - unsafe { - libafl_qemu_remove_breakpoint(addr); + pub fn set_breakpoint(&self, addr: u64) { + unsafe { + libafl_qemu_set_breakpoint(addr); + } } -} -pub fn set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) { - unsafe { - libafl_qemu_set_hook(addr, callback, val); + pub fn remove_breakpoint(&self, addr: u64) { + unsafe { + libafl_qemu_remove_breakpoint(addr); + } } -} -pub fn remove_hook(addr: u64) { - unsafe { - libafl_qemu_remove_hook(addr); + pub fn set_hook(&self, addr: u64, callback: extern "C" fn(u64), val: u64) { + unsafe { + libafl_qemu_set_hook(addr, callback, val); + } } -} -pub fn run() { - unsafe { + pub fn remove_hook(&self, addr: u64) { + unsafe { + libafl_qemu_remove_hook(addr); + } + } + + pub unsafe fn run(&self) { libafl_qemu_run(); } + + #[must_use] + pub fn g2h(&self, addr: u64) -> *mut T { + unsafe { transmute(addr + guest_base as u64) } + } + + #[must_use] + pub fn h2g(&self, addr: *const T) -> u64 { + unsafe { (addr as usize - guest_base) as u64 } + } + + #[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) -> u64 { + unsafe { libafl_load_addr() } + } + + #[must_use] + pub fn get_brk(&self) -> u64 { + unsafe { libafl_get_brk() } + } + + pub fn set_brk(&self, brk: u64) { + unsafe { libafl_set_brk(brk) }; + } + + pub fn map_private(&self, addr: u64, size: usize, perms: MmapPerms) -> Result { + let res = unsafe { + target_mmap( + addr, + size as u64, + perms.into(), + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + if res == 0 { + Err(format!("Failed to map {}", addr)) + } else { + Ok(res) + } + } + + pub fn unmap(&self, addr: u64, size: usize) -> Result<(), String> { + if unsafe { target_munmap(addr, size as u64) } == 0 { + Ok(()) + } else { + Err(format!("Failed to unmap {}", addr)) + } + } + + // TODO add has_X_hook() and panic when setting a hook for the second time + + pub fn set_exec_edge_hook(&self, hook: extern "C" fn(id: u64)) { + unsafe { + libafl_exec_edge_hook = hook; + } + } + + pub fn set_gen_edge_hook(&self, hook: extern "C" fn(src: u64, dest: u64) -> u64) { + unsafe { + libafl_gen_edge_hook = hook; + } + } + + pub fn set_exec_block_hook(&self, hook: extern "C" fn(pc: u64)) { + unsafe { + libafl_exec_block_hook = hook; + } + } + + pub fn set_gen_block_hook(&self, hook: extern "C" fn(pc: u64) -> u64) { + unsafe { + libafl_gen_block_hook = hook; + } + } + + pub fn set_exec_read1_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_read_hook1 = hook; + } + } + + pub fn set_exec_read2_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_read_hook2 = hook; + } + } + + pub fn set_exec_read4_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_read_hook4 = hook; + } + } + + pub fn set_exec_read8_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_read_hook8 = hook; + } + } + + pub fn set_exec_read_n_hook(&self, hook: extern "C" fn(id: u64, addr: u64, size: u32)) { + unsafe { + libafl_exec_read_hookN = hook; + } + } + + pub fn set_gen_read_hook(&self, hook: extern "C" fn(size: u32) -> u64) { + unsafe { + libafl_gen_read_hook = hook; + } + } + + pub fn set_exec_write1_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_write_hook1 = hook; + } + } + + pub fn set_exec_write2_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_write_hook2 = hook; + } + } + + pub fn set_exec_write4_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_write_hook4 = hook; + } + } + + pub fn set_exec_write8_hook(&self, hook: extern "C" fn(id: u64, addr: u64)) { + unsafe { + libafl_exec_write_hook8 = hook; + } + } + + pub fn set_exec_write_n_hook(&self, hook: extern "C" fn(id: u64, addr: u64, size: u32)) { + unsafe { + libafl_exec_write_hookN = hook; + } + } + + // TODO add pc arg + pub fn set_gen_write_hook(&self, hook: extern "C" fn(size: u32) -> u64) { + unsafe { + libafl_gen_write_hook = hook; + } + } + + pub fn set_exec_cmp1_hook(&self, hook: extern "C" fn(id: u64, v0: u8, v1: u8)) { + unsafe { + libafl_exec_cmp_hook1 = hook; + } + } + + pub fn set_exec_cmp2_hook(&self, hook: extern "C" fn(id: u64, v0: u16, v1: u16)) { + unsafe { + libafl_exec_cmp_hook2 = hook; + } + } + + pub fn set_exec_cmp4_hook(&self, hook: extern "C" fn(id: u64, v0: u32, v1: u32)) { + unsafe { + libafl_exec_cmp_hook4 = hook; + } + } + + pub fn set_exec_cmp8_hook(&self, hook: extern "C" fn(id: u64, v0: u64, v1: u64)) { + unsafe { + libafl_exec_cmp_hook8 = hook; + } + } + + pub fn set_gen_cmp_hook(&self, hook: extern "C" fn(pc: u64, size: u32) -> u64) { + unsafe { + libafl_gen_cmp_hook = hook; + } + } + + pub fn set_pre_syscall_hook( + &self, + hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult, + ) { + unsafe { + libafl_pre_syscall_hook = hook; + } + } + + pub fn set_post_syscall_hook( + &self, + hook: extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64, + ) { + unsafe { + libafl_post_syscall_hook = hook; + } + } } -#[must_use] -pub fn g2h(addr: u64) -> *mut T { - unsafe { transmute(addr + guest_base as u64) } -} +#[cfg(feature = "python")] +pub mod pybind { + use super::{MmapPerms, SyscallHookResult}; + use core::mem::transmute; + use pyo3::exceptions::PyValueError; + use pyo3::{prelude::*, types::PyInt}; + use std::convert::TryFrom; -#[must_use] -pub fn h2g(addr: *const T) -> u64 { - unsafe { (addr as usize - guest_base) as u64 } -} + static mut PY_SYSCALL_HOOK: Option = None; + static mut PY_GENERIC_HOOKS: Vec<(u64, PyObject)> = vec![]; -#[must_use] -pub fn binary_path<'a>() -> &'a str { - unsafe { from_utf8_unchecked(from_raw_parts(exec_path, strlen(exec_path))) } -} - -#[must_use] -pub fn load_addr() -> u64 { - unsafe { libafl_load_addr() } -} - -#[must_use] -pub fn get_brk() -> u64 { - unsafe { libafl_get_brk() } -} - -pub fn set_brk(brk: u64) { - unsafe { libafl_set_brk(brk) }; -} - -pub fn map_private(addr: u64, size: usize, perms: MmapPerms) -> Result { - let res = unsafe { - target_mmap( - addr, - size as u64, - perms.into(), - libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, - -1, - 0, + extern "C" fn py_syscall_hook_wrapper( + sys_num: i32, + a0: u64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + ) -> SyscallHookResult { + unsafe { PY_SYSCALL_HOOK.as_ref() }.map_or_else( + || SyscallHookResult::new(None), + |obj| { + let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7); + Python::with_gil(|py| { + let ret = obj.call1(py, args).expect("Error in the syscall hook"); + let any = ret.as_ref(py); + if any.is_none() { + SyscallHookResult::new(None) + } else { + let a: Result<&PyInt, _> = any.cast_as(); + if let Ok(i) = a { + SyscallHookResult::new(Some( + i.extract().expect("Invalid syscall hook return value"), + )) + } else { + SyscallHookResult::extract(any) + .expect("The syscall hook must return a SyscallHookResult") + } + } + }) + }, ) - }; - if res == 0 { - Err(format!("Failed to map {}", addr)) - } else { - Ok(res) - } -} - -pub fn unmap(addr: u64, size: usize) -> Result<(), String> { - if unsafe { target_munmap(addr, size as u64) } == 0 { - Ok(()) - } else { - Err(format!("Failed to unmap {}", addr)) - } -} - -// TODO add has_X_hook() and panic when setting a hook for the second time - -pub fn set_exec_edge_hook(hook: extern "C" fn(id: u64)) { - unsafe { - libafl_exec_edge_hook = hook; - } -} - -pub fn set_gen_edge_hook(hook: extern "C" fn(src: u64, dest: u64) -> u64) { - unsafe { - libafl_gen_edge_hook = hook; - } -} - -pub fn set_exec_block_hook(hook: extern "C" fn(pc: u64)) { - unsafe { - libafl_exec_block_hook = hook; - } -} - -pub fn set_gen_block_hook(hook: extern "C" fn(pc: u64) -> u64) { - unsafe { - libafl_gen_block_hook = hook; - } -} - -pub fn set_exec_read1_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook1 = hook; - } -} - -pub fn set_exec_read2_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook2 = hook; - } -} - -pub fn set_exec_read4_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook4 = hook; - } -} - -pub fn set_exec_read8_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_read_hook8 = hook; - } -} - -pub fn set_exec_read_n_hook(hook: extern "C" fn(id: u64, addr: u64, size: u32)) { - unsafe { - libafl_exec_read_hookN = hook; - } -} - -pub fn set_gen_read_hook(hook: extern "C" fn(size: u32) -> u64) { - unsafe { - libafl_gen_read_hook = hook; - } -} - -pub fn set_exec_write1_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook1 = hook; - } -} - -pub fn set_exec_write2_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook2 = hook; - } -} - -pub fn set_exec_write4_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook4 = hook; - } -} - -pub fn set_exec_write8_hook(hook: extern "C" fn(id: u64, addr: u64)) { - unsafe { - libafl_exec_write_hook8 = hook; - } -} - -pub fn set_exec_write_n_hook(hook: extern "C" fn(id: u64, addr: u64, size: u32)) { - unsafe { - libafl_exec_write_hookN = hook; - } -} - -// TODO add pc arg -pub fn set_gen_write_hook(hook: extern "C" fn(size: u32) -> u64) { - unsafe { - libafl_gen_write_hook = hook; - } -} - -pub fn set_exec_cmp1_hook(hook: extern "C" fn(id: u64, v0: u8, v1: u8)) { - unsafe { - libafl_exec_cmp_hook1 = hook; - } -} - -pub fn set_exec_cmp2_hook(hook: extern "C" fn(id: u64, v0: u16, v1: u16)) { - unsafe { - libafl_exec_cmp_hook2 = hook; - } -} - -pub fn set_exec_cmp4_hook(hook: extern "C" fn(id: u64, v0: u32, v1: u32)) { - unsafe { - libafl_exec_cmp_hook4 = hook; - } -} - -pub fn set_exec_cmp8_hook(hook: extern "C" fn(id: u64, v0: u64, v1: u64)) { - unsafe { - libafl_exec_cmp_hook8 = hook; - } -} - -pub fn set_gen_cmp_hook(hook: extern "C" fn(pc: u64, size: u32) -> u64) { - unsafe { - libafl_gen_cmp_hook = hook; - } -} - -pub fn set_syscall_hook( - hook: extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult, -) { - unsafe { - libafl_syscall_hook = hook; + } + + extern "C" fn py_generic_hook_wrapper(idx: u64) { + let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 }; + Python::with_gil(|py| { + obj.call0(py).expect("Error in the hook"); + }); + } + + #[pyclass(unsendable)] + pub struct Emulator { + pub emu: super::Emulator, + } + + #[pymethods] + impl Emulator { + #[allow(clippy::needless_pass_by_value)] + #[new] + fn new(args: Vec, env: Vec<(String, String)>) -> Emulator { + Emulator { + emu: super::Emulator::new(&args, &env), + } + } + + fn write_mem(&self, addr: u64, buf: &[u8]) { + unsafe { + self.emu.write_mem(addr, buf); + } + } + + fn read_mem(&self, addr: u64, size: usize) -> Vec { + let mut buf = vec![0; size]; + unsafe { + self.emu.read_mem(addr, &mut buf); + } + buf + } + + fn num_regs(&self) -> i32 { + self.emu.num_regs() + } + + fn write_reg(&self, reg: i32, val: u64) -> PyResult<()> { + self.emu.write_reg(reg, val).map_err(PyValueError::new_err) + } + + fn read_reg(&self, reg: i32) -> PyResult { + self.emu.read_reg(reg).map_err(PyValueError::new_err) + } + + fn set_breakpoint(&self, addr: u64) { + self.emu.set_breakpoint(addr); + } + + fn remove_breakpoint(&self, addr: u64) { + self.emu.remove_breakpoint(addr); + } + + fn run(&self) { + unsafe { + self.emu.run(); + } + } + + fn g2h(&self, addr: u64) -> u64 { + self.emu.g2h::<*const u8>(addr) as u64 + } + + fn h2g(&self, addr: u64) -> u64 { + self.emu.h2g(unsafe { transmute::<_, *const u8>(addr) }) + } + + fn binary_path(&self) -> String { + self.emu.binary_path().to_owned() + } + + fn load_addr(&self) -> u64 { + self.emu.load_addr() + } + + fn map_private(&self, addr: u64, size: usize, perms: i32) -> PyResult { + if let Ok(p) = MmapPerms::try_from(perms) { + self.emu + .map_private(addr, size, p) + .map_err(PyValueError::new_err) + } else { + Err(PyValueError::new_err("Invalid perms")) + } + } + + fn unmap(&self, addr: u64, size: usize) -> PyResult<()> { + self.emu.unmap(addr, size).map_err(PyValueError::new_err) + } + + fn set_syscall_hook(&self, hook: PyObject) { + unsafe { + PY_SYSCALL_HOOK = Some(hook); + } + self.emu.set_pre_syscall_hook(py_syscall_hook_wrapper); + } + + fn set_hook(&self, addr: u64, hook: PyObject) { + unsafe { + let idx = PY_GENERIC_HOOKS.len(); + PY_GENERIC_HOOKS.push((addr, hook)); + self.emu.set_hook(addr, py_generic_hook_wrapper, idx as u64); + } + } + + fn remove_hook(&self, addr: u64) { + unsafe { + PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr); + } + self.emu.remove_hook(addr); + } } } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 910daeef35..1c0abbc95c 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -15,7 +15,10 @@ use libafl::{ }; pub use crate::emu::SyscallHookResult; -use crate::{emu, emu::SKIP_EXEC_HOOK, helper::QemuHelperTuple}; +use crate::{ + emu::{Emulator, SKIP_EXEC_HOOK}, + helper::QemuHelperTuple, +}; static mut QEMU_HELPERS_PTR: *const c_void = ptr::null(); @@ -28,8 +31,10 @@ where unsafe { let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let state = inprocess_get_state::().unwrap(); - let func: fn(&mut QT, &mut S, u64, u64) -> Option = transmute(GEN_EDGE_HOOK_PTR); - (func)(helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id) + let emulator = Emulator::new_empty(); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) -> Option = + transmute(GEN_EDGE_HOOK_PTR); + (func)(&emulator, helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id) } } @@ -41,9 +46,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &EDGE_HOOKS } { - let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id); + let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id); } } @@ -56,8 +62,9 @@ where unsafe { let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let state = inprocess_get_state::().unwrap(); - let func: fn(&mut QT, &mut S, u64) -> Option = transmute(GEN_EDGE_HOOK_PTR); - (func)(helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id) + let emulator = Emulator::new_empty(); + let func: fn(&Emulator, &mut QT, &mut S, u64) -> Option = transmute(GEN_EDGE_HOOK_PTR); + (func)(&emulator, helpers, state, pc).map_or(SKIP_EXEC_HOOK, |id| id) } } @@ -69,9 +76,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &BLOCK_HOOKS } { - let func: fn(&mut QT, &mut S, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id); + let func: fn(&Emulator, &mut QT, &mut S, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id); } } @@ -84,8 +92,10 @@ where unsafe { let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let state = inprocess_get_state::().unwrap(); - let func: fn(&mut QT, &mut S, usize) -> Option = transmute(GEN_READ_HOOK_PTR); - (func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) + let emulator = Emulator::new_empty(); + let func: fn(&Emulator, &mut QT, &mut S, usize) -> Option = + transmute(GEN_READ_HOOK_PTR); + (func)(&emulator, helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) } } @@ -98,8 +108,10 @@ where unsafe { let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let state = inprocess_get_state::().unwrap(); - let func: fn(&mut QT, &mut S, usize) -> Option = transmute(GEN_WRITE_HOOK_PTR); - (func)(helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) + let emulator = Emulator::new_empty(); + let func: fn(&Emulator, &mut QT, &mut S, usize) -> Option = + transmute(GEN_WRITE_HOOK_PTR); + (func)(&emulator, helpers, state, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) } } @@ -111,9 +123,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &READ1_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -125,9 +138,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &READ2_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -139,9 +153,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &READ4_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -153,9 +168,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &READ8_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -167,9 +183,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &READ_N_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr, size as usize); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr, size as usize); } } @@ -181,9 +198,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE1_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -195,9 +213,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE2_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -209,9 +228,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE4_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -223,9 +243,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE8_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr); } } @@ -237,9 +258,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &WRITE_N_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, addr, size as usize); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64, usize) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, addr, size as usize); } } @@ -252,8 +274,10 @@ where unsafe { let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap(); let state = inprocess_get_state::().unwrap(); - let func: fn(&mut QT, &mut S, u64, usize) -> Option = transmute(GEN_CMP_HOOK_PTR); - (func)(helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) + let emulator = Emulator::new_empty(); + let func: fn(&Emulator, &mut QT, &mut S, u64, usize) -> Option = + transmute(GEN_CMP_HOOK_PTR); + (func)(&emulator, helpers, state, pc, size as usize).map_or(SKIP_EXEC_HOOK, |id| id) } } @@ -265,9 +289,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &CMP1_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, v0, v1); + let func: fn(&Emulator, &mut QT, &mut S, u64, u8, u8) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, v0, v1); } } @@ -279,9 +304,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &CMP2_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, v0, v1); + let func: fn(&Emulator, &mut QT, &mut S, u64, u16, u16) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, v0, v1); } } @@ -293,9 +319,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &CMP4_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, v0, v1); + let func: fn(&Emulator, &mut QT, &mut S, u64, u32, u32) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, v0, v1); } } @@ -307,9 +334,10 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); for hook in unsafe { &CMP8_HOOKS } { - let func: fn(&mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) }; - (func)(helpers, state, id, v0, v1); + let func: fn(&Emulator, &mut QT, &mut S, u64, u64, u64) = unsafe { transmute(*hook) }; + (func)(&emulator, helpers, state, id, v0, v1); } } @@ -331,10 +359,12 @@ where { let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); let mut res = SyscallHookResult::new(None); for hook in unsafe { &SYSCALL_HOOKS } { #[allow(clippy::type_complexity)] let func: fn( + &Emulator, &mut QT, &mut S, i32, @@ -347,7 +377,9 @@ where u64, u64, ) -> SyscallHookResult = unsafe { transmute(*hook) }; - let r = (func)(helpers, state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7); + let r = (func)( + &emulator, helpers, state, sys_num, a0, a1, a2, a3, a4, a5, a6, a7, + ); if r.skip_syscall { res.skip_syscall = true; res.retval = r.retval; @@ -356,6 +388,51 @@ where res } +static mut SYSCALL_POST_HOOKS: Vec<*const c_void> = vec![]; +extern "C" fn syscall_after_hooks_wrapper( + result: u64, + sys_num: i32, + a0: u64, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, +) -> u64 +where + I: Input, + QT: QemuHelperTuple, +{ + let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() }; + let state = inprocess_get_state::().unwrap(); + let emulator = Emulator::new_empty(); + let mut res = result; + for hook in unsafe { &SYSCALL_POST_HOOKS } { + #[allow(clippy::type_complexity)] + let func: fn( + &Emulator, + &mut QT, + &mut S, + u64, + i32, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> u64 = unsafe { transmute(*hook) }; + res = (func)( + &emulator, helpers, state, res, sys_num, a0, a1, a2, a3, a4, a5, a6, a7, + ); + } + res +} + pub struct QemuExecutor<'a, H, I, OT, QT, S> where H: FnMut(&I) -> ExitKind, @@ -364,6 +441,7 @@ where QT: QemuHelperTuple, { helpers: QT, + emulator: &'a Emulator, inner: InProcessExecutor<'a, H, I, OT, S>, } @@ -376,6 +454,7 @@ where { pub fn new( harness_fn: &'a mut H, + emulator: &'a Emulator, helpers: QT, observers: OT, fuzzer: &mut Z, @@ -391,6 +470,7 @@ where { let slf = Self { helpers, + emulator, inner: InProcessExecutor::new(harness_fn, observers, fuzzer, state, event_mgr)?, }; slf.helpers.init_all(&slf); @@ -405,184 +485,230 @@ where &mut self.inner } + pub fn emulator(&self) -> &Emulator { + self.emulator + } + #[allow(clippy::unused_self)] pub fn hook_edge_generation( &self, - hook: fn(&mut QT, &mut S, src: u64, dest: u64) -> Option, + hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64) -> Option, ) { unsafe { GEN_EDGE_HOOK_PTR = hook as *const _; } - emu::set_gen_edge_hook(gen_edge_hook_wrapper::); + self.emulator + .set_gen_edge_hook(gen_edge_hook_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_edge_execution(&self, hook: fn(&mut QT, &mut S, id: u64)) { + pub fn hook_edge_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64)) { unsafe { EDGE_HOOKS.push(hook as *const _); } - emu::set_exec_edge_hook(edge_hooks_wrapper::); + self.emulator + .set_exec_edge_hook(edge_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_block_generation(&self, hook: fn(&mut QT, &mut S, pc: u64) -> Option) { + pub fn hook_block_generation( + &self, + hook: fn(&Emulator, &mut QT, &mut S, pc: u64) -> Option, + ) { unsafe { GEN_BLOCK_HOOK_PTR = hook as *const _; } - emu::set_gen_block_hook(gen_block_hook_wrapper::); + self.emulator + .set_gen_block_hook(gen_block_hook_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_block_execution(&self, hook: fn(&mut QT, &mut S, id: u64)) { + pub fn hook_block_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64)) { unsafe { BLOCK_HOOKS.push(hook as *const _); } - emu::set_exec_block_hook(block_hooks_wrapper::); + self.emulator + .set_exec_block_hook(block_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_read_generation(&self, hook: fn(&mut QT, &mut S, size: usize) -> Option) { + pub fn hook_read_generation( + &self, + hook: fn(&Emulator, &mut QT, &mut S, size: usize) -> Option, + ) { unsafe { GEN_READ_HOOK_PTR = hook as *const _; } - emu::set_gen_read_hook(gen_read_hook_wrapper::); + self.emulator + .set_gen_read_hook(gen_read_hook_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_read1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read1_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { READ1_HOOKS.push(hook as *const _); } - emu::set_exec_read1_hook(read1_hooks_wrapper::); + self.emulator + .set_exec_read1_hook(read1_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_read2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read2_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { READ2_HOOKS.push(hook as *const _); } - emu::set_exec_read2_hook(read2_hooks_wrapper::); + self.emulator + .set_exec_read2_hook(read2_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_read4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read4_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { READ4_HOOKS.push(hook as *const _); } - emu::set_exec_read4_hook(read4_hooks_wrapper::); + self.emulator + .set_exec_read4_hook(read4_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_read8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_read8_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { READ8_HOOKS.push(hook as *const _); } - emu::set_exec_read8_hook(read8_hooks_wrapper::); + self.emulator + .set_exec_read8_hook(read8_hooks_wrapper::); } #[allow(clippy::unused_self)] pub fn hook_read_n_execution( &self, - hook: fn(&mut QT, &mut S, id: u64, addr: u64, size: usize), + hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64, size: usize), ) { unsafe { READ_N_HOOKS.push(hook as *const _); } - emu::set_exec_read_n_hook(read_n_hooks_wrapper::); + self.emulator + .set_exec_read_n_hook(read_n_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_write_generation(&self, hook: fn(&mut QT, &mut S, size: usize) -> Option) { + pub fn hook_write_generation( + &self, + hook: fn(&Emulator, &mut QT, &mut S, size: usize) -> Option, + ) { unsafe { GEN_WRITE_HOOK_PTR = hook as *const _; } - emu::set_gen_write_hook(gen_write_hook_wrapper::); + self.emulator + .set_gen_write_hook(gen_write_hook_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_write1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write1_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { WRITE1_HOOKS.push(hook as *const _); } - emu::set_exec_write1_hook(write1_hooks_wrapper::); + self.emulator + .set_exec_write1_hook(write1_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_write2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write2_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { WRITE2_HOOKS.push(hook as *const _); } - emu::set_exec_write2_hook(write2_hooks_wrapper::); + self.emulator + .set_exec_write2_hook(write2_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_write4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write4_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { WRITE4_HOOKS.push(hook as *const _); } - emu::set_exec_write4_hook(write4_hooks_wrapper::); + self.emulator + .set_exec_write4_hook(write4_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_write8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, addr: u64)) { + pub fn hook_write8_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64)) { unsafe { WRITE8_HOOKS.push(hook as *const _); } - emu::set_exec_write8_hook(write8_hooks_wrapper::); + self.emulator + .set_exec_write8_hook(write8_hooks_wrapper::); } #[allow(clippy::unused_self)] pub fn hook_write_n_execution( &self, - hook: fn(&mut QT, &mut S, id: u64, addr: u64, size: usize), + hook: fn(&Emulator, &mut QT, &mut S, id: u64, addr: u64, size: usize), ) { unsafe { WRITE_N_HOOKS.push(hook as *const _); } - emu::set_exec_write_n_hook(write_n_hooks_wrapper::); + self.emulator + .set_exec_write_n_hook(write_n_hooks_wrapper::); } #[allow(clippy::unused_self)] pub fn hook_cmp_generation( &self, - hook: fn(&mut QT, &mut S, pc: u64, size: usize) -> Option, + hook: fn(&Emulator, &mut QT, &mut S, pc: u64, size: usize) -> Option, ) { unsafe { GEN_CMP_HOOK_PTR = hook as *const _; } - emu::set_gen_cmp_hook(gen_cmp_hook_wrapper::); + self.emulator + .set_gen_cmp_hook(gen_cmp_hook_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_cmp1_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u8, v1: u8)) { + pub fn hook_cmp1_execution( + &self, + hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u8, v1: u8), + ) { unsafe { CMP1_HOOKS.push(hook as *const _); } - emu::set_exec_cmp1_hook(cmp1_hooks_wrapper::); + self.emulator + .set_exec_cmp1_hook(cmp1_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_cmp2_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u16, v1: u16)) { + pub fn hook_cmp2_execution( + &self, + hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u16, v1: u16), + ) { unsafe { CMP2_HOOKS.push(hook as *const _); } - emu::set_exec_cmp2_hook(cmp2_hooks_wrapper::); + self.emulator + .set_exec_cmp2_hook(cmp2_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_cmp4_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u32, v1: u32)) { + pub fn hook_cmp4_execution( + &self, + hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u32, v1: u32), + ) { unsafe { CMP4_HOOKS.push(hook as *const _); } - emu::set_exec_cmp4_hook(cmp4_hooks_wrapper::); + self.emulator + .set_exec_cmp4_hook(cmp4_hooks_wrapper::); } #[allow(clippy::unused_self)] - pub fn hook_cmp8_execution(&self, hook: fn(&mut QT, &mut S, id: u64, v0: u64, v1: u64)) { + pub fn hook_cmp8_execution( + &self, + hook: fn(&Emulator, &mut QT, &mut S, id: u64, v0: u64, v1: u64), + ) { unsafe { CMP8_HOOKS.push(hook as *const _); } - emu::set_exec_cmp8_hook(cmp8_hooks_wrapper::); + self.emulator + .set_exec_cmp8_hook(cmp8_hooks_wrapper::); } #[allow(clippy::unused_self)] @@ -590,6 +716,7 @@ where pub fn hook_syscalls( &self, hook: fn( + &Emulator, &mut QT, &mut S, sys_num: i32, @@ -606,7 +733,35 @@ where unsafe { SYSCALL_HOOKS.push(hook as *const _); } - emu::set_syscall_hook(syscall_hooks_wrapper::); + self.emulator + .set_pre_syscall_hook(syscall_hooks_wrapper::); + } + + #[allow(clippy::unused_self)] + #[allow(clippy::type_complexity)] + pub fn hook_after_syscalls( + &self, + hook: fn( + &Emulator, + &mut QT, + &mut S, + result: u64, + sys_num: i32, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + u64, + ) -> u64, + ) { + unsafe { + SYSCALL_POST_HOOKS.push(hook as *const _); + } + self.emulator + .set_post_syscall_hook(syscall_after_hooks_wrapper::); } } @@ -625,9 +780,9 @@ where input: &I, ) -> Result { unsafe { QEMU_HELPERS_PTR = &self.helpers as *const _ as *const c_void }; - self.helpers.pre_exec_all(input); + self.helpers.pre_exec_all(self.emulator, input); let r = self.inner.run_target(fuzzer, state, mgr, input); - self.helpers.post_exec_all(input); + self.helpers.post_exec_all(self.emulator, input); unsafe { QEMU_HELPERS_PTR = ptr::null() }; r } diff --git a/libafl_qemu/src/helper.rs b/libafl_qemu/src/helper.rs index 3421c55e9d..d8faee25ba 100644 --- a/libafl_qemu/src/helper.rs +++ b/libafl_qemu/src/helper.rs @@ -3,7 +3,7 @@ use libafl::{ }; use std::ops::Range; -use crate::executor::QemuExecutor; +use crate::{emu::Emulator, executor::QemuExecutor}; // TODO remove 'static when specialization will be stable pub trait QemuHelper: 'static @@ -18,9 +18,9 @@ where { } - fn pre_exec(&mut self, _input: &I) {} + fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) {} - fn post_exec(&mut self, _input: &I) {} + fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {} } pub trait QemuHelperTuple: MatchFirstType @@ -33,9 +33,9 @@ where OT: ObserversTuple, QT: QemuHelperTuple; - fn pre_exec_all(&mut self, input: &I); + fn pre_exec_all(&mut self, _emulator: &Emulator, input: &I); - fn post_exec_all(&mut self, input: &I); + fn post_exec_all(&mut self, _emulator: &Emulator, input: &I); } impl QemuHelperTuple for () @@ -50,9 +50,9 @@ where { } - fn pre_exec_all(&mut self, _input: &I) {} + fn pre_exec_all(&mut self, _emulator: &Emulator, _input: &I) {} - fn post_exec_all(&mut self, _input: &I) {} + fn post_exec_all(&mut self, _emulator: &Emulator, _input: &I) {} } impl QemuHelperTuple for (Head, Tail) @@ -71,14 +71,14 @@ where self.1.init_all(executor); } - fn pre_exec_all(&mut self, input: &I) { - self.0.pre_exec(input); - self.1.pre_exec_all(input); + fn pre_exec_all(&mut self, emulator: &Emulator, input: &I) { + self.0.pre_exec(emulator, input); + self.1.pre_exec_all(emulator, input); } - fn post_exec_all(&mut self, input: &I) { - self.0.post_exec(input); - self.1.post_exec_all(input); + fn post_exec_all(&mut self, emulator: &Emulator, input: &I) { + self.0.post_exec(emulator, input); + self.1.post_exec_all(emulator, input); } } diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index a70c76d3cd..499c82d3fd 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -1,40 +1,23 @@ use std::env; -#[cfg(feature = "aarch64")] +#[cfg(cpu_target = "aarch64")] pub mod aarch64; -#[cfg(all(feature = "aarch64", not(feature = "clippy")))] +#[cfg(all(cpu_target = "aarch64", not(feature = "clippy")))] pub use aarch64::*; -#[cfg(feature = "arm")] +#[cfg(cpu_target = "arm")] pub mod arm; -#[cfg(all(feature = "arm", not(feature = "clippy")))] +#[cfg(all(cpu_target = "arm", not(feature = "clippy")))] pub use arm::*; -#[cfg(feature = "i386")] +#[cfg(cpu_target = "i386")] pub mod i386; -#[cfg(all(feature = "i386", not(feature = "clippy")))] +#[cfg(all(cpu_target = "i386", not(feature = "clippy")))] pub use i386::*; -// We default to x86_64, having a default makes CI easier :) -#[cfg(any( - feature = "x86_64", - not(any( - feature = "arm", - feature = "aarch64", - feature = "i386", - feature = "x86_64" - )) -))] +#[cfg(cpu_target = "x86_64")] pub mod x86_64; -#[cfg(any( - feature = "x86_64", - not(any( - feature = "arm", - feature = "aarch64", - feature = "i386", - feature = "x86_64" - )) -))] +#[cfg(cpu_target = "x86_64")] pub use x86_64::*; pub mod elf; @@ -89,159 +72,15 @@ pub fn filter_qemu_args() -> Vec { } #[cfg(all(target_os = "linux", feature = "python"))] -use pyo3::{prelude::*, types::PyInt}; - -#[cfg(all(target_os = "linux", feature = "python"))] -static mut PY_SYSCALL_HOOK: Option = None; - -#[cfg(all(target_os = "linux", feature = "python"))] -static mut PY_GENERIC_HOOKS: Vec<(u64, PyObject)> = vec![]; +use pyo3::prelude::*; #[cfg(all(target_os = "linux", feature = "python"))] #[pymodule] #[pyo3(name = "libafl_qemu")] #[allow(clippy::items_after_statements, clippy::too_many_lines)] pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> { - use core::mem::transmute; - use pyo3::exceptions::PyValueError; - use std::convert::TryFrom; use strum::IntoEnumIterator; - #[pyfn(m)] - #[allow(clippy::needless_pass_by_value)] - fn init(args: Vec, env: Vec<(String, String)>) -> i32 { - emu::init(&args, &env) - } - - #[pyfn(m)] - #[allow(clippy::needless_pass_by_value)] - fn write_mem(addr: u64, buf: &[u8]) { - emu::write_mem(addr, buf); - } - #[pyfn(m)] - fn read_mem(addr: u64, size: usize) -> Vec { - let mut buf = vec![0; size]; - emu::read_mem(addr, &mut buf); - buf - } - #[pyfn(m)] - fn num_regs() -> i32 { - emu::num_regs() - } - #[pyfn(m)] - fn write_reg(reg: i32, val: u64) -> PyResult<()> { - emu::write_reg(reg, val).map_err(PyValueError::new_err) - } - #[pyfn(m)] - fn read_reg(reg: i32) -> PyResult { - emu::read_reg(reg).map_err(PyValueError::new_err) - } - #[pyfn(m)] - fn set_breakpoint(addr: u64) { - emu::set_breakpoint(addr); - } - #[pyfn(m)] - fn remove_breakpoint(addr: u64) { - emu::remove_breakpoint(addr); - } - #[pyfn(m)] - fn run() { - emu::run(); - } - #[pyfn(m)] - fn g2h(addr: u64) -> u64 { - emu::g2h::<*const u8>(addr) as u64 - } - #[pyfn(m)] - fn h2g(addr: u64) -> u64 { - emu::h2g(unsafe { transmute::<_, *const u8>(addr) }) - } - #[pyfn(m)] - fn binary_path() -> String { - emu::binary_path().to_owned() - } - #[pyfn(m)] - fn load_addr() -> u64 { - emu::load_addr() - } - #[pyfn(m)] - fn map_private(addr: u64, size: usize, perms: i32) -> PyResult { - if let Ok(p) = MmapPerms::try_from(perms) { - emu::map_private(addr, size, p).map_err(PyValueError::new_err) - } else { - Err(PyValueError::new_err("Invalid perms")) - } - } - #[pyfn(m)] - fn unmap(addr: u64, size: usize) -> PyResult<()> { - emu::unmap(addr, size).map_err(PyValueError::new_err) - } - - extern "C" fn py_syscall_hook_wrapper( - sys_num: i32, - a0: u64, - a1: u64, - a2: u64, - a3: u64, - a4: u64, - a5: u64, - a6: u64, - a7: u64, - ) -> SyscallHookResult { - unsafe { PY_SYSCALL_HOOK.as_ref() }.map_or_else( - || SyscallHookResult::new(None), - |obj| { - let args = (sys_num, a0, a1, a2, a3, a4, a5, a6, a7); - Python::with_gil(|py| { - let ret = obj.call1(py, args).expect("Error in the syscall hook"); - let any = ret.as_ref(py); - if any.is_none() { - SyscallHookResult::new(None) - } else { - let a: Result<&PyInt, _> = any.cast_as(); - if let Ok(i) = a { - SyscallHookResult::new(Some( - i.extract().expect("Invalid syscall hook return value"), - )) - } else { - SyscallHookResult::extract(any) - .expect("The syscall hook must return a SyscallHookResult") - } - } - }) - }, - ) - } - #[pyfn(m)] - fn set_syscall_hook(hook: PyObject) { - unsafe { - PY_SYSCALL_HOOK = Some(hook); - } - emu::set_syscall_hook(py_syscall_hook_wrapper); - } - - extern "C" fn py_generic_hook_wrapper(idx: u64) { - let obj = unsafe { &PY_GENERIC_HOOKS[idx as usize].1 }; - Python::with_gil(|py| { - obj.call0(py).expect("Error in the hook"); - }); - } - #[pyfn(m)] - fn set_hook(addr: u64, hook: PyObject) { - unsafe { - let idx = PY_GENERIC_HOOKS.len(); - PY_GENERIC_HOOKS.push((addr, hook)); - emu::set_hook(addr, py_generic_hook_wrapper, idx as u64); - } - } - #[pyfn(m)] - fn remove_hook(addr: u64) { - unsafe { - PY_GENERIC_HOOKS.retain(|(a, _)| *a != addr); - } - emu::remove_hook(addr); - } - let regsm = PyModule::new(py, "regs")?; for r in Regs::iter() { let v: i32 = r.into(); @@ -259,6 +98,7 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; Ok(()) } diff --git a/libafl_qemu/src/snapshot.rs b/libafl_qemu/src/snapshot.rs index 85b1f024ed..2948ae2e66 100644 --- a/libafl_qemu/src/snapshot.rs +++ b/libafl_qemu/src/snapshot.rs @@ -2,11 +2,10 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat use std::collections::HashMap; use crate::{ - emu, - emu::{GuestMaps, SyscallHookResult}, + emu::Emulator, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple}, - SYS_mmap, + SYS_mmap, SYS_mremap, }; pub const SNAPSHOT_PAGE_SIZE: usize = 4096; @@ -42,10 +41,10 @@ impl QemuSnapshotHelper { } } - pub fn snapshot(&mut self) { - self.brk = emu::get_brk(); + pub fn snapshot(&mut self, emulator: &Emulator) { + self.brk = emulator.get_brk(); self.pages.clear(); - for map in GuestMaps::new() { + for map in emulator.mappings() { // TODO track all the pages OR track mproctect if !map.flags().is_w() { continue; @@ -57,7 +56,7 @@ impl QemuSnapshotHelper { dirty: false, data: [0; SNAPSHOT_PAGE_SIZE], }; - emu::read_mem(addr, &mut info.data); + unsafe { emulator.read_mem(addr, &mut info.data) }; self.pages.insert(addr, info); addr += SNAPSHOT_PAGE_SIZE as u64; } @@ -94,26 +93,26 @@ impl QemuSnapshotHelper { } } - pub fn reset(&mut self) { + pub fn reset(&mut self, emulator: &Emulator) { self.access_cache = [u64::MAX; 4]; self.access_cache_idx = 0; while let Some(page) = self.dirty.pop() { if let Some(info) = self.pages.get_mut(&page) { - emu::write_mem(page, &info.data); + unsafe { emulator.write_mem(page, &info.data) }; info.dirty = false; } } - emu::set_brk(self.brk); - self.reset_maps(); + emulator.set_brk(self.brk); + self.reset_maps(emulator); } pub fn add_mapped(&mut self, start: u64, size: usize) { self.new_maps.push((start, size)); } - pub fn reset_maps(&mut self) { + pub fn reset_maps(&mut self, emulator: &Emulator) { for (addr, size) in &self.new_maps { - drop(emu::unmap(*addr, *size)); + drop(emulator.unmap(*addr, *size)); } self.new_maps.clear(); } @@ -142,20 +141,25 @@ where executor.hook_write1_execution(trace_write1_snapshot::); executor.hook_write_n_execution(trace_write_n_snapshot::); - executor.hook_syscalls(trace_mmap_snapshot::); + executor.hook_after_syscalls(trace_mmap_snapshot::); } - fn pre_exec(&mut self, _input: &I) { + fn pre_exec(&mut self, emulator: &Emulator, _input: &I) { if self.empty { - self.snapshot(); + self.snapshot(emulator); } else { - self.reset(); + self.reset(emulator); } } } -pub fn trace_write1_snapshot(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write1_snapshot( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { @@ -165,8 +169,13 @@ where h.access(addr, 1); } -pub fn trace_write2_snapshot(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write2_snapshot( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { @@ -176,8 +185,13 @@ where h.access(addr, 2); } -pub fn trace_write4_snapshot(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write4_snapshot( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { @@ -187,8 +201,13 @@ where h.access(addr, 4); } -pub fn trace_write8_snapshot(helpers: &mut QT, _state: &mut S, _id: u64, addr: u64) -where +pub fn trace_write8_snapshot( + _emulator: &Emulator, + helpers: &mut QT, + _state: &mut S, + _id: u64, + addr: u64, +) where I: Input, QT: QemuHelperTuple, { @@ -199,6 +218,7 @@ where } pub fn trace_write_n_snapshot( + _emulator: &Emulator, helpers: &mut QT, _state: &mut S, _id: u64, @@ -216,27 +236,39 @@ pub fn trace_write_n_snapshot( #[allow(clippy::too_many_arguments)] pub fn trace_mmap_snapshot( + _emulator: &Emulator, helpers: &mut QT, _state: &mut S, + result: u64, sys_num: i32, a0: u64, a1: u64, - _a2: u64, + a2: u64, _a3: u64, _a4: u64, _a5: u64, _a6: u64, _a7: u64, -) -> SyscallHookResult +) -> u64 where I: Input, QT: QemuHelperTuple, { + if result == u64::MAX + /* -1 */ + { + return result; + } if i64::from(sys_num) == SYS_mmap { let h = helpers .match_first_type_mut::() .unwrap(); - h.add_mapped(a0, a1 as usize); + h.add_mapped(result, a1 as usize); + } else if i64::from(sys_num) == SYS_mremap { + let h = helpers + .match_first_type_mut::() + .unwrap(); + h.add_mapped(a0, a2 as usize); } - SyscallHookResult::new(None) + result } diff --git a/libafl_sugar/Cargo.toml b/libafl_sugar/Cargo.toml index 63a8b3571b..cece221df0 100644 --- a/libafl_sugar/Cargo.toml +++ b/libafl_sugar/Cargo.toml @@ -15,14 +15,21 @@ build = "build.rs" python = ["pyo3", "libafl_qemu/python", "pyo3-build-config"] default = [] +# for libafl_qemu +# The following architecture features are mutually exclusive. +x86_64 = ["libafl_qemu/x86_64"] # build qemu for x86_64 (default) +i386 = ["libafl_qemu/i386"] # build qemu for i386 +arm = ["libafl_qemu/arm"] # build qemu for arm +aarch64 = ["libafl_qemu/aarch64"] # build qemu for aarch64 + [build-dependencies] pyo3-build-config = { version = "0.14.5", optional = true } [dependencies] libafl = { path = "../libafl", version = "0.7.0" } libafl_targets = { path = "../libafl_targets", version = "0.7.0" } -libafl_qemu = { path = "../libafl_qemu", version = "0.7.0", features = ["x86_64"] } -typed-builder = "0.9.0" # Implement the builder pattern at compiletime +libafl_qemu = { path = "../libafl_qemu", version = "0.7.0" } +typed-builder = "0.9.1" # Implement the builder pattern at compiletime #pyo3 = { version = "0.15", features = ["extension-module"], optional = true } pyo3 = { version = "0.15", optional = true } diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index 1da4d27ced..ca1eea390b 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -30,7 +30,7 @@ use libafl::{ state::{HasCorpus, HasMetadata, StdState}, }; -pub use libafl_qemu::emu; +pub use libafl_qemu::emu::Emulator; use libafl_qemu::{cmplog, edges, QemuCmpLogHelper, QemuEdgeCoverageHelper, QemuExecutor}; use libafl_targets::CmpLogObserver; @@ -75,7 +75,7 @@ where H: FnMut(&[u8]), { #[allow(clippy::too_many_lines, clippy::similar_names)] - pub fn run(&mut self) { + pub fn run(&mut self, emulator: &Emulator) { let conf = match self.configuration.as_ref() { Some(name) => EventConfig::from_name(name), None => EventConfig::AlwaysUnique, @@ -172,6 +172,7 @@ where if self.use_cmplog { let executor = QemuExecutor::new( &mut harness, + emulator, tuple_list!(QemuEdgeCoverageHelper::new(), QemuCmpLogHelper::new()), tuple_list!(edges_observer, time_observer), &mut fuzzer, @@ -247,6 +248,7 @@ where } else { let executor = QemuExecutor::new( &mut harness, + emulator, tuple_list!(QemuEdgeCoverageHelper::new()), tuple_list!(edges_observer, time_observer), &mut fuzzer, @@ -332,6 +334,7 @@ where pub mod pybind { use crate::qemu; use libafl::bolts::os::Cores; + use libafl_qemu::emu::pybind::Emulator; use pyo3::prelude::*; use pyo3::types::PyBytes; use std::path::PathBuf; @@ -362,7 +365,7 @@ pub mod pybind { } #[allow(clippy::needless_pass_by_value)] - pub fn run(&self, harness: PyObject) { + pub fn run(&self, emulator: &Emulator, harness: PyObject) { qemu::QemuBytesCoverageSugar::builder() .input_dirs(&self.input_dirs) .output_dir(self.output_dir.clone()) @@ -377,7 +380,7 @@ pub mod pybind { .unwrap(); }) .build() - .run(); + .run(&emulator.emu); } }