Merge TimeoutForkserverExecutor into ForkserverExecutor (#1819)
* delete timeout forkserver * clippies * name --------- Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
519ea435ed
commit
a4f753b0f0
@ -5,10 +5,7 @@ use clap::{self, Parser};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::{
|
executors::{forkserver::ForkserverExecutor, HasObservers},
|
||||||
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
|
||||||
HasObservers,
|
|
||||||
},
|
|
||||||
feedback_and_fast, feedback_or,
|
feedback_and_fast, feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
@ -165,31 +162,26 @@ pub fn main() {
|
|||||||
let args = opt.arguments;
|
let args = opt.arguments;
|
||||||
|
|
||||||
let mut tokens = Tokens::new();
|
let mut tokens = Tokens::new();
|
||||||
let mut forkserver = ForkserverExecutor::builder()
|
let mut executor = ForkserverExecutor::builder()
|
||||||
.program(opt.executable)
|
.program(opt.executable)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.parse_afl_cmdline(args)
|
.parse_afl_cmdline(args)
|
||||||
.coverage_map_size(MAP_SIZE)
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.timeout(Duration::from_millis(opt.timeout))
|
||||||
|
.kill_signal(opt.signal)
|
||||||
.build(tuple_list!(time_observer, edges_observer))
|
.build(tuple_list!(time_observer, edges_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
|
if let Some(dynamic_map_size) = executor.coverage_map_size() {
|
||||||
forkserver
|
executor
|
||||||
.observers_mut()
|
.observers_mut()
|
||||||
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.truncate(dynamic_map_size);
|
.truncate(dynamic_map_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut executor = TimeoutForkserverExecutor::with_signal(
|
|
||||||
forkserver,
|
|
||||||
Duration::from_millis(opt.timeout),
|
|
||||||
opt.signal,
|
|
||||||
)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.must_load_initial_inputs() {
|
if state.must_load_initial_inputs() {
|
||||||
state
|
state
|
||||||
|
@ -5,10 +5,7 @@ use clap::{self, Parser};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::{
|
executors::{forkserver::ForkserverExecutor, HasObservers},
|
||||||
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
|
||||||
HasObservers,
|
|
||||||
},
|
|
||||||
feedback_and_fast, feedback_or,
|
feedback_and_fast, feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
@ -165,31 +162,26 @@ pub fn main() {
|
|||||||
let args = opt.arguments;
|
let args = opt.arguments;
|
||||||
|
|
||||||
let mut tokens = Tokens::new();
|
let mut tokens = Tokens::new();
|
||||||
let mut forkserver = ForkserverExecutor::builder()
|
let mut executor = ForkserverExecutor::builder()
|
||||||
.program(opt.executable)
|
.program(opt.executable)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.parse_afl_cmdline(args)
|
.parse_afl_cmdline(args)
|
||||||
.coverage_map_size(MAP_SIZE)
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.timeout(Duration::from_millis(opt.timeout))
|
||||||
|
.kill_signal(opt.signal)
|
||||||
.build(tuple_list!(time_observer, edges_observer))
|
.build(tuple_list!(time_observer, edges_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
|
if let Some(dynamic_map_size) = executor.coverage_map_size() {
|
||||||
forkserver
|
executor
|
||||||
.observers_mut()
|
.observers_mut()
|
||||||
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.truncate(dynamic_map_size);
|
.truncate(dynamic_map_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut executor = TimeoutForkserverExecutor::with_signal(
|
|
||||||
forkserver,
|
|
||||||
Duration::from_millis(opt.timeout),
|
|
||||||
opt.signal,
|
|
||||||
)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.must_load_initial_inputs() {
|
if state.must_load_initial_inputs() {
|
||||||
state
|
state
|
||||||
|
@ -11,7 +11,7 @@ use clap::{Arg, ArgAction, Command};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
executors::forkserver::ForkserverExecutor,
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
@ -310,20 +310,19 @@ fn fuzz(
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
let mut tokens = Tokens::new();
|
let mut tokens = Tokens::new();
|
||||||
let forkserver = ForkserverExecutor::builder()
|
let mut executor = ForkserverExecutor::builder()
|
||||||
.program(executable)
|
.program(executable)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.parse_afl_cmdline(arguments)
|
.parse_afl_cmdline(arguments)
|
||||||
.coverage_map_size(MAP_SIZE)
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.timeout(timeout)
|
||||||
|
.kill_signal(signal)
|
||||||
.is_persistent(true)
|
.is_persistent(true)
|
||||||
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut executor = TimeoutForkserverExecutor::with_signal(forkserver, timeout, signal)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
// Read tokens
|
// Read tokens
|
||||||
if let Some(tokenfile) = tokenfile {
|
if let Some(tokenfile) = tokenfile {
|
||||||
tokens.add_from_file(tokenfile)?;
|
tokens.add_from_file(tokenfile)?;
|
||||||
@ -349,19 +348,17 @@ fn fuzz(
|
|||||||
|
|
||||||
let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true);
|
let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true);
|
||||||
|
|
||||||
let cmplog_forkserver = ForkserverExecutor::builder()
|
let cmplog_executor = ForkserverExecutor::builder()
|
||||||
.program(exec)
|
.program(exec)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.parse_afl_cmdline(arguments)
|
.parse_afl_cmdline(arguments)
|
||||||
.is_persistent(true)
|
.is_persistent(true)
|
||||||
|
.timeout(timeout * 10)
|
||||||
|
.kill_signal(signal)
|
||||||
.build(tuple_list!(cmplog_observer))
|
.build(tuple_list!(cmplog_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cmplog_executor =
|
|
||||||
TimeoutForkserverExecutor::with_signal(cmplog_forkserver, timeout * 10, signal)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
let tracing = TracingStage::new(cmplog_executor);
|
let tracing = TracingStage::new(cmplog_executor);
|
||||||
|
|
||||||
// Setup a randomic Input2State stage
|
// Setup a randomic Input2State stage
|
||||||
|
@ -11,7 +11,7 @@ use clap::{Arg, ArgAction, Command};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx, InMemoryOnDiskCorpus, OnDiskCorpus},
|
corpus::{Corpus, HasCurrentCorpusIdx, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
executors::forkserver::ForkserverExecutor,
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
@ -314,20 +314,19 @@ fn fuzz(
|
|||||||
|
|
||||||
let colorization = ColorizationStage::new(&edges_observer);
|
let colorization = ColorizationStage::new(&edges_observer);
|
||||||
let mut tokens = Tokens::new();
|
let mut tokens = Tokens::new();
|
||||||
let forkserver = ForkserverExecutor::builder()
|
let mut executor = ForkserverExecutor::builder()
|
||||||
.program(executable)
|
.program(executable)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.parse_afl_cmdline(arguments)
|
.parse_afl_cmdline(arguments)
|
||||||
.coverage_map_size(MAP_SIZE)
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.timeout(timeout)
|
||||||
|
.kill_signal(signal)
|
||||||
.is_persistent(true)
|
.is_persistent(true)
|
||||||
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut executor = TimeoutForkserverExecutor::with_signal(forkserver, timeout, signal)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
// Read tokens
|
// Read tokens
|
||||||
if let Some(tokenfile) = tokenfile {
|
if let Some(tokenfile) = tokenfile {
|
||||||
tokens.add_from_file(tokenfile)?;
|
tokens.add_from_file(tokenfile)?;
|
||||||
@ -353,19 +352,17 @@ fn fuzz(
|
|||||||
|
|
||||||
let cmplog_observer = AFLppCmpLogObserver::new("cmplog", cmpmap, true);
|
let cmplog_observer = AFLppCmpLogObserver::new("cmplog", cmpmap, true);
|
||||||
|
|
||||||
let cmplog_forkserver = ForkserverExecutor::builder()
|
let cmplog_executor = ForkserverExecutor::builder()
|
||||||
.program(exec)
|
.program(exec)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.parse_afl_cmdline(arguments)
|
.parse_afl_cmdline(arguments)
|
||||||
.is_persistent(true)
|
.is_persistent(true)
|
||||||
|
.timeout(timeout * 10)
|
||||||
|
.kill_signal(signal)
|
||||||
.build(tuple_list!(cmplog_observer))
|
.build(tuple_list!(cmplog_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let cmplog_executor =
|
|
||||||
TimeoutForkserverExecutor::with_signal(cmplog_forkserver, timeout * 10, signal)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
let tracing = AFLppCmplogTracingStage::with_cmplog_observer_name(cmplog_executor, "cmplog");
|
let tracing = AFLppCmplogTracingStage::with_cmplog_observer_name(cmplog_executor, "cmplog");
|
||||||
|
|
||||||
// Setup a randomic Input2State stage
|
// Setup a randomic Input2State stage
|
||||||
|
@ -4,7 +4,6 @@ use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
|
|||||||
use core::{
|
use core::{
|
||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
sync::atomic::{compiler_fence, Ordering},
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -19,14 +18,14 @@ use libafl_bolts::{
|
|||||||
fs::{get_unique_std_input_file, InputFile},
|
fs::{get_unique_std_input_file, InputFile},
|
||||||
os::{dup2, pipes::Pipe},
|
os::{dup2, pipes::Pipe},
|
||||||
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{MatchName, Prepend},
|
tuples::Prepend,
|
||||||
AsMutSlice, AsSlice, Truncate,
|
AsMutSlice, AsSlice, Truncate,
|
||||||
};
|
};
|
||||||
use nix::{
|
use nix::{
|
||||||
sys::{
|
sys::{
|
||||||
select::{pselect, FdSet},
|
select::{pselect, FdSet},
|
||||||
signal::{kill, SigSet, Signal},
|
signal::{kill, SigSet, Signal},
|
||||||
time::{TimeSpec, TimeValLike},
|
time::TimeSpec,
|
||||||
wait::waitpid,
|
wait::waitpid,
|
||||||
},
|
},
|
||||||
unistd::Pid,
|
unistd::Pid,
|
||||||
@ -466,175 +465,6 @@ impl Forkserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct that has a forkserver
|
|
||||||
pub trait HasForkserver {
|
|
||||||
/// The [`ShMemProvider`] used for this forkserver's map
|
|
||||||
type SP: ShMemProvider;
|
|
||||||
|
|
||||||
/// The forkserver
|
|
||||||
fn forkserver(&self) -> &Forkserver;
|
|
||||||
|
|
||||||
/// The forkserver, mutable
|
|
||||||
fn forkserver_mut(&mut self) -> &mut Forkserver;
|
|
||||||
|
|
||||||
/// The file the forkserver is reading from
|
|
||||||
fn input_file(&self) -> &InputFile;
|
|
||||||
|
|
||||||
/// The file the forkserver is reading from, mutable
|
|
||||||
fn input_file_mut(&mut self) -> &mut InputFile;
|
|
||||||
|
|
||||||
/// The map of the fuzzer
|
|
||||||
fn shmem(&self) -> &Option<<<Self as HasForkserver>::SP as ShMemProvider>::ShMem>;
|
|
||||||
|
|
||||||
/// The map of the fuzzer, mutable
|
|
||||||
fn shmem_mut(&mut self) -> &mut Option<<<Self as HasForkserver>::SP as ShMemProvider>::ShMem>;
|
|
||||||
|
|
||||||
/// Whether testcases are expected in shared memory
|
|
||||||
fn uses_shmem_testcase(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The timeout forkserver executor that wraps around the standard forkserver executor and sets a timeout before each run.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TimeoutForkserverExecutor<E> {
|
|
||||||
executor: E,
|
|
||||||
timeout: TimeSpec,
|
|
||||||
signal: Signal,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> TimeoutForkserverExecutor<E> {
|
|
||||||
/// Create a new [`TimeoutForkserverExecutor`]
|
|
||||||
pub fn new(executor: E, exec_tmout: Duration) -> Result<Self, Error> {
|
|
||||||
let signal = Signal::SIGKILL;
|
|
||||||
Self::with_signal(executor, exec_tmout, signal)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new [`TimeoutForkserverExecutor`] that sends a user-defined signal to the timed-out process
|
|
||||||
pub fn with_signal(executor: E, exec_tmout: Duration, signal: Signal) -> Result<Self, Error> {
|
|
||||||
let milli_sec = exec_tmout.as_millis() as i64;
|
|
||||||
let timeout = TimeSpec::milliseconds(milli_sec);
|
|
||||||
Ok(Self {
|
|
||||||
executor,
|
|
||||||
timeout,
|
|
||||||
signal,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, Z> Executor<EM, Z> for TimeoutForkserverExecutor<E>
|
|
||||||
where
|
|
||||||
E: Executor<EM, Z> + HasForkserver + HasObservers,
|
|
||||||
E::Input: HasTargetBytes,
|
|
||||||
E::State: HasExecutions,
|
|
||||||
EM: UsesState<State = E::State>,
|
|
||||||
Z: UsesState<State = E::State>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn run_target(
|
|
||||||
&mut self,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
state: &mut Self::State,
|
|
||||||
_mgr: &mut EM,
|
|
||||||
input: &Self::Input,
|
|
||||||
) -> Result<ExitKind, Error> {
|
|
||||||
*state.executions_mut() += 1;
|
|
||||||
|
|
||||||
let mut exit_kind = ExitKind::Ok;
|
|
||||||
|
|
||||||
let last_run_timed_out = self.executor.forkserver().last_run_timed_out_raw();
|
|
||||||
|
|
||||||
if self.executor.uses_shmem_testcase() {
|
|
||||||
debug_assert!(
|
|
||||||
self.executor.shmem_mut().is_some(),
|
|
||||||
"The uses_shmem_testcase() bool can only exist when a map is set"
|
|
||||||
);
|
|
||||||
// # Safety
|
|
||||||
// Struct can never be created when uses_shmem_testcase is true and map is none.
|
|
||||||
let map = unsafe { self.executor.shmem_mut().as_mut().unwrap_unchecked() };
|
|
||||||
let target_bytes = input.target_bytes();
|
|
||||||
let mut size = target_bytes.as_slice().len();
|
|
||||||
let max_size = map.len() - SHMEM_FUZZ_HDR_SIZE;
|
|
||||||
if size > max_size {
|
|
||||||
// Truncate like AFL++ does
|
|
||||||
size = max_size;
|
|
||||||
}
|
|
||||||
let size_in_bytes = size.to_ne_bytes();
|
|
||||||
// The first four bytes tells the size of the shmem.
|
|
||||||
map.as_mut_slice()[..SHMEM_FUZZ_HDR_SIZE]
|
|
||||||
.copy_from_slice(&size_in_bytes[..SHMEM_FUZZ_HDR_SIZE]);
|
|
||||||
map.as_mut_slice()[SHMEM_FUZZ_HDR_SIZE..(SHMEM_FUZZ_HDR_SIZE + size)]
|
|
||||||
.copy_from_slice(&target_bytes.as_slice()[..size]);
|
|
||||||
} else {
|
|
||||||
self.executor
|
|
||||||
.input_file_mut()
|
|
||||||
.write_buf(input.target_bytes().as_slice())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let send_len = self
|
|
||||||
.executor
|
|
||||||
.forkserver_mut()
|
|
||||||
.write_ctl(last_run_timed_out)?;
|
|
||||||
|
|
||||||
self.executor.forkserver_mut().set_last_run_timed_out(false);
|
|
||||||
|
|
||||||
if send_len != 4 {
|
|
||||||
return Err(Error::unknown(
|
|
||||||
"Unable to request new process from fork server (OOM?)".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (recv_pid_len, pid) = self.executor.forkserver_mut().read_st()?;
|
|
||||||
if recv_pid_len != 4 {
|
|
||||||
return Err(Error::unknown(
|
|
||||||
"Unable to request new process from fork server (OOM?)".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if pid <= 0 {
|
|
||||||
return Err(Error::unknown(
|
|
||||||
"Fork server is misbehaving (OOM?)".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.executor
|
|
||||||
.forkserver_mut()
|
|
||||||
.set_child_pid(Pid::from_raw(pid));
|
|
||||||
|
|
||||||
if let Some(status) = self
|
|
||||||
.executor
|
|
||||||
.forkserver_mut()
|
|
||||||
.read_st_timed(&self.timeout)?
|
|
||||||
{
|
|
||||||
self.executor.forkserver_mut().set_status(status);
|
|
||||||
if libc::WIFSIGNALED(self.executor.forkserver().status()) {
|
|
||||||
exit_kind = ExitKind::Crash;
|
|
||||||
#[cfg(feature = "regex")]
|
|
||||||
if let Some(asan_observer) = self
|
|
||||||
.observers_mut()
|
|
||||||
.match_name_mut::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
|
||||||
{
|
|
||||||
asan_observer.parse_asan_output_from_asan_log_file(pid)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.executor.forkserver_mut().set_last_run_timed_out(true);
|
|
||||||
|
|
||||||
// We need to kill the child in case he has timed out, or we can't get the correct pid in the next call to self.executor.forkserver_mut().read_st()?
|
|
||||||
let _ = kill(self.executor.forkserver().child_pid(), self.signal);
|
|
||||||
let (recv_status_len, _) = self.executor.forkserver_mut().read_st()?;
|
|
||||||
if recv_status_len != 4 {
|
|
||||||
return Err(Error::unknown("Could not kill timed-out child".to_string()));
|
|
||||||
}
|
|
||||||
exit_kind = ExitKind::Timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !libc::WIFSTOPPED(self.executor.forkserver().status()) {
|
|
||||||
self.executor.forkserver_mut().reset_child_pid();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(exit_kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This [`Executor`] can run binaries compiled for AFL/AFL++ that make use of a forkserver.
|
/// This [`Executor`] can run binaries compiled for AFL/AFL++ that make use of a forkserver.
|
||||||
/// Shared memory feature is also available, but you have to set things up in your code.
|
/// Shared memory feature is also available, but you have to set things up in your code.
|
||||||
/// Please refer to AFL++'s docs. <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md>
|
/// Please refer to AFL++'s docs. <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md>
|
||||||
@ -650,9 +480,8 @@ where
|
|||||||
observers: OT,
|
observers: OT,
|
||||||
map: Option<SP::ShMem>,
|
map: Option<SP::ShMem>,
|
||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
/// Cache that indicates if we have a `ASan` observer registered.
|
|
||||||
has_asan_observer: Option<bool>,
|
|
||||||
map_size: Option<usize>,
|
map_size: Option<usize>,
|
||||||
|
timeout: TimeSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<OT, S, SP> Debug for ForkserverExecutor<OT, S, SP>
|
impl<OT, S, SP> Debug for ForkserverExecutor<OT, S, SP>
|
||||||
@ -732,6 +561,7 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
|
|||||||
map_size: Option<usize>,
|
map_size: Option<usize>,
|
||||||
real_map_size: i32,
|
real_map_size: i32,
|
||||||
kill_signal: Option<Signal>,
|
kill_signal: Option<Signal>,
|
||||||
|
timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
||||||
@ -764,6 +594,11 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let timeout: TimeSpec = match self.timeout {
|
||||||
|
Some(t) => t.into(),
|
||||||
|
None => Duration::from_millis(5000).into(),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(ForkserverExecutor {
|
Ok(ForkserverExecutor {
|
||||||
target,
|
target,
|
||||||
args: self.arguments.clone(),
|
args: self.arguments.clone(),
|
||||||
@ -773,8 +608,8 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
observers,
|
observers,
|
||||||
map,
|
map,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
has_asan_observer: None, // initialized on first use
|
|
||||||
map_size: self.map_size,
|
map_size: self.map_size,
|
||||||
|
timeout,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -815,6 +650,11 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let timeout: TimeSpec = match self.timeout {
|
||||||
|
Some(t) => t.into(),
|
||||||
|
None => Duration::from_millis(5000).into(),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(ForkserverExecutor {
|
Ok(ForkserverExecutor {
|
||||||
target,
|
target,
|
||||||
args: self.arguments.clone(),
|
args: self.arguments.clone(),
|
||||||
@ -824,8 +664,8 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
observers,
|
observers,
|
||||||
map,
|
map,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
has_asan_observer: None, // initialized on first use
|
|
||||||
map_size: self.map_size,
|
map_size: self.map_size,
|
||||||
|
timeout,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,6 +816,13 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// set the timeout for the executor
|
||||||
|
pub fn timeout(mut self, timeout: Duration) -> Self {
|
||||||
|
self.timeout = Some(timeout);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Parse afl style command line
|
/// Parse afl style command line
|
||||||
///
|
///
|
||||||
@ -1177,6 +1024,7 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
|
|||||||
real_map_size: 0,
|
real_map_size: 0,
|
||||||
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
||||||
kill_signal: None,
|
kill_signal: None,
|
||||||
|
timeout: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,6 +1049,7 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
|
|||||||
real_map_size: self.real_map_size,
|
real_map_size: self.real_map_size,
|
||||||
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
max_input_size: MAX_INPUT_SIZE_DEFAULT,
|
||||||
kill_signal: None,
|
kill_signal: None,
|
||||||
|
timeout: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1229,13 +1078,15 @@ where
|
|||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> Result<ExitKind, Error> {
|
||||||
*state.executions_mut() += 1;
|
*state.executions_mut() += 1;
|
||||||
|
|
||||||
let mut exit_kind = ExitKind::Ok;
|
let mut exit_kind = ExitKind::Ok;
|
||||||
|
|
||||||
// Write to testcase
|
let last_run_timed_out = self.forkserver.last_run_timed_out_raw();
|
||||||
|
|
||||||
if self.uses_shmem_testcase {
|
if self.uses_shmem_testcase {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.map.is_some(),
|
self.map.is_some(),
|
||||||
"The uses_shmem_testcase bool can only exist when a map is set"
|
"The uses_shmem_testcase() bool can only exist when a map is set"
|
||||||
);
|
);
|
||||||
// # Safety
|
// # Safety
|
||||||
// Struct can never be created when uses_shmem_testcase is true and map is none.
|
// Struct can never be created when uses_shmem_testcase is true and map is none.
|
||||||
@ -1257,21 +1108,19 @@ where
|
|||||||
self.input_file.write_buf(input.target_bytes().as_slice())?;
|
self.input_file.write_buf(input.target_bytes().as_slice())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't tell the forkserver to spawn a new process before clearing the cov map
|
let send_len = self.forkserver.write_ctl(last_run_timed_out)?;
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
|
self.forkserver.set_last_run_timed_out(false);
|
||||||
|
|
||||||
let send_len = self
|
|
||||||
.forkserver
|
|
||||||
.write_ctl(self.forkserver().last_run_timed_out_raw())?;
|
|
||||||
if send_len != 4 {
|
if send_len != 4 {
|
||||||
return Err(Error::illegal_state(
|
return Err(Error::unknown(
|
||||||
"Unable to request new process from fork server (OOM?)".to_string(),
|
"Unable to request new process from fork server (OOM?)".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (recv_pid_len, pid) = self.forkserver.read_st()?;
|
let (recv_pid_len, pid) = self.forkserver.read_st()?;
|
||||||
if recv_pid_len != 4 {
|
if recv_pid_len != 4 {
|
||||||
return Err(Error::illegal_state(
|
return Err(Error::unknown(
|
||||||
"Unable to request new process from fork server (OOM?)".to_string(),
|
"Unable to request new process from fork server (OOM?)".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1284,41 +1133,34 @@ where
|
|||||||
|
|
||||||
self.forkserver.set_child_pid(Pid::from_raw(pid));
|
self.forkserver.set_child_pid(Pid::from_raw(pid));
|
||||||
|
|
||||||
let (recv_status_len, status) = self.forkserver.read_st()?;
|
if let Some(status) = self.forkserver.read_st_timed(&self.timeout)? {
|
||||||
if recv_status_len != 4 {
|
|
||||||
return Err(Error::unknown(
|
|
||||||
"Unable to communicate with fork server (OOM?)".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.forkserver.set_status(status);
|
self.forkserver.set_status(status);
|
||||||
|
if libc::WIFSIGNALED(self.forkserver().status()) {
|
||||||
if libc::WIFSIGNALED(self.forkserver.status()) {
|
|
||||||
exit_kind = ExitKind::Crash;
|
exit_kind = ExitKind::Crash;
|
||||||
#[cfg(feature = "regex")]
|
#[cfg(feature = "regex")]
|
||||||
if self.has_asan_observer.is_none() {
|
if let Some(asan_observer) = self
|
||||||
self.has_asan_observer = Some(
|
.observers_mut()
|
||||||
self.observers()
|
|
||||||
.match_name::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
|
||||||
.is_some(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[cfg(feature = "regex")]
|
|
||||||
if self.has_asan_observer.unwrap() {
|
|
||||||
self.observers_mut()
|
|
||||||
.match_name_mut::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
.match_name_mut::<AsanBacktraceObserver>("AsanBacktraceObserver")
|
||||||
.unwrap()
|
{
|
||||||
.parse_asan_output_from_asan_log_file(pid)?;
|
asan_observer.parse_asan_output_from_asan_log_file(pid)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.forkserver.set_last_run_timed_out(true);
|
||||||
|
|
||||||
|
// We need to kill the child in case he has timed out, or we can't get the correct pid in the next call to self.executor.forkserver_mut().read_st()?
|
||||||
|
let _ = kill(self.forkserver().child_pid(), self.forkserver.kill_signal);
|
||||||
|
let (recv_status_len, _) = self.forkserver.read_st()?;
|
||||||
|
if recv_status_len != 4 {
|
||||||
|
return Err(Error::unknown("Could not kill timed-out child".to_string()));
|
||||||
|
}
|
||||||
|
exit_kind = ExitKind::Timeout;
|
||||||
|
}
|
||||||
|
|
||||||
if !libc::WIFSTOPPED(self.forkserver.status) {
|
if !libc::WIFSTOPPED(self.forkserver().status()) {
|
||||||
self.forkserver.reset_child_pid();
|
self.forkserver.reset_child_pid();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the observer map after the execution is finished
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
|
|
||||||
Ok(exit_kind)
|
Ok(exit_kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1357,80 +1199,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<OT, S, SP> HasForkserver for ForkserverExecutor<OT, S, SP>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<S>,
|
|
||||||
S: UsesInput,
|
|
||||||
S::Input: Input + HasTargetBytes,
|
|
||||||
SP: ShMemProvider,
|
|
||||||
{
|
|
||||||
type SP = SP;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn forkserver(&self) -> &Forkserver {
|
|
||||||
&self.forkserver
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn forkserver_mut(&mut self) -> &mut Forkserver {
|
|
||||||
&mut self.forkserver
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn input_file(&self) -> &InputFile {
|
|
||||||
&self.input_file
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn input_file_mut(&mut self) -> &mut InputFile {
|
|
||||||
&mut self.input_file
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn shmem(&self) -> &Option<SP::ShMem> {
|
|
||||||
&self.map
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn shmem_mut(&mut self) -> &mut Option<SP::ShMem> {
|
|
||||||
&mut self.map
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn uses_shmem_testcase(&self) -> bool {
|
|
||||||
self.uses_shmem_testcase
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> UsesState for TimeoutForkserverExecutor<E>
|
|
||||||
where
|
|
||||||
E: UsesState,
|
|
||||||
{
|
|
||||||
type State = E::State;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> UsesObservers for TimeoutForkserverExecutor<E>
|
|
||||||
where
|
|
||||||
E: UsesObservers,
|
|
||||||
{
|
|
||||||
type Observers = E::Observers;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> HasObservers for TimeoutForkserverExecutor<E>
|
|
||||||
where
|
|
||||||
E: HasObservers,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn observers(&self) -> &Self::Observers {
|
|
||||||
self.executor.observers()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn observers_mut(&mut self) -> &mut Self::Observers {
|
|
||||||
self.executor.observers_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
@ -9,7 +9,7 @@ pub use combined::CombinedExecutor;
|
|||||||
pub use command::CommandExecutor;
|
pub use command::CommandExecutor;
|
||||||
pub use differential::DiffExecutor;
|
pub use differential::DiffExecutor;
|
||||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||||
pub use forkserver::{Forkserver, ForkserverExecutor, TimeoutForkserverExecutor};
|
pub use forkserver::{Forkserver, ForkserverExecutor};
|
||||||
pub use inprocess::InProcessExecutor;
|
pub use inprocess::InProcessExecutor;
|
||||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||||
pub use inprocess_fork::InProcessForkExecutor;
|
pub use inprocess_fork::InProcessForkExecutor;
|
||||||
|
@ -5,7 +5,7 @@ use std::{fs, net::SocketAddr, path::PathBuf, time::Duration};
|
|||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus},
|
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus},
|
||||||
events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager},
|
events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager},
|
||||||
executors::{forkserver::ForkserverExecutorBuilder, TimeoutForkserverExecutor},
|
executors::forkserver::ForkserverExecutorBuilder,
|
||||||
feedback_or, feedback_or_fast,
|
feedback_or, feedback_or_fast,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
@ -179,6 +179,7 @@ impl<'a> ForkserverBytesCoverageSugar<'a> {
|
|||||||
.is_persistent(true)
|
.is_persistent(true)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.coverage_map_size(MAP_SIZE)
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.timeout(timeout)
|
||||||
.debug_child(self.debug_output)
|
.debug_child(self.debug_output)
|
||||||
.shmem_provider(&mut shmem_provider_client)
|
.shmem_provider(&mut shmem_provider_client)
|
||||||
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
||||||
@ -189,10 +190,12 @@ impl<'a> ForkserverBytesCoverageSugar<'a> {
|
|||||||
.is_persistent(true)
|
.is_persistent(true)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.coverage_map_size(MAP_SIZE)
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.timeout(timeout)
|
||||||
.debug_child(self.debug_output)
|
.debug_child(self.debug_output)
|
||||||
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut executor = forkserver.unwrap();
|
||||||
if let Some(tokens_file) = &self.tokens_file {
|
if let Some(tokens_file) = &self.tokens_file {
|
||||||
// if a token file is provided, load it into our set of tokens
|
// if a token file is provided, load it into our set of tokens
|
||||||
tokens.add_from_file(tokens_file)?;
|
tokens.add_from_file(tokens_file)?;
|
||||||
@ -203,13 +206,6 @@ impl<'a> ForkserverBytesCoverageSugar<'a> {
|
|||||||
state.add_metadata(tokens);
|
state.add_metadata(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
|
||||||
let mut executor = TimeoutForkserverExecutor::new(
|
|
||||||
forkserver.expect("Failed to create the executor."),
|
|
||||||
timeout,
|
|
||||||
)
|
|
||||||
.expect("Failed to create the executor.");
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.must_load_initial_inputs() {
|
if state.must_load_initial_inputs() {
|
||||||
if self.input_dirs.is_empty() {
|
if self.input_dirs.is_empty() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user