generic executor

This commit is contained in:
Andrea Fioraldi 2020-11-03 14:58:23 +01:00
parent 0f4d4d9795
commit fdf566bda4
5 changed files with 112 additions and 100 deletions

View File

@ -24,6 +24,7 @@ pub trait Corpus<I> : HasEntriesVec<I> + HasRand where I: Input {
} }
/// Add an entry to the corpus /// Add an entry to the corpus
#[allow(unused_mut)]
fn add(&mut self, mut entry: Box<Testcase<I>>) { fn add(&mut self, mut entry: Box<Testcase<I>>) {
self.entries_mut().push(entry); self.entries_mut().push(entry);
} }

View File

@ -8,14 +8,14 @@ use crate::monitors::Monitor;
use crate::stages::Stage; use crate::stages::Stage;
use crate::utils::Rand; use crate::utils::Rand;
pub struct AflEngine<'a, InputT: Input> { pub struct AflEngine<'a, I: Input> {
pub rand: &'a mut dyn Rand, pub rand: &'a mut dyn Rand,
pub feedbacks: Vec<Box<dyn Feedback<InputT>>>, pub feedbacks: Vec<Box<dyn Feedback<I>>>,
pub stages: Vec<Box<dyn Stage<InputT>>>, pub stages: Vec<Box<dyn Stage<I>>>,
pub current_stage: &'a Box<dyn Stage<InputT>>, pub current_stage: &'a Box<dyn Stage<I>>,
pub executor: Box<dyn Executor>, pub executor: Box<dyn Executor<dyn Input>>,
pub executions: u64, pub executions: u64,
@ -26,4 +26,4 @@ pub struct AflEngine<'a, InputT: Input> {
pub monitors: Vec<Box<dyn Monitor>>, pub monitors: Vec<Box<dyn Monitor>>,
} }
impl<InputT: Input> Engine<'_> for AflEngine<'_, InputT> {} impl<I: Input> Engine<'_> for AflEngine<'_, I> {}

View File

@ -2,20 +2,89 @@ use crate::inputs::Input;
use crate::observers::Observer; use crate::observers::Observer;
use crate::AflError; use crate::AflError;
use crate::executors::{Executor, ExitKind, ExecutorBase}; use crate::executors::{Executor, ExitKind};
use std::ptr; use std::ptr;
use std::os::raw::c_void;
type HarnessFunction = fn(&dyn Executor, &[u8]) -> ExitKind; type HarnessFunction<I> = fn(&dyn Executor<I>, &[u8]) -> ExitKind;
pub struct InMemoryExecutor { pub struct InMemoryExecutor<I> where I: Input {
base: ExecutorBase, cur_input: Option<Box<I>>,
harness: HarnessFunction, observers: Vec<Box<dyn Observer>>,
harness: HarnessFunction<I>,
} }
static mut CURRENT_INMEMORY_EXECUTOR_PTR: *const InMemoryExecutor = ptr::null(); static mut CURRENT_INMEMORY_EXECUTOR_PTR: *const c_void = ptr::null();
impl<I> Executor<I> for InMemoryExecutor<I> where I: Input {
fn run_target(&mut self) -> Result<ExitKind, AflError> {
let bytes = match self.cur_input.as_ref() {
Some(i) => i.serialize(),
None => return Err(AflError::Empty("cur_input".to_string())),
};
unsafe {
CURRENT_INMEMORY_EXECUTOR_PTR = self as *const InMemoryExecutor<I> as *const c_void;
}
let ret = match bytes {
Ok(b) => Ok((self.harness)(self, b)),
Err(e) => Err(e),
};
unsafe {
CURRENT_INMEMORY_EXECUTOR_PTR = ptr::null();
}
ret
}
fn place_input(&mut self, input: Box<I>) -> Result<(), AflError> {
self.cur_input = Some(input);
Ok(())
}
fn cur_input(&self) -> &Option<Box<I>> {
&self.cur_input
}
fn cur_input_mut(&mut self) -> &mut Option<Box<I>> {
&mut self.cur_input
}
fn reset_observers(&mut self) -> Result<(), AflError> {
for observer in &mut self.observers {
observer.reset()?;
}
Ok(())
}
fn post_exec_observers(&mut self) -> Result<(), AflError> {
self.observers
.iter_mut()
.map(|x| x.post_exec())
.fold(Ok(()), |acc, x| if x.is_err() { x } else { acc })
}
fn add_observer(&mut self, observer: Box<dyn Observer>) {
self.observers.push(observer);
}
fn observers(&self) -> &Vec<Box<dyn Observer>> {
&self.observers
}
}
impl<I> InMemoryExecutor<I> where I: Input {
pub fn new(harness_fn: HarnessFunction<I>) -> Self {
unsafe {
os_signals::setup_crash_handlers::<I, Self>();
}
InMemoryExecutor {
cur_input: None,
observers: vec![],
harness: harness_fn,
}
}
}
/// TODO move in platform/
#[cfg(unix)] #[cfg(unix)]
pub mod unix_signals { pub mod unix_signals {
@ -29,12 +98,14 @@ pub mod unix_signals {
use std::{mem, process, ptr}; use std::{mem, process, ptr};
use crate::executors::inmemory::CURRENT_INMEMORY_EXECUTOR_PTR; use crate::executors::inmemory::CURRENT_INMEMORY_EXECUTOR_PTR;
use crate::inputs::Input;
use crate::executors::Executor;
pub extern "C" fn libaflrs_executor_inmem_handle_crash( pub extern "C" fn libaflrs_executor_inmem_handle_crash<I, E>(
_sig: c_int, _sig: c_int,
info: siginfo_t, info: siginfo_t,
_void: c_void, _void: c_void,
) { ) where I: Input, E: Executor<I> {
unsafe { unsafe {
if CURRENT_INMEMORY_EXECUTOR_PTR == ptr::null() { if CURRENT_INMEMORY_EXECUTOR_PTR == ptr::null() {
println!( println!(
@ -48,11 +119,11 @@ pub mod unix_signals {
let _ = stdout().flush(); let _ = stdout().flush();
} }
pub extern "C" fn libaflrs_executor_inmem_handle_timeout( pub extern "C" fn libaflrs_executor_inmem_handle_timeout<I, E>(
_sig: c_int, _sig: c_int,
_info: siginfo_t, _info: siginfo_t,
_void: c_void, _void: c_void,
) { ) where I: Input, E: Executor<I> {
dbg!("TIMEOUT/SIGUSR2 received"); dbg!("TIMEOUT/SIGUSR2 received");
unsafe { unsafe {
if CURRENT_INMEMORY_EXECUTOR_PTR == ptr::null() { if CURRENT_INMEMORY_EXECUTOR_PTR == ptr::null() {
@ -66,11 +137,11 @@ pub mod unix_signals {
process::abort(); process::abort();
} }
pub unsafe fn setup_crash_handlers() { pub unsafe fn setup_crash_handlers<I, E>() where I: Input, E: Executor<I> {
let mut sa: sigaction = mem::zeroed(); let mut sa: sigaction = mem::zeroed();
libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t); libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t);
sa.sa_flags = SA_NODEFER | SA_SIGINFO; sa.sa_flags = SA_NODEFER | SA_SIGINFO;
sa.sa_sigaction = libaflrs_executor_inmem_handle_crash as usize; sa.sa_sigaction = libaflrs_executor_inmem_handle_crash::<I, E> as usize;
for (sig, msg) in &[ for (sig, msg) in &[
(SIGSEGV, "segfault"), (SIGSEGV, "segfault"),
(SIGBUS, "sigbus"), (SIGBUS, "sigbus"),
@ -84,7 +155,7 @@ pub mod unix_signals {
} }
} }
sa.sa_sigaction = libaflrs_executor_inmem_handle_timeout as usize; sa.sa_sigaction = libaflrs_executor_inmem_handle_timeout::<I, E> as usize;
if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 { if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 {
panic!("Could not set up sigusr2 handler for timeouts"); panic!("Could not set up sigusr2 handler for timeouts");
} }
@ -96,72 +167,6 @@ use unix_signals as os_signals;
#[cfg(not(unix))] #[cfg(not(unix))]
compile_error!("InMemoryExecutor not yet supported on this OS"); compile_error!("InMemoryExecutor not yet supported on this OS");
impl Executor for InMemoryExecutor {
fn run_target(&mut self) -> Result<ExitKind, AflError> {
let bytes = match self.base.cur_input.as_ref() {
Some(i) => i.serialize(),
None => return Err(AflError::Empty("cur_input".to_string())),
};
unsafe {
CURRENT_INMEMORY_EXECUTOR_PTR = self as *const InMemoryExecutor;
}
let ret = match bytes {
Ok(b) => Ok((self.harness)(self, b)),
Err(e) => Err(e),
};
unsafe {
CURRENT_INMEMORY_EXECUTOR_PTR = ptr::null();
}
ret
}
fn place_input(&mut self, input: Box<dyn Input>) -> Result<(), AflError> {
self.base.cur_input = Some(input);
Ok(())
}
fn get_input(&self) -> Option<& Box<dyn Input>> {
self.base.cur_input.as_ref()
}
fn reset_observers(&mut self) -> Result<(), AflError> {
for observer in &mut self.base.observers {
observer.reset()?;
}
Ok(())
}
fn post_exec_observers(&mut self) -> Result<(), AflError> {
self.base
.observers
.iter_mut()
.map(|x| x.post_exec())
.fold(Ok(()), |acc, x| if x.is_err() { x } else { acc })
}
fn add_observer(&mut self, observer: Box<dyn Observer>) {
self.base.observers.push(observer);
}
fn get_observers(&self) -> &Vec<Box<dyn Observer>> {
&self.base.observers
}
}
impl InMemoryExecutor {
pub fn new(harness_fn: HarnessFunction) -> InMemoryExecutor {
unsafe {
os_signals::setup_crash_handlers();
}
InMemoryExecutor {
base: ExecutorBase {
observers: vec![],
cur_input: Option::None,
},
harness: harness_fn,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -197,7 +202,7 @@ mod tests {
} }
} }
fn test_harness_fn_nop(_executor: &dyn Executor, buf: &[u8]) -> ExitKind { fn test_harness_fn_nop(_executor: &dyn Executor<NopInput>, buf: &[u8]) -> ExitKind {
println! {"Fake exec with buf of len {}", buf.len()}; println! {"Fake exec with buf of len {}", buf.len()};
ExitKind::Ok ExitKind::Ok
} }

View File

@ -12,24 +12,30 @@ pub enum ExitKind {
Timeout, Timeout,
} }
pub trait Executor { // TODO unbox input
pub trait Executor<I> where I: Input {
/// Run the target
fn run_target(&mut self) -> Result<ExitKind, AflError>; fn run_target(&mut self) -> Result<ExitKind, AflError>;
fn place_input(&mut self, input: Box<dyn Input>) -> Result<(), AflError>; /// Instruct the target about the input before the run
fn place_input(&mut self, input: Box<I>) -> Result<(), AflError>;
fn get_input(&self) -> Option<& Box<dyn Input>>; /// Get the current input, if any
fn cur_input(&self) -> &Option<Box<I>>;
/// Get the current input, if any (mutable)
fn cur_input_mut(&mut self) -> &mut Option<Box<I>>;
/// Reset the state of all the observes linked to this executor
fn reset_observers(&mut self) -> Result<(), AflError>; fn reset_observers(&mut self) -> Result<(), AflError>;
/// Run the post exec hook for all the observes linked to this executor
fn post_exec_observers(&mut self) -> Result<(), AflError>; fn post_exec_observers(&mut self) -> Result<(), AflError>;
/// Add a linked observer
fn add_observer(&mut self, observer: Box<dyn Observer>); fn add_observer(&mut self, observer: Box<dyn Observer>);
fn get_observers(&self) -> &Vec<Box<dyn Observer>>; /// Get the linked observers
} fn observers(&self) -> &Vec<Box<dyn Observer>>;
}
// TODO abstract classes? how?
pub struct ExecutorBase {
observers: Vec<Box<dyn Observer>>,
cur_input: Option<Box<dyn Input>>,
}

View File

@ -8,9 +8,9 @@ use crate::observers::MapObserver;
use num::Integer; use num::Integer;
use std::marker::PhantomData; use std::marker::PhantomData;
pub trait Feedback<InputT: Input> { pub trait Feedback<I> where I: Input {
/// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55) /// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55)
fn is_interesting(&mut self, executor: &dyn Executor, entry: &Testcase<InputT>) -> u8; fn is_interesting(&mut self, executor: &dyn Executor<I>, entry: &Testcase<I>) -> u8;
} }
/* /*