diff --git a/fuzzers/fuzzbench_qemu/src/fuzzer.rs b/fuzzers/fuzzbench_qemu/src/fuzzer.rs index 18e8e27353..331f5e3b7d 100644 --- a/fuzzers/fuzzbench_qemu/src/fuzzer.rs +++ b/fuzzers/fuzzbench_qemu/src/fuzzer.rs @@ -40,7 +40,6 @@ use libafl::{ Error, }; use libafl_qemu::{ - amd64::Amd64Regs, asan::QemuAsanHelper, cmplog, cmplog::{CmpLogObserver, QemuCmpLogHelper}, @@ -49,7 +48,7 @@ use libafl_qemu::{ elf::EasyElf, emu, filter_qemu_args, init_with_asan, snapshot::QemuSnapshotHelper, - MmapPerms, QemuExecutor, + MmapPerms, QemuExecutor, Regs, }; /// The fuzzer main @@ -175,10 +174,10 @@ fn fuzz( println!( "Break at {:#x}", - emu::read_reg::<_, u64>(Amd64Regs::Rip).unwrap() + emu::read_reg::<_, u64>(Regs::Rip).unwrap() ); - let stack_ptr: u64 = emu::read_reg(Amd64Regs::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); let ret_addr = ret_addr[0]; @@ -293,10 +292,10 @@ fn fuzz( emu::write_mem(input_addr, buf); - emu::write_reg(Amd64Regs::Rdi, input_addr).unwrap(); - emu::write_reg(Amd64Regs::Rsi, len).unwrap(); - emu::write_reg(Amd64Regs::Rip, test_one_input_ptr).unwrap(); - emu::write_reg(Amd64Regs::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(); diff --git a/libafl_qemu/build.rs b/libafl_qemu/build.rs index cbb8450695..a8692d877d 100644 --- a/libafl_qemu/build.rs +++ b/libafl_qemu/build.rs @@ -32,6 +32,8 @@ fn main() { "cc".to_owned() }); + println!("cargo:rustc-cfg=cpu_target=\"{}\"", cpu_target); + let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = out_dir.to_string_lossy().to_string(); let out_dir_path = Path::new(&out_dir); diff --git a/libafl_qemu/src/aarch64.rs b/libafl_qemu/src/aarch64.rs index 1f196083ac..2ee3bee777 100644 --- a/libafl_qemu/src/aarch64.rs +++ b/libafl_qemu/src/aarch64.rs @@ -6,7 +6,7 @@ use pyo3::prelude::*; #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] #[repr(i32)] -pub enum Aarch64Regs { +pub enum Regs { X0 = 0, X1 = 1, X2 = 2, @@ -38,17 +38,20 @@ pub enum Aarch64Regs { X28 = 28, X29 = 29, X30 = 30, + Sp = 31, + Pc = 32, + Pstate = 33, } /// alias registers #[allow(non_upper_case_globals)] -impl Aarch64Regs { - pub const Fp: Aarch64Regs = Aarch64Regs::X29; - pub const Lr: Aarch64Regs = Aarch64Regs::X30; +impl Regs { + pub const Fp: Regs = Regs::X29; + pub const Lr: Regs = Regs::X30; } #[cfg(feature = "python")] -impl IntoPy for Aarch64Regs { +impl IntoPy for Regs { fn into_py(self, py: Python) -> PyObject { let n: i32 = self.into(); n.into_py(py) diff --git a/libafl_qemu/src/arm.rs b/libafl_qemu/src/arm.rs index d15f716a47..412766bcb0 100644 --- a/libafl_qemu/src/arm.rs +++ b/libafl_qemu/src/arm.rs @@ -7,7 +7,7 @@ use pyo3::prelude::*; /// Registers for the ARM instruction set. #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] #[repr(i32)] -pub enum ArmRegs { +pub enum Regs { R0 = 0, R1 = 1, R2 = 2, @@ -28,18 +28,18 @@ pub enum ArmRegs { /// alias registers #[allow(non_upper_case_globals)] -impl ArmRegs { - pub const Sp: ArmRegs = ArmRegs::R13; - pub const Lr: ArmRegs = ArmRegs::R14; - pub const Pc: ArmRegs = ArmRegs::R15; - pub const Sb: ArmRegs = ArmRegs::R9; - pub const Sl: ArmRegs = ArmRegs::R10; - pub const Fp: ArmRegs = ArmRegs::R11; - pub const Ip: ArmRegs = ArmRegs::R12; +impl Regs { + pub const Sp: Regs = Regs::R13; + pub const Lr: Regs = Regs::R14; + pub const Pc: Regs = Regs::R15; + pub const Sb: Regs = Regs::R9; + pub const Sl: Regs = Regs::R10; + pub const Fp: Regs = Regs::R11; + pub const Ip: Regs = Regs::R12; } #[cfg(feature = "python")] -impl IntoPy for ArmRegs { +impl IntoPy for Regs { fn into_py(self, py: Python) -> PyObject { let n: i32 = self.into(); n.into_py(py) diff --git a/libafl_qemu/src/asan-giovese.c b/libafl_qemu/src/asan-giovese.c index fbf70c099f..333b08f916 100644 --- a/libafl_qemu/src/asan-giovese.c +++ b/libafl_qemu/src/asan-giovese.c @@ -1375,7 +1375,7 @@ int asan_giovese_report_and_crash(int access_type, target_ulong addr, size_t n, "==%d==ABORTING\n", getpid()); - signal(SIGABRT, SIG_DFL); + // signal(SIGABRT, SIG_DFL); abort(); } @@ -1491,7 +1491,7 @@ int asan_giovese_badfree(target_ulong addr, target_ulong pc) { ": bad-free %s\n", printable_pc); fprintf(stderr, "==%d==ABORTING\n", getpid()); - signal(SIGABRT, SIG_DFL); + //signal(SIGABRT, SIG_DFL); abort(); } diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index 0de137cd5b..a1a0445864 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -1,12 +1,13 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use std::{env, fs}; +use std::{env, fs, ptr}; use crate::{ emu, emu::SyscallHookResult, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + Regs, }; // TODO at some point, merge parts with libafl_frida @@ -58,7 +59,7 @@ pub enum PoisonKind { #[repr(C)] struct CallContext { pub addresses: *const u64, - pub tid: u32, + pub tid: i32, pub size: u32, } @@ -80,20 +81,35 @@ extern "C" { fn asan_giovese_store2(ptr: *const u8) -> i32; fn asan_giovese_store4(ptr: *const u8) -> i32; fn asan_giovese_store8(ptr: *const u8) -> i32; - // int asan_giovese_loadN(void* ptr, size_t n); fn asan_giovese_loadN(ptr: *const u8, n: usize) -> i32; - // int asan_giovese_storeN(void* ptr, size_t n); fn asan_giovese_storeN(ptr: *const u8, n: usize) -> i32; - // int asan_giovese_poison_region(void* ptr, size_t n, uint8_t poison_byte); fn asan_giovese_poison_region(ptr: *const u8, n: usize, poison: u8) -> i32; - // int asan_giovese_unpoison_region(void* ptr, size_t n); fn asan_giovese_unpoison_region(ptr: *const u8, n: usize) -> i32; - // struct chunk_info* asan_giovese_alloc_search(target_ulong query); fn asan_giovese_alloc_search(query: u64) -> *mut ChunkInfo; - // void asan_giovese_alloc_remove(target_ulong start, target_ulong end); fn asan_giovese_alloc_remove(start: u64, end: u64); - // void asan_giovese_alloc_insert(target_ulong start, target_ulong end, struct call_context* alloc_ctx); fn asan_giovese_alloc_insert(start: u64, end: u64, alloc_ctx: *const CallContext); + fn asan_giovese_report_and_crash( + access_type: i32, + addr: u64, + n: usize, + pc: u64, + bp: u64, + sp: u64, + ); + fn asan_giovese_badfree(addr: u64, pc: u64); +} + +#[no_mangle] +extern "C" fn asan_giovese_printaddr(_addr: u64) -> *const u8 { + // Just addresses ATM + ptr::null() +} + +#[no_mangle] +unsafe extern "C" fn asan_giovese_populate_context(ctx: *mut CallContext, _pc: u64) { + let ctx = ctx.as_mut().unwrap(); + ctx.tid = libc::gettid() as i32; + ctx.size = 0; } static mut ASAN_INITED: bool = false; @@ -201,14 +217,14 @@ impl QemuAsanHelper { if let Some(ck) = ckinfo.as_mut() { if ck.start != addr { // Free not the start of the chunk - std::process::abort(); + asan_giovese_badfree(addr, emu::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 - std::process::abort(); + asan_giovese_badfree(addr, emu::read_reg(Regs::Pc).unwrap_or(u64::MAX)); } } } @@ -220,62 +236,152 @@ impl QemuAsanHelper { } pub fn read_1(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_load1(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_load1(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 0, + addr, + 1, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn read_2(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_load2(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_load2(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 0, + addr, + 2, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn read_4(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_load4(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_load4(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 0, + addr, + 4, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn read_8(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_load8(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_load8(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 0, + addr, + 8, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn read_n(&mut self, addr: u64, size: usize) { - if self.enabled() && unsafe { asan_giovese_loadN(emu::g2h(addr), size) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_loadN(emu::g2h(addr), size) != 0 { + asan_giovese_report_and_crash( + 0, + addr, + size, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn write_1(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_store1(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_store1(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 1, + addr, + 1, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn write_2(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_store2(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_store2(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 1, + addr, + 2, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn write_4(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_store4(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_store4(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 1, + addr, + 4, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn write_8(&mut self, addr: u64) { - if self.enabled() && unsafe { asan_giovese_store8(emu::g2h(addr)) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_store8(emu::g2h(addr)) != 0 { + asan_giovese_report_and_crash( + 1, + addr, + 8, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } pub fn write_n(&mut self, addr: u64, size: usize) { - if self.enabled() && unsafe { asan_giovese_storeN(emu::g2h(addr), size) != 0 } { - std::process::abort(); + unsafe { + if self.enabled() && asan_giovese_storeN(emu::g2h(addr), size) != 0 { + asan_giovese_report_and_crash( + 1, + addr, + size, + emu::read_reg(Regs::Pc).unwrap_or(u64::MAX), + 0, + emu::read_reg(Regs::Sp).unwrap_or(u64::MAX), + ); + } } } diff --git a/libafl_qemu/src/x86.rs b/libafl_qemu/src/i386.rs similarity index 77% rename from libafl_qemu/src/x86.rs rename to libafl_qemu/src/i386.rs index 024b5c90cd..550f1c22ad 100644 --- a/libafl_qemu/src/x86.rs +++ b/libafl_qemu/src/i386.rs @@ -6,7 +6,7 @@ use pyo3::prelude::*; #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] #[repr(i32)] -pub enum X86Regs { +pub enum Regs { Eax = 0, Ebx = 1, Ecx = 2, @@ -21,13 +21,13 @@ pub enum X86Regs { /// alias registers #[allow(non_upper_case_globals)] -impl X86Regs { - pub const Sp: X86Regs = X86Regs::Esp; - pub const Pc: X86Regs = X86Regs::Eip; +impl Regs { + pub const Sp: Regs = Regs::Esp; + pub const Pc: Regs = Regs::Eip; } #[cfg(feature = "python")] -impl IntoPy for X86Regs { +impl IntoPy for Regs { fn into_py(self, py: Python) -> PyObject { let n: i32 = self.into(); n.into_py(py) diff --git a/libafl_qemu/src/lib.rs b/libafl_qemu/src/lib.rs index 9dbf0b78b8..cc24ecd728 100644 --- a/libafl_qemu/src/lib.rs +++ b/libafl_qemu/src/lib.rs @@ -1,9 +1,18 @@ use std::env; pub mod aarch64; -pub mod amd64; pub mod arm; -pub mod x86; +pub mod i386; +pub mod x86_64; + +#[cfg(cpu_target = "aarch64")] +pub use aarch64::*; +#[cfg(cpu_target = "arm")] +pub use arm::*; +#[cfg(cpu_target = "i386")] +pub use i386::*; +#[cfg(cpu_target = "x86_64")] +pub use x86_64::*; pub mod elf; @@ -210,19 +219,12 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> { emu::remove_hook(addr); } - let x86m = PyModule::new(py, "x86")?; - for r in x86::X86Regs::iter() { + let regsm = PyModule::new(py, "regs")?; + for r in Regs::iter() { let v: i32 = r.into(); - x86m.add(&format!("{:?}", r), v)?; + regsm.add(&format!("{:?}", r), v)?; } - m.add_submodule(x86m)?; - - let amd64m = PyModule::new(py, "amd64")?; - for r in amd64::Amd64Regs::iter() { - let v: i32 = r.into(); - amd64m.add(&format!("{:?}", r), v)?; - } - m.add_submodule(amd64m)?; + m.add_submodule(regsm)?; let mmapm = PyModule::new(py, "mmap")?; for r in emu::MmapPerms::iter() { diff --git a/libafl_qemu/src/snapshot.rs b/libafl_qemu/src/snapshot.rs index 220b80cc16..189a7adfb0 100644 --- a/libafl_qemu/src/snapshot.rs +++ b/libafl_qemu/src/snapshot.rs @@ -1,9 +1,11 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata}; use std::collections::HashMap; +#[cfg(cpu_target = "x86_64")] +use crate::TARGET_NR_mmap; use crate::{ emu, - emu::GuestMaps, + emu::{GuestMaps, SyscallHookResult}, executor::QemuExecutor, helper::{QemuHelper, QemuHelperTuple}, }; @@ -23,6 +25,7 @@ pub struct QemuSnapshotHelper { pub pages: HashMap, pub dirty: Vec, pub brk: u64, + pub new_maps: Vec<(u64, usize)>, pub empty: bool, } @@ -35,6 +38,7 @@ impl QemuSnapshotHelper { pages: HashMap::default(), dirty: vec![], brk: 0, + new_maps: vec![], empty: true, } } @@ -101,6 +105,18 @@ impl QemuSnapshotHelper { } } emu::set_brk(self.brk); + self.reset_maps(); + } + + pub fn add_mapped(&mut self, start: u64, size: usize) { + self.new_maps.push((start, size)); + } + + pub fn reset_maps(&mut self) { + for (addr, size) in &self.new_maps { + drop(emu::unmap(*addr, *size)); + } + self.new_maps.clear(); } } @@ -126,6 +142,11 @@ where executor.hook_write2_execution(trace_write2_snapshot::); executor.hook_write1_execution(trace_write1_snapshot::); executor.hook_write_n_execution(trace_write_n_snapshot::); + + #[cfg(cpu_target = "x86_64")] + { + executor.hook_syscalls(trace_mmap_snapshot::); + } } fn pre_exec(&mut self, _input: &I) { @@ -196,3 +217,30 @@ pub fn trace_write_n_snapshot( .unwrap(); h.access(addr, size); } + +#[cfg(cpu_target = "x86_64")] +pub fn trace_mmap_snapshot( + helpers: &mut QT, + _state: &mut S, + sys_num: i32, + a0: u64, + a1: u64, + _a2: u64, + _a3: u64, + _a4: u64, + _a5: u64, + _a6: u64, + _a7: u64, +) -> SyscallHookResult +where + I: Input, + QT: QemuHelperTuple, +{ + if sys_num == TARGET_NR_mmap { + let h = helpers + .match_first_type_mut::() + .unwrap(); + h.add_mapped(a0, a1 as usize); + } + SyscallHookResult::new(None) +} diff --git a/libafl_qemu/src/amd64.rs b/libafl_qemu/src/x86_64.rs similarity index 99% rename from libafl_qemu/src/amd64.rs rename to libafl_qemu/src/x86_64.rs index 229db0464f..40968630ef 100644 --- a/libafl_qemu/src/amd64.rs +++ b/libafl_qemu/src/x86_64.rs @@ -6,7 +6,7 @@ use pyo3::prelude::*; #[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)] #[repr(i32)] -pub enum Amd64Regs { +pub enum Regs { Rax = 0, Rbx = 1, Rcx = 2, @@ -29,13 +29,13 @@ pub enum Amd64Regs { /// alias registers #[allow(non_upper_case_globals)] -impl Amd64Regs { - pub const Sp: Amd64Regs = Amd64Regs::Rsp; - pub const Pc: Amd64Regs = Amd64Regs::Rip; +impl Regs { + pub const Sp: Regs = Regs::Rsp; + pub const Pc: Regs = Regs::Rip; } #[cfg(feature = "python")] -impl IntoPy for Amd64Regs { +impl IntoPy for Regs { fn into_py(self, py: Python) -> PyObject { let n: i32 = self.into(); n.into_py(py)