diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/fuzzer.rs index 4640433c40..486dbd27ae 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/fuzzer.rs @@ -1,6 +1,7 @@ //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! The example harness is built for libpng. +use core::time::Duration; use std::{env, path::PathBuf}; #[cfg(unix)] @@ -11,8 +12,8 @@ use libafl::{ QueueCorpusScheduler, }, events::setup_restarting_mgr, - executors::{inprocess::InProcessExecutor, Executor, ExitKind}, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, + executors::{inprocess::InProcessExecutor, inprocess::TimeoutExecutor, Executor, ExitKind}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, inputs::Input, mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, @@ -117,7 +118,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new()), + tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), ) }); @@ -143,13 +144,17 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); // Create the executor for an in-process function with just one observer for edge coverage - let mut executor = InProcessExecutor::new( - "in-process(edges)", - harness, - tuple_list!(edges_observer, TimeObserver::new("time")), - &mut state, - &mut restarting_mgr, - )?; + let mut executor = TimeoutExecutor::new( + InProcessExecutor::new( + "in-process(edges)", + harness, + tuple_list!(edges_observer, TimeObserver::new("time")), + &mut state, + &mut restarting_mgr, + )?, + // 10 seconds timeout + Duration::new(10, 0), + ); // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index a890be9518..beb34f71fb 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -2,6 +2,14 @@ //! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective. use core::marker::PhantomData; + +#[cfg(unix)] +use core::time::Duration; +#[cfg(unix)] +use std::os::raw::c_int; +#[cfg(unix)] +use std::ptr::null_mut; + #[cfg(unix)] use core::{ ptr::{self, write_volatile}, @@ -164,6 +172,152 @@ where } } +#[repr(C)] +#[cfg(unix)] +struct Timeval { + pub tv_sec: i64, + pub tv_usec: i64, +} + +#[repr(C)] +#[cfg(unix)] +struct Itimerval { + pub it_interval: Timeval, + pub it_value: Timeval, +} + +#[cfg(unix)] +extern "C" { + fn setitimer(which: c_int, new_value: *mut Itimerval, old_value: *mut Itimerval) -> c_int; +} + +#[cfg(unix)] +const ITIMER_REAL: c_int = 0; + +//timeout excutor wrap a InProcessExecutor +#[cfg(unix)] +pub struct TimeoutExecutor +where + EX: Executor + HasObservers, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + executor: EX, + exec_tmout: Duration, + phantom: PhantomData<(I, OT)>, +} + +impl Named for TimeoutExecutor +where + EX: Executor + HasObservers, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + fn name(&self) -> &str { + self.executor.name() + } +} + +impl HasObservers for TimeoutExecutor +where + EX: Executor + HasObservers, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + #[inline] + fn observers(&self) -> &OT { + self.executor.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.executor.observers_mut() + } +} + +impl TimeoutExecutor +where + EX: Executor + HasObservers, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + pub fn new(executor: EX, exec_tmout: Duration) -> Self { + Self { + executor, + exec_tmout, + phantom: PhantomData, + } + } +} + +impl Executor for TimeoutExecutor +where + EX: Executor + HasObservers, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + #[inline] + fn pre_exec, S>( + &mut self, + _state: &mut S, + _event_mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { + unsafe { + let milli_sec = self.exec_tmout.as_millis(); + let it_value = Timeval { + tv_sec: (milli_sec / 1000) as i64, + tv_usec: (milli_sec % 1000) as i64, + }; + let it_interval = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + setitimer( + ITIMER_REAL, + &mut Itimerval { + it_interval, + it_value, + }, + null_mut(), + ); + } + self.executor.pre_exec(_state, _event_mgr, _input) + } + + #[inline] + fn post_exec, S>( + &mut self, + _state: &S, + _event_mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { + unsafe { + let it_value = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + let it_interval = Timeval { + tv_sec: 0, + tv_usec: 0, + }; + setitimer( + ITIMER_REAL, + &mut Itimerval { + it_interval, + it_value, + }, + null_mut(), + ); + } + self.executor.post_exec(_state, _event_mgr, _input) + } + + fn run_target(&mut self, input: &I) -> Result { + self.executor.run_target(input) + } +} + #[cfg(unix)] mod unix_signal_handler { use alloc::vec::Vec; @@ -228,7 +382,9 @@ mod unix_signal_handler { unsafe { let data = &mut GLOBAL_STATE; match signal { - Signal::SigUser2 => (data.timeout_handler)(signal, info, void, data), + Signal::SigUser2 | Signal::SigAlarm => { + (data.timeout_handler)(signal, info, void, data) + } _ => (data.crash_handler)(signal, info, void, data), } } @@ -236,6 +392,7 @@ mod unix_signal_handler { fn signals(&self) -> Vec { vec![ + Signal::SigAlarm, Signal::SigUser2, Signal::SigAbort, Signal::SigBus, @@ -279,7 +436,7 @@ mod unix_signal_handler { let obj_fitness = state .objectives_mut() - .is_interesting_all(&input, observers, ExitKind::Crash) + .is_interesting_all(&input, observers, ExitKind::Timeout) .expect("In timeout handler objectives failure."); if obj_fitness > 0 { state @@ -425,7 +582,6 @@ mod tests { let mut in_process_executor = InProcessExecutor:: { harness_fn: test_harness_fn_nop, - // TODO: on_crash_fn: Box::new(|_, _, _, _, _| ()), observers: tuple_list!(), name: "main", phantom: PhantomData, diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 79dddaaf5d..5a156539eb 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -157,6 +157,46 @@ impl Default for CrashFeedback { } } +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct TimeoutFeedback {} + +impl Feedback for TimeoutFeedback +where + I: Input, +{ + fn is_interesting( + &mut self, + _input: &I, + _observers: &OT, + exit_kind: ExitKind, + ) -> Result { + if exit_kind == ExitKind::Timeout { + Ok(1) + } else { + Ok(0) + } + } +} + +impl Named for TimeoutFeedback { + #[inline] + fn name(&self) -> &str { + "TimeoutFeedback" + } +} + +impl TimeoutFeedback { + pub fn new() -> Self { + Self {} + } +} + +impl Default for TimeoutFeedback { + fn default() -> Self { + Self::new() + } +} + /// Nop feedback that annotates execution time in the new testcase, if any #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TimeFeedback {