diff --git a/crates/libafl/src/common/nautilus/README.md b/crates/libafl/src/common/nautilus/README.md index 9f91e27329..32f0afe898 100644 --- a/crates/libafl/src/common/nautilus/README.md +++ b/crates/libafl/src/common/nautilus/README.md @@ -1,4 +1,4 @@ -# Nautilus 2.0 LibAFL Mutator +# Nautilus 2.0 `LibAFL` Mutator Nautilus is a coverage guided, grammar-based mutator. You can use it to improve your test coverage and find more bugs. By specifying the grammar of semi-valid inputs, Nautilus is able to perform complex mutation and to uncover more interesting test cases. Many of the ideas behind the original fuzzer are documented in a paper published at NDSS 2019. @@ -7,7 +7,7 @@ Nautilus is a coverage guided, grammar-based mutator. You can use it to improve

Version 2.0 has added many improvements to this early prototype. -Features from version 2.0 we support in LibAFL: +Features from version 2.0 we support in `LibAFL`: * Support for grammars specified in python * Support for non-context free grammars using python scripts to generate inputs from the structure diff --git a/crates/libafl/src/common/nautilus/mod.rs b/crates/libafl/src/common/nautilus/mod.rs index 215c00cb6f..7130110069 100644 --- a/crates/libafl/src/common/nautilus/mod.rs +++ b/crates/libafl/src/common/nautilus/mod.rs @@ -1,4 +1,4 @@ -//! LibAFL version of the [`Nautilus`](https://github.com/nautilus-fuzz/nautilus) grammar fuzzer +//! `LibAFL` version of the [`Nautilus`](https://github.com/nautilus-fuzz/nautilus) grammar fuzzer #![doc = include_str!("README.md")] #[allow(missing_docs)] diff --git a/crates/libafl/src/executors/command.rs b/crates/libafl/src/executors/command.rs index a57a30c486..597ee9b4d4 100644 --- a/crates/libafl/src/executors/command.rs +++ b/crates/libafl/src/executors/command.rs @@ -235,6 +235,7 @@ pub struct PTraceCommandConfigurator { } #[cfg(all(feature = "intel_pt", target_os = "linux"))] +#[allow(unreachable_code)] impl CommandConfigurator for PTraceCommandConfigurator { fn spawn_child(&mut self, target_bytes: OwnedSlice<'_, u8>) -> Result { use nix::{ diff --git a/crates/libafl_bolts/src/rands/mod.rs b/crates/libafl_bolts/src/rands/mod.rs index 14b5c50ce9..8ad2e94ea7 100644 --- a/crates/libafl_bolts/src/rands/mod.rs +++ b/crates/libafl_bolts/src/rands/mod.rs @@ -567,9 +567,9 @@ pub mod pybind { #[pyclass(unsendable, name = "StdRand")] #[expect(clippy::unsafe_derive_deserialize)] #[derive(Serialize, Deserialize, Debug, Clone)] - /// Python class for StdRand + /// Python class for `StdRand` pub struct PythonStdRand { - /// Rust wrapped StdRand object + /// Rust wrapped `StdRand` object pub inner: StdRand, } diff --git a/crates/libafl_frida/src/helper.rs b/crates/libafl_frida/src/helper.rs index cd65bb362f..5894ebaf1a 100644 --- a/crates/libafl_frida/src/helper.rs +++ b/crates/libafl_frida/src/helper.rs @@ -699,26 +699,26 @@ where }; #[cfg(target_arch = "x86_64")] - if let Some(details) = res { - if let Some(rt) = runtimes.match_first_type_mut::() { - let start = output.writer().pc(); - rt.emit_shadow_check( - address, - output, - instr.bytes().len(), - details.0, - details.1, - details.2, - details.3, - details.4, - ); - log::trace!( - "emitted shadow_check for {:x} at {:x}-{:x}", - address, - start, - output.writer().pc() - ); - } + if let Some(details) = res + && let Some(rt) = runtimes.match_first_type_mut::() + { + let start = output.writer().pc(); + rt.emit_shadow_check( + address, + output, + instr.bytes().len(), + details.0, + details.1, + details.2, + details.3, + details.4, + ); + log::trace!( + "emitted shadow_check for {:x} at {:x}-{:x}", + address, + start, + output.writer().pc() + ); } #[cfg(target_arch = "aarch64")] @@ -740,21 +740,13 @@ where feature = "cmplog", any(target_arch = "aarch64", target_arch = "x86_64") ))] - if let Some(rt) = runtimes.match_first_type_mut::() { - if let Some((op1, op2, shift, special_case)) = + if let Some(rt) = runtimes.match_first_type_mut::() + && let Some((op1, op2, shift, special_case)) = CmpLogRuntime::cmplog_is_interesting_instruction(decoder, address, instr) - //change this as well - { - //emit code that saves the relevant data in runtime(passes it to x0, x1) - rt.emit_comparison_handling( - address, - output, - &op1, - &op2, - &shift, - &special_case, - ); - } + //change this as well + { + //emit code that saves the relevant data in runtime(passes it to x0, x1) + rt.emit_comparison_handling(address, output, &op1, &op2, &shift, &special_case); } if let Some(rt) = runtimes.match_first_type_mut::() { diff --git a/crates/libafl_qemu/src/elf.rs b/crates/libafl_qemu/src/elf.rs index fba390a0c6..300475755e 100644 --- a/crates/libafl_qemu/src/elf.rs +++ b/crates/libafl_qemu/src/elf.rs @@ -49,26 +49,26 @@ impl<'a> EasyElf<'a> { #[must_use] pub fn resolve_symbol(&self, name: &str, load_addr: GuestAddr) -> Option { for sym in &self.elf.syms { - if let Some(sym_name) = self.elf.strtab.get_at(sym.st_name) { - if sym_name == name { - return if sym.st_value == 0 { - None - } else if self.is_pic() { - #[cfg(cpu_target = "arm")] - // Required because of arm interworking addresses aka bit(0) for thumb mode - let addr = (sym.st_value as GuestAddr + load_addr) & !(0x1 as GuestAddr); - #[cfg(not(cpu_target = "arm"))] - let addr = sym.st_value as GuestAddr + load_addr; - Some(addr) - } else { - #[cfg(cpu_target = "arm")] - // Required because of arm interworking addresses aka bit(0) for thumb mode - let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); - #[cfg(not(cpu_target = "arm"))] - let addr = sym.st_value as GuestAddr; - Some(addr) - }; - } + if let Some(sym_name) = self.elf.strtab.get_at(sym.st_name) + && sym_name == name + { + return if sym.st_value == 0 { + None + } else if self.is_pic() { + #[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr + load_addr) & !(0x1 as GuestAddr); + #[cfg(not(cpu_target = "arm"))] + let addr = sym.st_value as GuestAddr + load_addr; + Some(addr) + } else { + #[cfg(cpu_target = "arm")] + // Required because of arm interworking addresses aka bit(0) for thumb mode + let addr = (sym.st_value as GuestAddr) & !(0x1 as GuestAddr); + #[cfg(not(cpu_target = "arm"))] + let addr = sym.st_value as GuestAddr; + Some(addr) + }; } } None diff --git a/crates/libafl_qemu/src/modules/cmplog.rs b/crates/libafl_qemu/src/modules/cmplog.rs index 0572036d59..7c92e034b3 100644 --- a/crates/libafl_qemu/src/modules/cmplog.rs +++ b/crates/libafl_qemu/src/modules/cmplog.rs @@ -204,11 +204,12 @@ where I: Unpin, S: Unpin + HasMetadata, { - if let Some(h) = emulator_modules.get::() { - if !h.must_instrument(pc) { - return None; - } + if let Some(h) = emulator_modules.get::() + && !h.must_instrument(pc) + { + return None; } + let state = state.expect("The gen_unique_cmp_ids hook works only for in-process fuzzing. Is the Executor initialized?"); if state.metadata_map().get::().is_none() { state.add_metadata(QemuCmpsMapMetadata::new()); @@ -238,11 +239,12 @@ where I: Unpin, S: HasMetadata + Unpin, { - if let Some(h) = emulator_modules.get::() { - if !h.must_instrument(pc) { - return None; - } + if let Some(h) = emulator_modules.get::() + && !h.must_instrument(pc) + { + return None; } + Some(hash_64_fast(pc.into()) & (CMPLOG_MAP_W as u64 - 1)) } diff --git a/crates/libafl_qemu/src/modules/usermode/asan_guest.rs b/crates/libafl_qemu/src/modules/usermode/asan_guest.rs index 54d55abf14..2fa7e9c2e0 100644 --- a/crates/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/crates/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -116,13 +116,12 @@ where } /* Don't sanitize the sanitizer! */ - if let Some(asan_mappings) = &h.asan_mappings { - if asan_mappings + if let Some(asan_mappings) = &h.asan_mappings + && asan_mappings .iter() .any(|m| m.start() <= pc && pc < m.end()) - { - return None; - } + { + return None; } let size = info.size(); diff --git a/crates/libafl_qemu/src/modules/usermode/asan_host.rs b/crates/libafl_qemu/src/modules/usermode/asan_host.rs index 8062845392..d4a92f2155 100644 --- a/crates/libafl_qemu/src/modules/usermode/asan_host.rs +++ b/crates/libafl_qemu/src/modules/usermode/asan_host.rs @@ -1199,13 +1199,12 @@ where } // Don't sanitize the sanitizer! - if let Some(asan_mappings) = &h.asan_mappings { - if asan_mappings + if let Some(asan_mappings) = &h.asan_mappings + && asan_mappings .iter() .any(|m| m.start() <= pc && pc < m.end()) - { - return None; - } + { + return None; } Some(pc.into()) @@ -1296,13 +1295,12 @@ where } // Don't sanitize the sanitizer! - if let Some(asan_mappings) = &h.asan_mappings { - if asan_mappings + if let Some(asan_mappings) = &h.asan_mappings + && asan_mappings .iter() .any(|m| m.start() <= pc && pc < m.end()) - { - return Some(0); - } + { + return Some(0); } Some(pc.into()) diff --git a/crates/libafl_qemu/src/modules/usermode/snapshot.rs b/crates/libafl_qemu/src/modules/usermode/snapshot.rs index 0062c8a534..421728a893 100644 --- a/crates/libafl_qemu/src/modules/usermode/snapshot.rs +++ b/crates/libafl_qemu/src/modules/usermode/snapshot.rs @@ -26,7 +26,10 @@ use crate::{ modules::{ EmulatorModule, EmulatorModuleTuple, asan_host::AsanHostModule, - utils::filters::{HasAddressFilter, NOP_ADDRESS_FILTER, NopAddressFilter}, + utils::{ + filters::{HasAddressFilter, NOP_ADDRESS_FILTER, NopAddressFilter}, + logic::OptionalModule, + }, }, qemu::{Hook, SyscallHookResult}, }; @@ -842,8 +845,15 @@ pub fn trace_write_snapshot( I: Unpin, S: Unpin, { - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, SIZE); + if let Some(h) = emulator_modules.get_mut::() { + h.access(addr, SIZE); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(addr, SIZE); + } } pub fn trace_write_n_snapshot( @@ -859,8 +869,15 @@ pub fn trace_write_n_snapshot( I: Unpin, S: Unpin, { - let h = emulator_modules.get_mut::().unwrap(); - h.access(addr, size); + if let Some(h) = emulator_modules.get_mut::() { + h.access(addr, size); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(addr, size); + } } /// Do not consider munmap syscalls that are not allowed @@ -885,9 +902,18 @@ where S: Unpin, { if i64::from(sys_num) == SYS_munmap { - let h = emulator_modules.get_mut::().unwrap(); - if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) { - return SyscallHookResult::Skip(0); + if let Some(h) = emulator_modules.get_mut::() { + if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) { + return SyscallHookResult::Skip(0); + } + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + if !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) { + return SyscallHookResult::Skip(0); + } } } @@ -918,23 +944,50 @@ where // NOT A COMPLETE LIST OF MEMORY EFFECTS match i64::from(sys_num) { SYS_read | SYS_pread64 => { - let h = emulator_modules.get_mut::().unwrap(); - /* - * Only note the access if the call is successful. And only mark the - * portion of the buffer which has actually been modified. - */ - if result != GuestAddr::MAX { - h.access(a1, result as usize); + if let Some(h) = emulator_modules.get_mut::() { + /* + * Only note the access if the call is successful. And only mark the + * portion of the buffer which has actually been modified. + */ + if result != GuestAddr::MAX { + h.access(a1, result as usize); + } + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + /* + * Only note the access if the call is successful. And only mark the + * portion of the buffer which has actually been modified. + */ + if result != GuestAddr::MAX { + h.access(a1, result as usize); + } } } SYS_readlinkat => { - let h = emulator_modules.get_mut::().unwrap(); - h.access(a2, a3 as usize); + if let Some(h) = emulator_modules.get_mut::() { + h.access(a2, a3 as usize); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(a2, a3 as usize); + } } #[cfg(not(cpu_target = "riscv32"))] SYS_futex => { - let h = emulator_modules.get_mut::().unwrap(); - h.access(a0, a3 as usize); + if let Some(h) = emulator_modules.get_mut::() { + h.access(a0, a3 as usize); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(a0, a3 as usize); + } } #[cfg(not(any( cpu_target = "arm", @@ -945,26 +998,54 @@ where )))] SYS_newfstatat => { if a2 != 0 { - let h = emulator_modules.get_mut::().unwrap(); - h.access(a2, 4096); // stat is not greater than a page + if let Some(h) = emulator_modules.get_mut::() { + h.access(a2, 4096); // stat is not greater than a page + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(a2, 4096); // stat is not greater than a page + } } } #[cfg(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "i386"))] SYS_fstatat64 => { if a2 != 0 { - let h = emulator_modules.get_mut::().unwrap(); - h.access(a2, 4096); // stat is not greater than a page + if let Some(h) = emulator_modules.get_mut::() { + h.access(a2, 4096); // stat is not greater than a page + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(a2, 4096); // stat is not greater than a page + } } } #[cfg(not(cpu_target = "riscv32"))] SYS_statfs | SYS_fstat | SYS_fstatfs => { - let h = emulator_modules.get_mut::().unwrap(); - h.access(a1, 4096); // stat is not greater than a page + if let Some(h) = emulator_modules.get_mut::() { + h.access(a1, 4096); // stat is not greater than a page + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(a1, 4096); // stat is not greater than a page + } } #[cfg(not(cpu_target = "riscv32"))] SYS_getrandom => { - let h = emulator_modules.get_mut::().unwrap(); - h.access(a0, a1 as usize); + if let Some(h) = emulator_modules.get_mut::() { + h.access(a0, a1 as usize); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.access(a0, a1 as usize); + } } SYS_brk => { // We don't handle brk here. It is handled in the reset function only when it's needed. @@ -983,33 +1064,72 @@ where #[cfg(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "riscv32"))] if sys_const == SYS_mmap2 { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = emulator_modules.get_mut::().unwrap(); - h.add_mapped(result, a1 as usize, Some(prot)); + if let Some(h) = emulator_modules.get_mut::() { + h.add_mapped(result, a1 as usize, Some(prot)); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.add_mapped(result, a1 as usize, Some(prot)); + } } } #[cfg(not(any(cpu_target = "arm", cpu_target = "riscv32")))] - if sys_const == SYS_mmap { - if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = emulator_modules.get_mut::().unwrap(); + if sys_const == SYS_mmap + && let Ok(prot) = MmapPerms::try_from(a2 as i32) + { + if let Some(h) = emulator_modules.get_mut::() { + h.add_mapped(result, a1 as usize, Some(prot)); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); h.add_mapped(result, a1 as usize, Some(prot)); } } if sys_const == SYS_mremap { - let h = emulator_modules.get_mut::().unwrap(); - // TODO get the old permissions from the removed mapping - h.remove_mapped(a0, a1 as usize); - h.add_mapped(result, a2 as usize, None); + if let Some(h) = emulator_modules.get_mut::() { + // TODO get the old permissions from the removed mapping + h.remove_mapped(a0, a1 as usize); + h.add_mapped(result, a2 as usize, None); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + // TODO get the old permissions from the removed mapping + h.remove_mapped(a0, a1 as usize); + h.add_mapped(result, a2 as usize, None); + } } else if sys_const == SYS_mprotect { if let Ok(prot) = MmapPerms::try_from(a2 as i32) { - let h = emulator_modules.get_mut::().unwrap(); - h.change_mapped_perms(a0, a1 as usize, Some(prot)); + if let Some(h) = emulator_modules.get_mut::() { + h.change_mapped_perms(a0, a1 as usize, Some(prot)); + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + h.change_mapped_perms(a0, a1 as usize, Some(prot)); + } } } else if sys_const == SYS_munmap { - let h = emulator_modules.get_mut::().unwrap(); - if h.is_unmap_allowed(a0, a1 as usize) { - h.remove_mapped(a0, a1 as usize); + if let Some(h) = emulator_modules.get_mut::() { + if h.is_unmap_allowed(a0, a1 as usize) { + h.remove_mapped(a0, a1 as usize); + } + } else { + let snap = emulator_modules + .get_mut::>() + .unwrap(); + let h = snap.get_inner_module_mut(); + if h.is_unmap_allowed(a0, a1 as usize) { + h.remove_mapped(a0, a1 as usize); + } } } } diff --git a/crates/libafl_qemu/src/modules/utils/logic.rs b/crates/libafl_qemu/src/modules/utils/logic.rs new file mode 100644 index 0000000000..54865e15fa --- /dev/null +++ b/crates/libafl_qemu/src/modules/utils/logic.rs @@ -0,0 +1,113 @@ +use std::fmt::Debug; + +use crate::modules::{EmulatorModule, EmulatorModuleTuple}; + +#[derive(Debug)] +pub struct OptionalModule { + enabled: bool, + module: MD, +} + +impl OptionalModule { + pub fn new(enabled: bool, module: MD) -> Self { + Self { enabled, module } + } + + pub fn get_inner_module_mut(&mut self) -> &mut MD { + &mut self.module + } +} + +impl EmulatorModule for OptionalModule +where + I: Unpin, + S: Unpin, + MD: EmulatorModule, +{ + const HOOKS_DO_SIDE_EFFECTS: bool = true; + + fn pre_qemu_init( + &mut self, + emulator_modules: &mut crate::EmulatorModules, + qemu_params: &mut crate::QemuParams, + ) where + ET: EmulatorModuleTuple, + { + if self.enabled { + self.module.pre_qemu_init(emulator_modules, qemu_params); + } + } + + fn post_qemu_init( + &mut self, + qemu: crate::Qemu, + emulator_modules: &mut crate::EmulatorModules, + ) where + ET: EmulatorModuleTuple, + { + if self.enabled { + self.module.post_qemu_init(qemu, emulator_modules); + } + } + + fn first_exec( + &mut self, + qemu: crate::Qemu, + emulator_modules: &mut crate::EmulatorModules, + state: &mut S, + ) where + ET: EmulatorModuleTuple, + { + if self.enabled { + self.module.first_exec(qemu, emulator_modules, state); + } + } + + fn pre_exec( + &mut self, + qemu: crate::Qemu, + emulator_modules: &mut crate::EmulatorModules, + state: &mut S, + input: &I, + ) where + ET: EmulatorModuleTuple, + { + if self.enabled { + self.module.pre_exec(qemu, emulator_modules, state, input); + } + } + + fn post_exec( + &mut self, + qemu: crate::Qemu, + emulator_modules: &mut crate::EmulatorModules, + state: &mut S, + input: &I, + observers: &mut OT, + exit_kind: &mut libafl::executors::ExitKind, + ) where + OT: libafl::observers::ObserversTuple, + ET: EmulatorModuleTuple, + { + if self.enabled { + self.module + .post_exec(qemu, emulator_modules, state, input, observers, exit_kind); + } + } + + unsafe fn on_crash(&mut self) { + if self.enabled { + unsafe { + self.module.on_crash(); + } + } + } + + unsafe fn on_timeout(&mut self) { + if self.enabled { + unsafe { + self.module.on_timeout(); + } + } + } +} diff --git a/crates/libafl_qemu/src/modules/utils/mod.rs b/crates/libafl_qemu/src/modules/utils/mod.rs index 6ad6c17029..83f60e60a1 100644 --- a/crates/libafl_qemu/src/modules/utils/mod.rs +++ b/crates/libafl_qemu/src/modules/utils/mod.rs @@ -1,4 +1,5 @@ pub mod filters; +pub mod logic; #[cfg(feature = "usermode")] pub use addr2line::*; diff --git a/crates/libafl_sugar/src/forkserver.rs b/crates/libafl_sugar/src/forkserver.rs index aacbf55d98..2f29e852ae 100644 --- a/crates/libafl_sugar/src/forkserver.rs +++ b/crates/libafl_sugar/src/forkserver.rs @@ -81,6 +81,9 @@ pub struct ForkserverBytesCoverageSugar<'a> { /// Fuzz `iterations` number of times, instead of indefinitely; implies use of `fuzz_loop_for` #[builder(default = None)] iterations: Option, + /// Disable redirection of stdout to /dev/null on unix build targets + #[builder(default = None)] + enable_stdout: Option, } impl ForkserverBytesCoverageSugar<'_> { @@ -337,11 +340,19 @@ impl ForkserverBytesCoverageSugar<'_> { .remote_broker_addr(self.remote_broker_addr); #[cfg(unix)] - let launcher = launcher.stdout_file(Some("/dev/null")); - match launcher.build().launch() { - Ok(()) => (), - Err(Error::ShuttingDown) => log::info!("\nFuzzing stopped by user. Good Bye."), - Err(err) => panic!("Fuzzingg failed {err:?}"), + if self.enable_stdout.unwrap_or(false) { + match launcher.build().launch() { + Ok(()) => (), + Err(Error::ShuttingDown) => log::info!("\nFuzzing stopped by user. Good Bye."), + Err(err) => panic!("Fuzzingg failed {err:?}"), + } + } else { + let launcher = launcher.stdout_file(Some("/dev/null")); + match launcher.build().launch() { + Ok(()) => (), + Err(Error::ShuttingDown) => log::info!("\nFuzzing stopped by user. Good Bye."), + Err(err) => panic!("Fuzzingg failed {err:?}"), + } } } } @@ -367,6 +378,7 @@ pub mod pybind { iterations: Option, tokens_file: Option, timeout: Option, + enable_stdout: Option, } #[pymethods] @@ -380,7 +392,8 @@ pub mod pybind { cores, iterations=None, tokens_file=None, - timeout=None + timeout=None, + enable_stdout=None ))] fn new( input_dirs: Vec, @@ -390,6 +403,7 @@ pub mod pybind { iterations: Option, tokens_file: Option, timeout: Option, + enable_stdout: Option, ) -> Self { Self { input_dirs, @@ -399,6 +413,7 @@ pub mod pybind { iterations, tokens_file, timeout, + enable_stdout, } } @@ -416,6 +431,7 @@ pub mod pybind { .timeout(self.timeout) .tokens_file(self.tokens_file.clone()) .iterations(self.iterations) + .enable_stdout(self.enable_stdout) .build() .run(); } diff --git a/crates/libafl_sugar/src/qemu.rs b/crates/libafl_sugar/src/qemu.rs index 63b8442302..4c7c4e21f4 100644 --- a/crates/libafl_sugar/src/qemu.rs +++ b/crates/libafl_sugar/src/qemu.rs @@ -40,7 +40,10 @@ use libafl_bolts::{ #[cfg(not(any(feature = "mips", feature = "hexagon")))] use libafl_qemu::modules::CmpLogModule; pub use libafl_qemu::qemu::Qemu; -use libafl_qemu::{Emulator, QemuExecutor, modules::edges::StdEdgeCoverageModule}; +use libafl_qemu::{ + Emulator, QemuExecutor, + modules::{SnapshotModule, edges::StdEdgeCoverageModule, utils::logic::OptionalModule}, +}; use libafl_targets::{CmpLogObserver, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND, edges_map_mut_ptr}; use typed_builder::TypedBuilder; @@ -88,6 +91,8 @@ where /// Disable redirection of stdout to /dev/null on unix build targets #[builder(default = None)] enable_stdout: Option, + #[builder(default = None)] + use_snapshot: Option, } impl Debug for QemuBytesCoverageSugar<'_, H> @@ -115,6 +120,7 @@ where ) .field("iterations", &self.iterations) .field("enable_stdout", &self.enable_stdout) + .field("use_snapshot", &self.use_snapshot) .finish() } } @@ -234,6 +240,9 @@ where // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + let snap = SnapshotModule::new(); + let snap = OptionalModule::new(self.use_snapshot.unwrap_or(false), snap); + // The wrapped harness function, calling out to the LLVM-style harness if self.use_cmplog.unwrap_or(false) { let modules = { @@ -245,6 +254,7 @@ where .build() .unwrap(), CmpLogModule::default(), + snap ) } #[cfg(any(feature = "mips", feature = "hexagon"))] @@ -253,7 +263,8 @@ where StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) .build() - .unwrap() + .unwrap(), + snap ) } }; @@ -380,11 +391,14 @@ where } } } else { + let snap = SnapshotModule::new(); + let snap = OptionalModule::new(self.use_snapshot.unwrap_or(false), snap); let modules = tuple_list!( StdEdgeCoverageModule::builder() .map_observer(edges_observer.as_mut()) .build() - .unwrap() + .unwrap(), + snap ); let mut harness = |_emulator: &mut Emulator<_, _, _, _, _, _, _>, @@ -549,6 +563,7 @@ pub mod pybind { tokens_file: Option, timeout: Option, enable_stdout: Option, + use_snapshot: Option, } #[pymethods] @@ -566,6 +581,7 @@ pub mod pybind { tokens_file=None, timeout=None, enable_stdout=None, + use_snapshot=None, ))] fn new( input_dirs: Vec, @@ -577,6 +593,7 @@ pub mod pybind { tokens_file: Option, timeout: Option, enable_stdout: Option, + use_snapshot: Option, ) -> Self { Self { input_dirs, @@ -588,6 +605,7 @@ pub mod pybind { tokens_file, timeout, enable_stdout, + use_snapshot, } } @@ -612,6 +630,7 @@ pub mod pybind { .tokens_file(self.tokens_file.clone()) .iterations(self.iterations) .enable_stdout(self.enable_stdout) + .use_snapshot(self.use_snapshot) .build() .run(QemuSugarParameter::Qemu(&qemu.qemu)); }