diff --git a/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs index c430b5f613..adff58deba 100644 --- a/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs @@ -4,7 +4,12 @@ use libafl::bolts::shmem::ShMemProvider; use libafl::bolts::AsSlice; use libafl::observers::ConstMapObserver; use libafl::{ - bolts::{current_nanos, rands::StdRand, shmem::UnixShMemProvider, tuples::tuple_list}, + bolts::{ + current_nanos, + rands::StdRand, + shmem::{ShMem, StdShMemProvider}, + tuples::tuple_list, + }, corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler}, events::SimpleEventManager, executors::InProcessForkExecutor, @@ -34,7 +39,7 @@ extern "C" { #[allow(clippy::similar_names)] pub fn main() { - let shmem_provider = UnixShMemProvider::new().unwrap(); + let mut shmem_provider = StdShMemProvider::new().unwrap(); unsafe { create_shmem_array() }; let map_ptr = unsafe { get_ptr() }; let mut harness = |input: &BytesInput| { @@ -46,8 +51,12 @@ pub fn main() { // Create an observation channel using the signals map let observer = unsafe { ConstMapObserver::::new_from_ptr("signals", map_ptr) }; // Create a stacktrace observer - let bt_observer = - BacktraceObserver::new("BacktraceObserver", libafl::observers::HarnessType::FFI); + let mut bt = shmem_provider.new_shmem_object::>().unwrap(); + let bt_observer = BacktraceObserver::new( + "BacktraceObserver", + unsafe { bt.as_object_mut::>() }, + libafl::observers::HarnessType::Child, + ); // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&observer); diff --git a/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/output b/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/output deleted file mode 100755 index 0a7d4247c2..0000000000 Binary files a/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/output and /dev/null differ diff --git a/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs index a567885e5c..4868db153d 100644 --- a/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs @@ -40,8 +40,12 @@ pub fn main() { // Create an observation channel using the signals map let observer = unsafe { ConstMapObserver::::new_from_ptr("signals", array_ptr) }; // Create a stacktrace observer - let bt_observer = - BacktraceObserver::new("BacktraceObserver", libafl::observers::HarnessType::FFI); + let mut bt = None; + let bt_observer = BacktraceObserver::new( + "BacktraceObserver", + &mut bt, + libafl::observers::HarnessType::InProcess, + ); // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&observer); diff --git a/fuzzers/backtrace_baby_fuzzers/command_executor/Cargo.toml b/fuzzers/backtrace_baby_fuzzers/command_executor/Cargo.toml index a6d5cbeb9a..099d1dd537 100644 --- a/fuzzers/backtrace_baby_fuzzers/command_executor/Cargo.toml +++ b/fuzzers/backtrace_baby_fuzzers/command_executor/Cargo.toml @@ -13,6 +13,9 @@ codegen-units = 1 opt-level = 3 debug = true +[build-dependencies] +cc = "*" + [dependencies] libafl = { path = "../../../libafl/" } ahash = { version = "0.7"} # another hash diff --git a/fuzzers/backtrace_baby_fuzzers/command_executor/build.rs b/fuzzers/backtrace_baby_fuzzers/command_executor/build.rs new file mode 100644 index 0000000000..aae5e26982 --- /dev/null +++ b/fuzzers/backtrace_baby_fuzzers/command_executor/build.rs @@ -0,0 +1,11 @@ +use std::env; + +fn main() { + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + let mut cmd = cc::Build::new().get_compiler().to_command(); + cmd.args(&["src/test_command.c", "-o"]) + .arg(&format!("{}/test_command", &cwd)) + .arg("-fsanitize=address") + .status() + .unwrap(); +} diff --git a/fuzzers/backtrace_baby_fuzzers/command_executor/test_command b/fuzzers/backtrace_baby_fuzzers/command_executor/test_command deleted file mode 100755 index 3a1c1f7aa7..0000000000 Binary files a/fuzzers/backtrace_baby_fuzzers/command_executor/test_command and /dev/null differ diff --git a/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs index e94c4c91b6..e04a56a8df 100644 --- a/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs @@ -7,7 +7,7 @@ use libafl::{ bolts::{ current_nanos, rands::StdRand, - shmem::{unix_shmem, ShMemProvider}, + shmem::{unix_shmem, ShMem, ShMemProvider}, tuples::tuple_list, AsMutSlice, AsSlice, }, @@ -33,6 +33,7 @@ pub fn main() { let mut shmem_provider = unix_shmem::UnixShMemProvider::new().unwrap(); let mut signals = shmem_provider.new_shmem(16).unwrap(); let mut signals_clone = signals.clone(); + let mut bt = shmem_provider.new_shmem_object::>().unwrap(); let mut signals_set = |idx: usize| { let a = signals.as_mut_slice(); @@ -68,8 +69,11 @@ pub fn main() { // Create an observation channel using the signals map let observer = StdMapObserver::new("signals", signals_clone.as_mut_slice()); // Create a stacktrace observer - let bt_observer = - BacktraceObserver::new("BacktraceObserver", libafl::observers::HarnessType::RUST); + let bt_observer = BacktraceObserver::new( + "BacktraceObserver", + unsafe { bt.as_object_mut::>() }, + libafl::observers::HarnessType::Child, + ); // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&observer); diff --git a/fuzzers/backtrace_baby_fuzzers/rust_code_with_inprocess_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/rust_code_with_inprocess_executor/src/main.rs index adc2844518..831e68f14d 100644 --- a/fuzzers/backtrace_baby_fuzzers/rust_code_with_inprocess_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/rust_code_with_inprocess_executor/src/main.rs @@ -62,8 +62,12 @@ pub fn main() { // Create an observation channel using the signals map let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS }); // Create a stacktrace observer to add the observers tuple - let bt_observer = - BacktraceObserver::new("BacktraceObserver", libafl::observers::HarnessType::RUST); + let mut bt = None; + let bt_observer = BacktraceObserver::new( + "BacktraceObserver", + &mut bt, + libafl::observers::HarnessType::InProcess, + ); // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&observer); diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index f01137a4b5..c215ec890a 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -172,6 +172,32 @@ pub trait ShMem: Sized + Debug + Clone + AsSlice + AsMutSlice { self.len() == 0 } + /// Convert to an owned object reference + /// + /// # Safety + /// This function is not safe as the object may be not initialized. + /// The user is responsible to initialize the object with something like + /// `*shmem.as_object_mut::() = T::new();` + unsafe fn as_object(&self) -> &T { + assert!(self.len() >= core::mem::size_of::()); + (self.as_slice().as_ptr() as *const () as *const T) + .as_ref() + .unwrap() + } + + /// Convert to an owned object mutable reference + /// + /// # Safety + /// This function is not safe as the object may be not initialized. + /// The user is responsible to initialize the object with something like + /// `*shmem.as_object_mut::() = T::new();` + unsafe fn as_object_mut(&mut self) -> &mut T { + assert!(self.len() >= core::mem::size_of::()); + (self.as_mut_slice().as_mut_ptr() as *mut () as *mut T) + .as_mut() + .unwrap() + } + /// Get the description of the shared memory mapping fn description(&self) -> ShMemDescription { ShMemDescription { @@ -207,6 +233,19 @@ pub trait ShMemProvider: Clone + Default + Debug { /// Get a mapping given its id and size fn shmem_from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result; + /// Create a new shared memory mapping to hold an object of the given type + fn new_shmem_object(&mut self) -> Result { + self.new_shmem(core::mem::size_of::()) + } + + /// Get a mapping given its id to hold an object of the given type + fn shmem_object_from_id( + &mut self, + id: ShMemId, + ) -> Result { + self.shmem_from_id_and_size(id, core::mem::size_of::()) + } + /// Get a mapping given a description fn shmem_from_description( &mut self, diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 551d3f573f..07341b7394 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -16,7 +16,7 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", unix))] use std::intrinsics::transmute; #[cfg(all(feature = "std", unix))] @@ -28,23 +28,18 @@ use nix::{ unistd::{fork, ForkResult}, }; -#[cfg(all(feature = "std", windows))] -use windows::Win32::System::Diagnostics::Debug::EXCEPTION_POINTERS; - #[cfg(unix)] use crate::bolts::os::unix_signals::setup_signal_handler; #[cfg(all(windows, feature = "std"))] use crate::bolts::os::windows_exceptions::setup_exception_handler; #[cfg(all(feature = "std", unix))] use crate::bolts::shmem::ShMemProvider; -#[cfg(feature = "std")] -use crate::observers::{BacktraceObserver, HarnessType}; #[cfg(windows)] use windows::Win32::System::Threading::SetThreadStackGuarantee; -#[cfg(all(feature = "std", windows))] -use crate::bolts::os::windows_exceptions::{ExceptionCode, Handler, CRASH_EXCEPTIONS}; +#[cfg(all(feature = "std", unix))] +use crate::bolts::os::unix_signals::{Handler, Signal}; use crate::{ events::{EventFirer, EventRestarter}, @@ -57,14 +52,6 @@ use crate::{ Error, }; -#[cfg(feature = "std")] -use crate::executors::inprocess::bt_signal_handlers::setup_bt_panic_hook; -#[cfg(all(feature = "std", unix))] -use crate::{ - bolts::os::unix_signals::{Handler, Signal}, - executors::inprocess::bt_signal_handlers::setup_child_panic_hook, -}; - /// The inmem executor simply calls a target function, then returns afterwards. #[allow(dead_code)] pub struct InProcessExecutor<'a, H, I, OT, S> @@ -161,21 +148,6 @@ where S: HasSolutions + HasClientPerfMonitor, Z: HasObjective, { - #[cfg(feature = "std")] - BacktraceObserver::setup_static_variable(); - - #[cfg(feature = "std")] - if let Some(obs) = observers.match_name::("BacktraceObserver") { - if obs.harness_type() == &HarnessType::RUST { - setup_bt_panic_hook::< - InProcessExecutor, - I, - OT, - S, - InProcessExecutorHandlerData, - >(unsafe { &GLOBAL_STATE }); - } - } let handlers = InProcessHandlers::new::()?; #[cfg(windows)] unsafe { @@ -225,6 +197,27 @@ where } } +/// The struct has [`InProcessHandlers`]. +#[cfg(windows)] +pub trait HasInProcessHandlers { + /// Get the in-process handlers. + fn inprocess_handlers(&self) -> &InProcessHandlers; +} + +#[cfg(windows)] +impl<'a, H, I, OT, S> HasInProcessHandlers for InProcessExecutor<'a, H, I, OT, S> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, +{ + /// the timeout handler + #[inline] + fn inprocess_handlers(&self) -> &InProcessHandlers { + &self.handlers + } +} + /// The inmem executor's handlers. #[derive(Debug)] pub struct InProcessHandlers { @@ -317,6 +310,8 @@ impl InProcessHandlers { #[cfg(unix)] unsafe { let data = &mut GLOBAL_STATE; + #[cfg(feature = "std")] + unix_signal_handler::setup_panic_hook::(); setup_signal_handler(data)?; compiler_fence(Ordering::SeqCst); Ok(Self { @@ -329,6 +324,8 @@ impl InProcessHandlers { #[cfg(all(windows, feature = "std"))] unsafe { let data = &mut GLOBAL_STATE; + #[cfg(feature = "std")] + windows_exception_handler::setup_panic_hook::(); setup_exception_handler(data)?; compiler_fence(Ordering::SeqCst); @@ -369,23 +366,7 @@ impl InProcessHandlers { } } } -/// trait implemented by the static variables handling the global data -pub trait HasHandlerData: 'static + Sync { - /// get executor - fn executor(&self) -> &E; - /// get mutable executor - #[allow(clippy::mut_from_ref)] - fn executor_mut(&self) -> &mut E; - /// get state - fn state(&self) -> &S; - /// get mutable state - #[allow(clippy::mut_from_ref)] - fn state_mut(&self) -> &mut S; - /// get current input - fn current_input(&self) -> &I; - /// Check if the pointers in the handler are valid - fn is_valid(&self) -> bool; -} + /// The global state of the in-process harness. #[derive(Debug)] #[allow(missing_docs)] @@ -410,27 +391,34 @@ pub struct InProcessExecutorHandlerData { unsafe impl Send for InProcessExecutorHandlerData {} unsafe impl Sync for InProcessExecutorHandlerData {} -impl HasHandlerData for InProcessExecutorHandlerData { - fn executor(&self) -> &E { - unsafe { (self.executor_ptr as *const E).as_ref().unwrap() } - } - - fn executor_mut(&self) -> &mut E { +#[allow(unused)] +impl InProcessExecutorHandlerData { + fn executor_mut<'a, E>(&self) -> &'a mut E { unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() } } - fn state(&self) -> &S { - unsafe { (self.state_ptr as *const S).as_ref().unwrap() } - } - - fn state_mut(&self) -> &mut S { + fn state_mut<'a, S>(&self) -> &'a mut S { unsafe { (self.state_ptr as *mut S).as_mut().unwrap() } } - fn current_input(&self) -> &I { + fn event_mgr_mut<'a, EM>(&self) -> &'a mut EM { + unsafe { (self.event_mgr_ptr as *mut EM).as_mut().unwrap() } + } + + fn fuzzer_mut<'a, Z>(&self) -> &'a mut Z { + unsafe { (self.fuzzer_ptr as *mut Z).as_mut().unwrap() } + } + + fn current_input<'a, I>(&self) -> &'a I { unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() } } + fn take_current_input<'a, I>(&mut self) -> &'a I { + let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }; + self.current_input_ptr = ptr::null(); + r + } + #[cfg(windows)] fn is_valid(&self) -> bool { self.in_target == 1 @@ -441,6 +429,7 @@ impl HasHandlerData for InProcessExecutorHandlerData { !self.current_input_ptr.is_null() } } + /// Exception handling needs some nasty unsafe. pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData { /// The state ptr for signal handling @@ -491,137 +480,22 @@ pub fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> { unsafe { (GLOBAL_STATE.executor_ptr as *mut E).as_mut() } } -/// signal handlers and `panic_hooks` for used BT collection -#[cfg(feature = "std")] -pub mod bt_signal_handlers { - - use std::panic; - - #[cfg(all(unix))] - use libc::{siginfo_t, ucontext_t}; - - #[cfg(all(unix))] - use crate::bolts::os::unix_signals::Signal; - - #[cfg(all(windows))] - use windows::Win32::System::Diagnostics::Debug::EXCEPTION_POINTERS; - - #[cfg(all(windows))] - use super::InProcessExecutorHandlerData; - - use crate::{ - executors::{ExitKind, HasObservers}, - inputs::Input, - observers::ObserversTuple, - state::{HasClientPerfMonitor, HasSolutions}, - }; - - use super::HasHandlerData; - - /// invokes the `post_exec` hook on all observer in case of panic - pub fn setup_bt_panic_hook(data: &'static D) - where - E: HasObservers, - OT: ObserversTuple, - I: Input, - D: HasHandlerData, - { - let old_hook = panic::take_hook(); - panic::set_hook(Box::new(move |panic_info| { - if data.is_valid() { - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - let input = data.current_input::(); - observers - .post_exec_all(state, input, &ExitKind::Crash) - .expect("Failed to run post_exec on observers"); - } - old_hook(panic_info); - })); - } - - /// invokes the `post_exec_child` hook on all observer in case the child process panics - pub fn setup_child_panic_hook(data: &'static D) - where - E: HasObservers, - OT: ObserversTuple, - I: Input, - D: HasHandlerData, - { - let old_hook = panic::take_hook(); - panic::set_hook(Box::new(move |panic_info| { - if data.is_valid() { - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - let input = data.current_input::(); - observers - .post_exec_child_all(state, input, &ExitKind::Crash) - .expect("Failed to run post_exec on observers"); - } - old_hook(panic_info); - })); - } - - /// invokes the `post_exec` hook on all observer in case the child process crashes - #[cfg(unix)] - pub fn child_crash_handler( - _signal: Signal, - _info: siginfo_t, - _context: &mut ucontext_t, - data: &D, - ) where - E: HasObservers, - OT: ObserversTuple, - S: HasSolutions + HasClientPerfMonitor, - I: Input, - D: HasHandlerData, - { - if data.is_valid() { - // redundant check, but better to be safe - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - let input = data.current_input::(); - observers - .post_exec_child_all(state, input, &ExitKind::Crash) - .expect("Failed to run post_exec on observers"); - } - } - - /// invokes the `post_exec` hook on all observer in case the child process crashes - #[cfg(windows)] - pub fn child_crash_handler( - _exception_pointers: *mut EXCEPTION_POINTERS, - data: &mut InProcessExecutorHandlerData, - ) where - E: HasObservers, - OT: ObserversTuple, - S: HasSolutions + HasClientPerfMonitor, - I: Input, - D: HasHandlerData, - { - if data.is_valid() { - // redundant check, but better to be safe - let executor = data.executor_mut::(); - let observers = executor.observers_mut(); - let state = data.state_mut::(); - let input = data.current_input::(); - observers - .post_exec_child_all(state, input, &ExitKind::Crash) - .expect("Failed to run post_exec on observers"); - } - } +/// Gets the inprocess [`Input`] +#[must_use] +pub fn inprocess_get_input<'a, I>() -> Option<&'a I> { + unsafe { (GLOBAL_STATE.current_input_ptr as *const I).as_ref() } } #[cfg(unix)] mod unix_signal_handler { use alloc::vec::Vec; - use core::{mem::transmute, ptr}; + use core::mem::transmute; use libc::siginfo_t; #[cfg(feature = "std")] - use std::io::{stdout, Write}; + use std::{ + io::{stdout, Write}, + panic, + }; use crate::{ bolts::os::unix_signals::{ucontext_t, Handler, Signal}, @@ -687,6 +561,78 @@ mod unix_signal_handler { } } + /// invokes the `post_exec` hook on all observer in case of panic + #[cfg(feature = "std")] + pub fn setup_panic_hook() + where + E: HasObservers, + EM: EventFirer + EventRestarter, + OT: ObserversTuple, + OF: Feedback, + S: HasSolutions + HasClientPerfMonitor, + I: Input, + Z: HasObjective, + { + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + old_hook(panic_info); + let data = unsafe { &mut GLOBAL_STATE }; + if data.is_valid() { + // We are fuzzing! + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + let input = data.current_input::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + observers + .post_exec_all(state, input, &ExitKind::Crash) + .expect("Observers post_exec_all failed"); + + let interesting = fuzzer + .objective_mut() + .is_interesting(state, event_mgr, input, observers, &ExitKind::Crash) + .expect("In timeout handler objective failure."); + + if interesting { + let mut new_testcase = Testcase::new(input.clone()); + new_testcase.add_metadata(ExitKind::Timeout); + fuzzer + .objective_mut() + .append_metadata(state, &mut new_testcase) + .expect("Failed adding metadata"); + state + .solutions_mut() + .add(new_testcase) + .expect("In timeout handler solutions failure."); + event_mgr + .fire( + state, + Event::Objective { + objective_size: state.solutions().count(), + }, + ) + .expect("Could not send timeouting input"); + } + + event_mgr.on_restart(state).unwrap(); + + #[cfg(feature = "std")] + println!("Waiting for broker..."); + event_mgr.await_restart_safe(); + #[cfg(feature = "std")] + println!("Bye!"); + + event_mgr.await_restart_safe(); + + unsafe { + libc::_exit(128 + 6); + } // SIGABRT exit code + } + })); + } + #[cfg(unix)] pub unsafe fn inproc_timeout_handler( _signal: Signal, @@ -702,26 +648,25 @@ mod unix_signal_handler { I: Input, Z: HasObjective, { - let state = (data.state_ptr as *mut S).as_mut().unwrap(); - let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); - let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); - let executor = (data.executor_ptr as *mut E).as_mut().unwrap(); - let observers = executor.observers_mut(); - - if data.current_input_ptr.is_null() { + if !data.is_valid() { #[cfg(feature = "std")] println!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing."); return; } + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + let input = data.take_current_input::(); + #[cfg(feature = "std")] println!("Timeout in fuzz run."); #[cfg(feature = "std")] let _res = stdout().flush(); - let input = (data.current_input_ptr as *const I).as_ref().unwrap(); - data.current_input_ptr = ptr::null(); - observers .post_exec_all(state, input, &ExitKind::Timeout) .expect("Observers post_exec_all failed"); @@ -789,53 +734,16 @@ mod unix_signal_handler { #[cfg(feature = "std")] eprintln!("Crashed with {}", signal); - if data.current_input_ptr.is_null() { - #[cfg(feature = "std")] - { - eprintln!("Double crash\n"); - #[cfg(target_os = "android")] - let si_addr = (_info._pad[0] as i64) | ((_info._pad[1] as i64) << 32); - #[cfg(not(target_os = "android"))] - let si_addr = { _info.si_addr() as usize }; - - eprintln!( - "We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.", - si_addr - ); - - #[cfg(all(feature = "std", unix))] - { - let mut writer = std::io::BufWriter::new(std::io::stderr()); - crate::bolts::minibsod::generate_minibsod(&mut writer, signal, _info, _context) - .unwrap(); - writer.flush().unwrap(); - } - } - - #[cfg(feature = "std")] - { - eprintln!("Type QUIT to restart the child"); - let mut line = String::new(); - while line.trim() != "QUIT" { - std::io::stdin().read_line(&mut line).unwrap(); - } - } - - // TODO tell the parent to not restart - } else { - let executor = (data.executor_ptr as *mut E).as_mut().unwrap(); + if data.is_valid() { + let executor = data.executor_mut::(); // disarms timeout in case of TimeoutExecutor executor.post_run_reset(); - let state = (data.state_ptr as *mut S).as_mut().unwrap(); - let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); - let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let observers = executor.observers_mut(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); - let input = (data.current_input_ptr as *const I).as_ref().unwrap(); - data.current_input_ptr = ptr::null(); - - #[cfg(feature = "std")] - eprintln!("Triggering post_exec_all from crash_handler"); + let input = data.take_current_input::(); observers .post_exec_all(state, input, &ExitKind::Crash) @@ -887,6 +795,39 @@ mod unix_signal_handler { event_mgr.await_restart_safe(); #[cfg(feature = "std")] eprintln!("Bye!"); + } else { + #[cfg(feature = "std")] + { + eprintln!("Double crash\n"); + #[cfg(target_os = "android")] + let si_addr = (_info._pad[0] as i64) | ((_info._pad[1] as i64) << 32); + #[cfg(not(target_os = "android"))] + let si_addr = { _info.si_addr() as usize }; + + eprintln!( + "We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.", + si_addr + ); + + #[cfg(all(feature = "std", unix))] + { + let mut writer = std::io::BufWriter::new(std::io::stderr()); + crate::bolts::minibsod::generate_minibsod(&mut writer, signal, _info, _context) + .unwrap(); + writer.flush().unwrap(); + } + } + + #[cfg(feature = "std")] + { + eprintln!("Type QUIT to restart the child"); + let mut line = String::new(); + while line.trim() != "QUIT" { + std::io::stdin().read_line(&mut line).unwrap(); + } + } + + // TODO tell the parent to not restart } libc::_exit(128 + (signal as i32)); @@ -899,7 +840,10 @@ mod windows_exception_handler { use core::ffi::c_void; use core::{mem::transmute, ptr}; #[cfg(feature = "std")] - use std::io::{stdout, Write}; + use std::{ + io::{stdout, Write}, + panic, + }; use crate::{ bolts::os::windows_exceptions::{ @@ -951,6 +895,97 @@ mod windows_exception_handler { EnterCriticalSection, LeaveCriticalSection, RTL_CRITICAL_SECTION, }; + /// invokes the `post_exec` hook on all observer in case of panic + #[cfg(feature = "std")] + pub fn setup_panic_hook() + where + E: HasObservers, + EM: EventFirer + EventRestarter, + OT: ObserversTuple, + OF: Feedback, + S: HasSolutions + HasClientPerfMonitor, + I: Input, + Z: HasObjective, + { + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + let data = unsafe { &mut GLOBAL_STATE }; + // Have we set a timer_before? + unsafe { + if !(data.tp_timer as *mut windows::Win32::System::Threading::TP_TIMER).is_null() { + /* + We want to prevent the timeout handler being run while the main thread is executing the crash handler + Timeout handler runs if it has access to the critical section or data.in_target == 0 + Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid. + */ + compiler_fence(Ordering::SeqCst); + EnterCriticalSection(data.critical as *mut RTL_CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + data.in_target = 0; + compiler_fence(Ordering::SeqCst); + LeaveCriticalSection(data.critical as *mut RTL_CRITICAL_SECTION); + compiler_fence(Ordering::SeqCst); + } + } + + if data.is_valid() { + // We are fuzzing! + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + + let input = data.take_current_input::(); + + observers + .post_exec_all(state, input, &ExitKind::Crash) + .expect("Observers post_exec_all failed"); + + let interesting = fuzzer + .objective_mut() + .is_interesting(state, event_mgr, input, observers, &ExitKind::Crash) + .expect("In timeout handler objective failure."); + + if interesting { + let mut new_testcase = Testcase::new(input.clone()); + new_testcase.add_metadata(ExitKind::Timeout); + fuzzer + .objective_mut() + .append_metadata(state, &mut new_testcase) + .expect("Failed adding metadata"); + state + .solutions_mut() + .add(new_testcase) + .expect("In timeout handler solutions failure."); + event_mgr + .fire( + state, + Event::Objective { + objective_size: state.solutions().count(), + }, + ) + .expect("Could not send timeouting input"); + } + + event_mgr.on_restart(state).unwrap(); + + #[cfg(feature = "std")] + println!("Waiting for broker..."); + event_mgr.await_restart_safe(); + #[cfg(feature = "std")] + println!("Bye!"); + + event_mgr.await_restart_safe(); + + unsafe { + ExitProcess(1); + } + } + old_hook(panic_info); + })); + } + pub unsafe extern "system" fn inproc_timeout_handler( _p0: *mut u8, global_state: *mut c_void, @@ -975,11 +1010,11 @@ mod windows_exception_handler { compiler_fence(Ordering::SeqCst); if data.in_target == 1 { - let state = (data.state_ptr as *mut S).as_mut().unwrap(); - let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); - let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); - let executor = (data.executor_ptr as *const E).as_ref().unwrap(); - let observers = executor.observers(); + let executor = data.executor_mut::(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + let observers = executor.observers_mut(); if data.timeout_input_ptr.is_null() { #[cfg(feature = "std")] @@ -993,6 +1028,10 @@ mod windows_exception_handler { let input = (data.timeout_input_ptr as *const I).as_ref().unwrap(); data.timeout_input_ptr = ptr::null_mut(); + observers + .post_exec_all(state, input, &ExitKind::Timeout) + .expect("Observers post_exec_all failed"); + let interesting = fuzzer .objective_mut() .is_interesting(state, event_mgr, input, observers, &ExitKind::Timeout) @@ -1049,6 +1088,7 @@ mod windows_exception_handler { // println!("TIMER INVOKED!"); } + #[allow(clippy::too_many_lines)] pub unsafe fn inproc_crash_handler( exception_pointers: *mut EXCEPTION_POINTERS, data: &mut InProcessExecutorHandlerData, @@ -1062,9 +1102,7 @@ mod windows_exception_handler { Z: HasObjective, { // Have we set a timer_before? - if let Some(_) = - (data.tp_timer as *mut windows::Win32::System::Threading::TP_TIMER).as_mut() - { + if !(data.tp_timer as *mut windows::Win32::System::Threading::TP_TIMER).is_null() { /* We want to prevent the timeout handler being run while the main thread is executing the crash handler Timeout handler runs if it has access to the critical section or data.in_target == 0 @@ -1121,26 +1159,34 @@ mod windows_exception_handler { // TODO tell the parent to not restart } else { - let executor = (data.executor_ptr as *mut E).as_mut().unwrap(); + let executor = data.executor_mut::(); // reset timer if !data.tp_timer.is_null() { executor.post_run_reset(); data.tp_timer = ptr::null_mut(); } - let state = (data.state_ptr as *mut S).as_mut().unwrap(); - let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); - let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); - let observers = executor.observers(); + let state = data.state_mut::(); + let fuzzer = data.fuzzer_mut::(); + let event_mgr = data.event_mgr_mut::(); + let observers = executor.observers_mut(); #[cfg(feature = "std")] eprintln!("Child crashed!"); #[cfg(feature = "std")] drop(stdout().flush()); - let input = (data.current_input_ptr as *const I).as_ref().unwrap(); // Make sure we don't crash in the crash handler forever. - data.current_input_ptr = ptr::null(); + let input = data.take_current_input::(); + + #[cfg(feature = "std")] + eprintln!("Child crashed!"); + #[cfg(feature = "std")] + drop(stdout().flush()); + + observers + .post_exec_all(state, input, &ExitKind::Crash) + .expect("Observers post_exec_all failed"); let interesting = fuzzer .objective_mut() @@ -1181,103 +1227,56 @@ mod windows_exception_handler { } } -/// The struct has [`InProcessHandlers`]. -#[cfg(windows)] -pub trait HasInProcessHandlers { - /// Get the in-process handlers. - fn inprocess_handlers(&self) -> &InProcessHandlers; -} - -#[cfg(windows)] -impl<'a, H, I, OT, S> HasInProcessHandlers for InProcessExecutor<'a, H, I, OT, S> -where - H: FnMut(&I) -> ExitKind, - I: Input, - OT: ObserversTuple, -{ - /// the timeout handler - #[inline] - fn inprocess_handlers(&self) -> &InProcessHandlers { - &self.handlers - } -} - /// The signature of the crash handler function #[cfg(all(feature = "std", unix))] pub type ForkHandlerFuncPtr = unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut InProcessForkExecutorGlobalData); -/// The signature of the crash handler function -#[cfg(all(feature = "std", windows))] -pub type ForkHandlerFuncPtr = - unsafe fn(*mut EXCEPTION_POINTERS, &mut InProcessForkExecutorGlobalData); - -/// The inmem executor's handlers. +/// The inmem fork executor's handlers. +#[cfg(all(feature = "std", unix))] #[derive(Debug)] pub struct InChildProcessHandlers { /// On crash C function pointer pub crash_handler: *const c_void, } -#[cfg(feature = "std")] +#[cfg(all(feature = "std", unix))] impl InChildProcessHandlers { /// Call before running a target. - pub fn pre_run_target( - &self, - _executor: &E, - _fuzzer: &mut Z, - _state: &mut S, - _mgr: &mut EM, - _input: &I, - ) { - #[cfg(any(unix, windows))] + pub fn pre_run_target(&self, executor: &E, state: &mut S, input: &I) { unsafe { let data = &mut FORK_EXECUTOR_GLOBAL_DATA; write_volatile( &mut data.executor_ptr, - _executor as *const _ as *const c_void, + executor as *const _ as *const c_void, ); write_volatile( &mut data.current_input_ptr, - _input as *const _ as *const c_void, + input as *const _ as *const c_void, ); - write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); + write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); data.crash_handler = self.crash_handler; compiler_fence(Ordering::SeqCst); } } /// Create new [`InChildProcessHandlers`]. - pub fn new() -> Result + pub fn new() -> Result where I: Input, E: HasObservers, OT: ObserversTuple, - EM: EventFirer + EventRestarter, - OF: Feedback, - S: HasSolutions + HasClientPerfMonitor, - Z: HasObjective, { - #[cfg(any(unix, windows))] - // unsafe - { - // let data = &mut FORK_EXECUTOR_GLOBAL_DATA; + unsafe { + let data = &mut FORK_EXECUTOR_GLOBAL_DATA; + child_signal_handlers::setup_child_panic_hook::(); + setup_signal_handler(data)?; compiler_fence(Ordering::SeqCst); Ok(Self { - crash_handler: bt_signal_handlers::child_crash_handler::< - E, - I, - OT, - S, - InProcessForkExecutorGlobalData, - > as *const c_void, + crash_handler: child_signal_handlers::child_crash_handler:: + as *const c_void, }) } - - #[cfg(not(any(unix, all(windows, feature = "std"))))] - Ok(Self { - crash_handler: ptr::null(), - }) } /// Replace the handlers with `nop` handlers, deactivating the handlers @@ -1290,6 +1289,7 @@ impl InChildProcessHandlers { } /// The global state of the in-process-fork harness. +#[cfg(all(feature = "std", unix))] #[derive(Debug)] pub struct InProcessForkExecutorGlobalData { /// Stores a pointer to the fork executor struct @@ -1302,28 +1302,29 @@ pub struct InProcessForkExecutorGlobalData { pub crash_handler: *const c_void, } +#[cfg(all(feature = "std", unix))] unsafe impl Sync for InProcessForkExecutorGlobalData {} +#[cfg(all(feature = "std", unix))] unsafe impl Send for InProcessForkExecutorGlobalData {} -impl HasHandlerData for InProcessForkExecutorGlobalData { - fn executor(&self) -> &E { - unsafe { (self.executor_ptr as *const E).as_ref().unwrap() } - } - - fn executor_mut(&self) -> &mut E { +#[cfg(all(feature = "std", unix))] +impl InProcessForkExecutorGlobalData { + fn executor_mut<'a, E>(&self) -> &'a mut E { unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() } } - fn state(&self) -> &S { - unsafe { (self.state_ptr as *const S).as_ref().unwrap() } - } - - fn state_mut(&self) -> &mut S { + fn state_mut<'a, S>(&self) -> &'a mut S { unsafe { (self.state_ptr as *mut S).as_mut().unwrap() } } - fn current_input(&self) -> &I { + /*fn current_input<'a, I>(&self) -> &'a I { unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() } + }*/ + + fn take_current_input<'a, I>(&mut self) -> &'a I { + let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() }; + self.current_input_ptr = ptr::null(); + r } fn is_valid(&self) -> bool { @@ -1332,6 +1333,7 @@ impl HasHandlerData for InProcessForkExecutorGlobalData { } /// a static variable storing the global state +#[cfg(all(feature = "std", unix))] pub static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData = InProcessForkExecutorGlobalData { executor_ptr: ptr::null(), @@ -1340,9 +1342,8 @@ pub static mut FORK_EXECUTOR_GLOBAL_DATA: InProcessForkExecutorGlobalData = current_input_ptr: ptr::null(), }; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", unix))] impl Handler for InProcessForkExecutorGlobalData { - #[cfg(unix)] fn handle(&mut self, signal: Signal, info: siginfo_t, context: &mut ucontext_t) { match signal { Signal::SigUser2 | Signal::SigAlarm => (), @@ -1356,7 +1357,6 @@ impl Handler for InProcessForkExecutorGlobalData { } } - #[cfg(unix)] fn signals(&self) -> Vec { vec![ Signal::SigAlarm, @@ -1370,23 +1370,6 @@ impl Handler for InProcessForkExecutorGlobalData { Signal::SigTrap, ] } - - #[cfg(windows)] - #[allow(clippy::not_unsafe_ptr_arg_deref)] - fn handle(&mut self, _code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) { - unsafe { - let data = &mut FORK_EXECUTOR_GLOBAL_DATA; - if !data.crash_handler.is_null() { - let func: ForkHandlerFuncPtr = transmute(data.crash_handler); - (func)(exception_pointers, data); - } - } - } - - #[cfg(windows)] - fn exceptions(&self) -> Vec { - CRASH_EXCEPTIONS.to_vec() - } } /// [`InProcessForkExecutor`] is an executor that forks the current process before each execution. @@ -1434,9 +1417,9 @@ where #[inline] fn run_target( &mut self, - fuzzer: &mut Z, + _fuzzer: &mut Z, state: &mut S, - mgr: &mut EM, + _mgr: &mut EM, input: &I, ) -> Result { unsafe { @@ -1446,41 +1429,26 @@ where // Child self.shmem_provider.post_fork(true)?; - self.handlers - .pre_run_target(self, fuzzer, state, mgr, input); + self.handlers.pre_run_target(self, state, input); - match self - .observers() - .match_name::("BacktraceObserver") - .unwrap() - .harness_type() - { - crate::observers::HarnessType::FFI => { - setup_signal_handler(&mut FORK_EXECUTOR_GLOBAL_DATA)?; - } - crate::observers::HarnessType::RUST => { - setup_child_panic_hook::< - InProcessForkExecutor, - I, - OT, - S, - InProcessForkExecutorGlobalData, - >(&FORK_EXECUTOR_GLOBAL_DATA); - } - } + self.observers + .pre_exec_child_all(state, input) + .expect("Failed to run post_exec on observers"); (self.harness_fn)(input); + self.observers + .post_exec_child_all(state, input, &ExitKind::Ok) + .expect("Failed to run post_exec on observers"); + std::process::exit(0); Ok(ExitKind::Ok) } Ok(ForkResult::Parent { child }) => { // Parent - println!("from parent {} child is {}", std::process::id(), child); + // println!("from parent {} child is {}", std::process::id(), child); self.shmem_provider.post_fork(false)?; - self.handlers - .pre_run_target(self, fuzzer, state, mgr, input); let res = waitpid(child, None)?; @@ -1518,15 +1486,7 @@ where S: HasSolutions + HasClientPerfMonitor, Z: HasObjective, { - // should match on type when it's available - let handlers = match observers.match_name::("BacktraceObserver") { - Some(_) => { - BacktraceObserver::setup_shmem(); - InChildProcessHandlers::new::()? - } - None => InChildProcessHandlers::nop(), - }; - + let handlers = InChildProcessHandlers::new::()?; Ok(Self { harness_fn, shmem_provider, @@ -1568,6 +1528,74 @@ where } } +/// signal handlers and `panic_hooks` for the child process +#[cfg(all(feature = "std", unix))] +pub mod child_signal_handlers { + use libc::{siginfo_t, ucontext_t}; + use std::panic; + + use super::InProcessForkExecutorGlobalData; + + use super::FORK_EXECUTOR_GLOBAL_DATA; + use crate::{ + bolts::os::unix_signals::Signal, + executors::{ExitKind, HasObservers}, + inputs::Input, + observers::ObserversTuple, + }; + + /// invokes the `post_exec_child` hook on all observer in case the child process panics + pub fn setup_child_panic_hook() + where + E: HasObservers, + OT: ObserversTuple, + I: Input, + { + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + old_hook(panic_info); + let data = unsafe { &mut FORK_EXECUTOR_GLOBAL_DATA }; + if data.is_valid() { + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + // Invalidate data to not execute again the observer hooks in the crash handler + let input = data.take_current_input::(); + observers + .post_exec_child_all(state, input, &ExitKind::Crash) + .expect("Failed to run post_exec on observers"); + + std::process::abort(); + } + })); + } + + /// invokes the `post_exec` hook on all observer in case the child process crashes + #[cfg(unix)] + pub unsafe fn child_crash_handler( + _signal: Signal, + _info: siginfo_t, + _context: &mut ucontext_t, + data: &mut InProcessForkExecutorGlobalData, + ) where + E: HasObservers, + OT: ObserversTuple, + I: Input, + { + if data.is_valid() { + let executor = data.executor_mut::(); + let observers = executor.observers_mut(); + let state = data.state_mut::(); + let input = data.take_current_input::(); + observers + .post_exec_child_all(state, input, &ExitKind::Crash) + .expect("Failed to run post_exec on observers"); + } + + //libc::_exit(128 + (_signal as i32)); + } +} + #[cfg(test)] mod tests { use core::marker::PhantomData; diff --git a/libafl/src/feedbacks/new_hash_feedback.rs b/libafl/src/feedbacks/new_hash_feedback.rs index 50b22b131c..ce6c0eae42 100644 --- a/libafl/src/feedbacks/new_hash_feedback.rs +++ b/libafl/src/feedbacks/new_hash_feedback.rs @@ -94,7 +94,7 @@ where fn update_hash_set(&mut self, value: T) -> Result { let r = self.hash_set.insert(value); - println!("Got r={}, the hashset is {:?}", r, &self.hash_set); + // println!("Got r={}, the hashset is {:?}", r, &self.hash_set); Ok(r) } } diff --git a/libafl/src/observers/stacktrace.rs b/libafl/src/observers/stacktrace.rs index 1f59af8e82..4bd9acd169 100644 --- a/libafl/src/observers/stacktrace.rs +++ b/libafl/src/observers/stacktrace.rs @@ -1,25 +1,19 @@ //! the ``StacktraceObserver`` looks up the stacktrace on the execution thread and computes a hash for it for dedupe use crate::{ - bolts::{ - shmem::{ShMemProvider, StdShMemProvider}, - tuples::Named, - AsMutSlice, AsSlice, - }, + bolts::{ownedref::OwnedRefMut, tuples::Named}, executors::ExitKind, inputs::Input, observers::Observer, Error, }; -use ahash::AHasher; + use backtrace::Backtrace; use regex::Regex; use serde::{Deserialize, Serialize}; use std::{ - collections::hash_map::DefaultHasher, fmt::Debug, fs::{self, File}, - hash::Hasher, io::Read, path::Path, process::ChildStderr, @@ -27,197 +21,121 @@ use std::{ use super::ObserverWithHashField; -type StdShMem = ::ShMem; -/// A struct that stores needed information to persist the backtrace across prcesses/runs -#[derive(Debug)] -pub enum BacktraceHashValueWrapper { - /// shared memory instance - Shmem(Box), - /// static variable - StaticVariable((u64, u64)), - /// Neither is set - None, -} - -impl BacktraceHashValueWrapper { - /// store a hash value in the [`BacktraceHashValueWrapper`] - fn store_stacktrace_hash(&mut self, bt_hash: u64, input_hash: u64) { - match self { - Self::Shmem(shmem) => { - let map = shmem.as_mut_slice(); - let bt_hash_bytes = bt_hash.to_be_bytes(); - let input_hash_bytes = input_hash.to_be_bytes(); - map.copy_from_slice(&[bt_hash_bytes, input_hash_bytes].concat()); - } - Self::StaticVariable(_) => { - *self = Self::StaticVariable((bt_hash, input_hash)); - } - Self::None => panic!("BacktraceSharedMemoryWrapper is not set yet!"), - } - } - - /// get the hash value from the [`BacktraceHashValueWrapper`] - fn get_stacktrace_hash(&self) -> Result<(u64, u64), Error> { - match &self { - Self::Shmem(shmem) => { - let map = shmem.as_slice(); - Ok(( - u64::from_be_bytes(map[0..8].try_into()?), - u64::from_be_bytes(map[8..16].try_into()?), - )) - } - Self::StaticVariable(hash_tuple) => Ok(*hash_tuple), - Self::None => Err(Error::IllegalState( - "BacktraceSharedMemoryWrapper is not set yet!".into(), - )), - } - } -} - -// Used for fuzzers not running in the same process -/// Static variable storing shared memory information -pub static mut BACKTRACE_HASH_VALUE: BacktraceHashValueWrapper = BacktraceHashValueWrapper::None; - /// Collects the backtrace via [`Backtrace`] and [`Debug`] /// ([`Debug`] is currently used for dev purposes, symbols hash will be used eventually) #[must_use] pub fn collect_backtrace() -> u64 { let b = Backtrace::new(); + if b.frames().is_empty() { + return 0; + } + let mut hash = 0; + for frame in &b.frames()[1..] { + hash ^= frame.ip() as u64; + } // will use symbols later - let trace = format!("{:?}", b); - eprintln!("{}", trace); - let mut hasher = AHasher::new_with_keys(0, 0); - hasher.write(trace.as_bytes()); - let hash = hasher.finish(); - println!( - "backtrace collected with hash={} at pid={}", - hash, - std::process::id() - ); + // let trace = format!("{:?}", b); + // eprintln!("{}", trace); + // println!( + // "backtrace collected with hash={} at pid={}", + // hash, + // std::process::id() + // ); hash } /// An enum encoding the types of harnesses #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub enum HarnessType { - /// Harness type when the harness is rust code - RUST, - /// Harness type when the harness is linked via FFI (e.g C code) - FFI, + /// Harness type when the target is in the same process + InProcess, + /// Harness type when the target is a child process + Child, } /// An observer looking at the backtrace after the harness crashes #[allow(clippy::unsafe_derive_deserialize)] -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct BacktraceObserver { +#[derive(Serialize, Deserialize, Debug)] +pub struct BacktraceObserver<'a> { observer_name: String, + hash: OwnedRefMut<'a, Option>, harness_type: HarnessType, - hash: Option, } -impl BacktraceObserver { +impl<'a> BacktraceObserver<'a> { /// Creates a new [`BacktraceObserver`] with the given name. #[must_use] - pub fn new(observer_name: &str, harness_type: HarnessType) -> Self { + pub fn new( + observer_name: &str, + backtrace_hash: &'a mut Option, + harness_type: HarnessType, + ) -> Self { Self { observer_name: observer_name.to_string(), + hash: OwnedRefMut::Ref(backtrace_hash), harness_type, - hash: None, } } - - /// Setup the shared memory and store it in [`BACKTRACE_HASH_VALUE`] - pub fn setup_shmem() { - let shmem_provider = StdShMemProvider::new(); - let mut shmem = shmem_provider.unwrap().new_shmem(16).unwrap(); - shmem.as_mut_slice().fill(0); - let boxed_shmem = Box::::new(shmem); - unsafe { - BACKTRACE_HASH_VALUE = BacktraceHashValueWrapper::Shmem(boxed_shmem); - } - } - - /// Init the [`BACKTRACE_HASH_VALUE`] to [`BacktraceHashValueWrapper::StaticVariable`] with `(0.0)` - pub fn setup_static_variable() { - unsafe { - BACKTRACE_HASH_VALUE = BacktraceHashValueWrapper::StaticVariable((0, 0)); - } - } - - /// returns `harness_type` for this [`BacktraceObserver`] instance - #[must_use] - pub fn harness_type(&self) -> &HarnessType { - &self.harness_type - } } -impl ObserverWithHashField for BacktraceObserver { +impl<'a> ObserverWithHashField for BacktraceObserver<'a> { /// Gets the hash value of this observer. #[must_use] fn hash(&self) -> &Option { - &self.hash + self.hash.as_ref() } /// Updates the hash value of this observer. fn update_hash(&mut self, hash: u64) { - self.hash = Some(hash); + *self.hash.as_mut() = Some(hash); } /// Clears the current hash value fn clear_hash(&mut self) { - self.hash = None; + *self.hash.as_mut() = None; } } -impl Default for BacktraceObserver { - fn default() -> Self { - Self::new("BacktraceObserver", HarnessType::RUST) - } -} - -impl Observer for BacktraceObserver +impl<'a, I, S> Observer for BacktraceObserver<'a> where I: Input + Debug, { - fn post_exec(&mut self, _state: &mut S, input: &I, exit_kind: &ExitKind) -> Result<(), Error> { - // run if this call resulted after a crash - if exit_kind == &ExitKind::Crash { - // hash input - let mut hasher = DefaultHasher::new(); - input.hash(&mut hasher); - let input_hash = hasher.finish(); - // get last backtrace hash and associated input hash - let (bt_hash, current_input_hash) = - unsafe { BACKTRACE_HASH_VALUE.get_stacktrace_hash()? }; - // replace if this is a new input - if current_input_hash != input_hash { - let bt_hash = collect_backtrace(); - unsafe { BACKTRACE_HASH_VALUE.store_stacktrace_hash(bt_hash, input_hash) }; + fn post_exec(&mut self, _state: &mut S, _input: &I, exit_kind: &ExitKind) -> Result<(), Error> { + if self.harness_type == HarnessType::InProcess { + if exit_kind == &ExitKind::Crash { + self.update_hash(collect_backtrace()); + } else { + self.clear_hash(); } - // update hash field in this observer - self.update_hash(bt_hash); } Ok(()) } fn post_exec_child( &mut self, - state: &mut S, - input: &I, + _state: &mut S, + _input: &I, exit_kind: &ExitKind, ) -> Result<(), Error> { - self.post_exec(state, input, exit_kind) + if self.harness_type == HarnessType::Child { + if exit_kind == &ExitKind::Crash { + self.update_hash(collect_backtrace()); + } else { + self.clear_hash(); + } + } + Ok(()) } } -impl Named for BacktraceObserver { +impl<'a> Named for BacktraceObserver<'a> { fn name(&self) -> &str { &self.observer_name } } /// static variable of ASAN log path -pub static ASAN_LOG_PATH: &str = "./asanlog"; +pub static ASAN_LOG_PATH: &str = "./asanlog"; // TODO make it unique /// returns the recommended ASAN runtime flags to capture the backtrace correctly with `log_path` set #[must_use] @@ -287,13 +205,17 @@ impl ASANBacktraceObserver { /// parse ASAN error output emited by the target command and compute the hash pub fn parse_asan_output(&mut self, output: &str) { - let mut hasher = AHasher::new_with_keys(0, 0); + let mut hash = 0; let matcher = Regex::new("\\s*#[0-9]*\\s0x[0-9a-f]*\\sin\\s(.*)").unwrap(); matcher.captures_iter(output).for_each(|m| { let g = m.get(1).unwrap(); - hasher.write(g.as_str().as_bytes()); + hash ^= g.as_str().parse::().unwrap(); + println!( + ">> {} {:#x}", + g.as_str(), + g.as_str().parse::().unwrap() + ); }); - let hash = hasher.finish(); self.update_hash(hash); } }