Merge pull request #31 from AFLplusplus/timeout_executors
add timeouts for executors
This commit is contained in:
commit
5d92871b27
@ -1,6 +1,7 @@
|
|||||||
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
|
||||||
//! The example harness is built for libpng.
|
//! The example harness is built for libpng.
|
||||||
|
|
||||||
|
use core::time::Duration;
|
||||||
use std::{env, path::PathBuf};
|
use std::{env, path::PathBuf};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -11,8 +12,8 @@ use libafl::{
|
|||||||
QueueCorpusScheduler,
|
QueueCorpusScheduler,
|
||||||
},
|
},
|
||||||
events::setup_restarting_mgr,
|
events::setup_restarting_mgr,
|
||||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind},
|
executors::{inprocess::InProcessExecutor, inprocess::TimeoutExecutor, Executor, ExitKind},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||||
@ -117,7 +118,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(objective_dir).unwrap(),
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
// Feedbacks to recognize an input as solution
|
// 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<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
|||||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer for edge coverage
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
let mut executor = InProcessExecutor::new(
|
let mut executor = TimeoutExecutor::new(
|
||||||
"in-process(edges)",
|
InProcessExecutor::new(
|
||||||
harness,
|
"in-process(edges)",
|
||||||
tuple_list!(edges_observer, TimeObserver::new("time")),
|
harness,
|
||||||
&mut state,
|
tuple_list!(edges_observer, TimeObserver::new("time")),
|
||||||
&mut restarting_mgr,
|
&mut state,
|
||||||
)?;
|
&mut restarting_mgr,
|
||||||
|
)?,
|
||||||
|
// 10 seconds timeout
|
||||||
|
Duration::new(10, 0),
|
||||||
|
);
|
||||||
|
|
||||||
// The actual target run starts here.
|
// The actual target run starts here.
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
// Call LLVMFUzzerInitialize() if present.
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
|
//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective.
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
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)]
|
#[cfg(unix)]
|
||||||
use core::{
|
use core::{
|
||||||
ptr::{self, write_volatile},
|
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<I, OT, EX>
|
||||||
|
where
|
||||||
|
EX: Executor<I> + HasObservers<OT>,
|
||||||
|
I: Input + HasTargetBytes,
|
||||||
|
OT: ObserversTuple,
|
||||||
|
{
|
||||||
|
executor: EX,
|
||||||
|
exec_tmout: Duration,
|
||||||
|
phantom: PhantomData<(I, OT)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, OT, EX> Named for TimeoutExecutor<I, OT, EX>
|
||||||
|
where
|
||||||
|
EX: Executor<I> + HasObservers<OT>,
|
||||||
|
I: Input + HasTargetBytes,
|
||||||
|
OT: ObserversTuple,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.executor.name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, OT, EX> HasObservers<OT> for TimeoutExecutor<I, OT, EX>
|
||||||
|
where
|
||||||
|
EX: Executor<I> + HasObservers<OT>,
|
||||||
|
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<I, OT, EX> TimeoutExecutor<I, OT, EX>
|
||||||
|
where
|
||||||
|
EX: Executor<I> + HasObservers<OT>,
|
||||||
|
I: Input + HasTargetBytes,
|
||||||
|
OT: ObserversTuple,
|
||||||
|
{
|
||||||
|
pub fn new(executor: EX, exec_tmout: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
executor,
|
||||||
|
exec_tmout,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, OT, EX> Executor<I> for TimeoutExecutor<I, OT, EX>
|
||||||
|
where
|
||||||
|
EX: Executor<I> + HasObservers<OT>,
|
||||||
|
I: Input + HasTargetBytes,
|
||||||
|
OT: ObserversTuple,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn pre_exec<EM: EventManager<I, S>, 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<EM: EventManager<I, S>, 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<ExitKind, Error> {
|
||||||
|
self.executor.run_target(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod unix_signal_handler {
|
mod unix_signal_handler {
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@ -228,7 +382,9 @@ mod unix_signal_handler {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let data = &mut GLOBAL_STATE;
|
let data = &mut GLOBAL_STATE;
|
||||||
match signal {
|
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),
|
_ => (data.crash_handler)(signal, info, void, data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,6 +392,7 @@ mod unix_signal_handler {
|
|||||||
|
|
||||||
fn signals(&self) -> Vec<Signal> {
|
fn signals(&self) -> Vec<Signal> {
|
||||||
vec![
|
vec![
|
||||||
|
Signal::SigAlarm,
|
||||||
Signal::SigUser2,
|
Signal::SigUser2,
|
||||||
Signal::SigAbort,
|
Signal::SigAbort,
|
||||||
Signal::SigBus,
|
Signal::SigBus,
|
||||||
@ -279,7 +436,7 @@ mod unix_signal_handler {
|
|||||||
|
|
||||||
let obj_fitness = state
|
let obj_fitness = state
|
||||||
.objectives_mut()
|
.objectives_mut()
|
||||||
.is_interesting_all(&input, observers, ExitKind::Crash)
|
.is_interesting_all(&input, observers, ExitKind::Timeout)
|
||||||
.expect("In timeout handler objectives failure.");
|
.expect("In timeout handler objectives failure.");
|
||||||
if obj_fitness > 0 {
|
if obj_fitness > 0 {
|
||||||
state
|
state
|
||||||
@ -425,7 +582,6 @@ mod tests {
|
|||||||
|
|
||||||
let mut in_process_executor = InProcessExecutor::<NopInput, ()> {
|
let mut in_process_executor = InProcessExecutor::<NopInput, ()> {
|
||||||
harness_fn: test_harness_fn_nop,
|
harness_fn: test_harness_fn_nop,
|
||||||
// TODO: on_crash_fn: Box::new(|_, _, _, _, _| ()),
|
|
||||||
observers: tuple_list!(),
|
observers: tuple_list!(),
|
||||||
name: "main",
|
name: "main",
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
@ -157,6 +157,46 @@ impl Default for CrashFeedback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct TimeoutFeedback {}
|
||||||
|
|
||||||
|
impl<I> Feedback<I> for TimeoutFeedback
|
||||||
|
where
|
||||||
|
I: Input,
|
||||||
|
{
|
||||||
|
fn is_interesting<OT: ObserversTuple>(
|
||||||
|
&mut self,
|
||||||
|
_input: &I,
|
||||||
|
_observers: &OT,
|
||||||
|
exit_kind: ExitKind,
|
||||||
|
) -> Result<u32, Error> {
|
||||||
|
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
|
/// Nop feedback that annotates execution time in the new testcase, if any
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct TimeFeedback {
|
pub struct TimeFeedback {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user