diff --git a/afl/Cargo.toml b/afl/Cargo.toml index 99c42680f7..d0b023a8e8 100644 --- a/afl/Cargo.toml +++ b/afl/Cargo.toml @@ -31,6 +31,7 @@ default = ["std"] std = [] [dependencies] +tuple_list = "0.1.2" hashbrown = { version = "0.9", features = ["serde"] } # A faster hashmap, nostd compatible libc = "0.2" # For (*nix) libc num = "*" diff --git a/afl/src/engines/mod.rs b/afl/src/engines/mod.rs index f5a31360c3..d74811fe97 100644 --- a/afl/src/engines/mod.rs +++ b/afl/src/engines/mod.rs @@ -1,18 +1,18 @@ //! The engine is the core piece of every good fuzzer use alloc::boxed::Box; -use alloc::vec::Vec; use core::fmt::Debug; use core::marker::PhantomData; use hashbrown::HashMap; use crate::corpus::{Corpus, Testcase}; use crate::events::{Event, EventManager}; -use crate::executors::Executor; -use crate::feedbacks::Feedback; +use crate::executors::{Executor, HasObservers}; +use crate::feedbacks::FeedbacksTuple; use crate::generators::Generator; use crate::inputs::Input; -use crate::stages::Stage; +use crate::observers::ObserversTuple; +use crate::stages::StagesTuple; use crate::utils::{current_milliseconds, Rand}; use crate::AflError; @@ -22,10 +22,11 @@ pub trait StateMetadata: Debug { } /// The state a fuzz run. -pub struct State +pub struct State where I: Input, R: Rand, + FT: FeedbacksTuple, { /// How many times the executor ran the harness/target executions: usize, @@ -34,14 +35,15 @@ where /// Metadata stored for this state by one of the components metadatas: HashMap<&'static str, Box>, // additional_corpuses: HashMap<&'static str, Box>, - feedbacks: Vec>>, - phantom: PhantomData, + feedbacks: FT, + phantom: PhantomData<(I, R)>, } -impl State +impl State where I: Input, R: Rand, + FT: FeedbacksTuple, { /// Get executions #[inline] @@ -98,39 +100,31 @@ where /// Returns vector of feebacks #[inline] - pub fn feedbacks(&self) -> &[Box>] { + pub fn feedbacks(&self) -> &FT { &self.feedbacks } /// Returns vector of feebacks (mutable) #[inline] - pub fn feedbacks_mut(&mut self) -> &mut Vec>> { + pub fn feedbacks_mut(&mut self) -> &mut FT { &mut self.feedbacks } - /// Adds a feedback - #[inline] - pub fn add_feedback(&mut self, feedback: Box>) { - self.feedbacks_mut().push(feedback); - } - - // TODO move some of these, like evaluate_input, to FuzzingEngine + // TODO move some of these, like evaluate_input, to Engine /// Runs the input and triggers observers and feedback - pub fn evaluate_input(&mut self, input: &I, executor: &mut E) -> Result + pub fn evaluate_input(&mut self, input: &I, executor: &mut E) -> Result where - E: Executor, + E: Executor + HasObservers, + OT: ObserversTuple, { executor.reset_observers()?; executor.run_target(&input)?; self.set_executions(self.executions() + 1); executor.post_exec_observers()?; - let mut fitness = 0; let observers = executor.observers(); - for feedback in self.feedbacks_mut() { - fitness += feedback.is_interesting(&input, observers)?; - } + let fitness = self.feedbacks_mut().is_interesting_all(&input, observers)?; Ok(fitness) } @@ -138,10 +132,7 @@ where #[inline] pub fn discard_input(&mut self, input: &I) -> Result<(), AflError> { // TODO: This could probably be automatic in the feedback somehow? - for feedback in self.feedbacks_mut() { - feedback.discard_metadata(input)?; - } - Ok(()) + self.feedbacks_mut().discard_metadata_all(&input) } /// Creates a new testcase, appending the metadata from each feedback @@ -149,10 +140,7 @@ where pub fn input_to_testcase(&mut self, input: I, fitness: u32) -> Result, AflError> { let mut testcase = Testcase::new(input); testcase.set_fitness(fitness); - for feedback in self.feedbacks_mut() { - feedback.append_metadata(&mut testcase)?; - } - + self.feedbacks_mut().append_metadata_all(&mut testcase)?; Ok(testcase) } @@ -191,20 +179,21 @@ where } } - pub fn generate_initial_inputs( + pub fn generate_initial_inputs( &mut self, rand: &mut R, corpus: &mut C, generator: &mut G, - engine: &mut Engine, + engine: &mut Engine, manager: &mut EM, num: usize, ) -> Result<(), AflError> where G: Generator, C: Corpus, - E: Executor, - EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + EM: EventManager, { let mut added = 0; for _ in 0..num { @@ -226,29 +215,31 @@ where Ok(()) } - pub fn new() -> Self { + pub fn new(feedbacks: FT) -> Self { Self { executions: 0, start_time: current_milliseconds(), metadatas: HashMap::default(), - feedbacks: vec![], + feedbacks: feedbacks, phantom: PhantomData, } } } -pub struct Engine +pub struct Engine where - E: Executor, + E: Executor + HasObservers, + OT: ObserversTuple, I: Input, { executor: E, - phantom: PhantomData, + phantom: PhantomData<(OT, I)>, } -impl Engine +impl Engine where - E: Executor, + E: Executor + HasObservers, + OT: ObserversTuple, I: Input, { /// Return the executor @@ -271,35 +262,33 @@ where } } -pub trait Fuzzer +pub trait Fuzzer where - EM: EventManager, - E: Executor, + ST: StagesTuple, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, { - fn stages(&self) -> &[Box>]; + fn stages(&self) -> &ST; - fn stages_mut(&mut self) -> &mut Vec>>; - - fn add_stage(&mut self, stage: Box>) { - self.stages_mut().push(stage); - } + fn stages_mut(&mut self) -> &mut ST; fn fuzz_one( &mut self, rand: &mut R, - state: &mut State, + state: &mut State, corpus: &mut C, - engine: &mut Engine, + engine: &mut Engine, manager: &mut EM, ) -> Result { let (_, idx) = corpus.next(rand)?; - for stage in self.stages_mut() { - stage.perform(rand, state, corpus, engine, manager, idx)?; - } + self.stages_mut() + .perform_all(rand, state, corpus, engine, manager, idx)?; manager.process(state, corpus)?; Ok(idx) @@ -308,9 +297,9 @@ where fn fuzz_loop( &mut self, rand: &mut R, - state: &mut State, + state: &mut State, corpus: &mut C, - engine: &mut Engine, + engine: &mut Engine, manager: &mut EM, ) -> Result<(), AflError> { let mut last = current_milliseconds(); @@ -328,44 +317,58 @@ where } } -pub struct StdFuzzer +pub struct StdFuzzer where - EM: EventManager, - E: Executor, + ST: StagesTuple, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, { - stages: Vec>>, + stages: ST, + phantom: PhantomData<(EM, E, OT, FT, C, I, R)>, } -impl Fuzzer for StdFuzzer +impl Fuzzer + for StdFuzzer where - EM: EventManager, - E: Executor, + ST: StagesTuple, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, { - fn stages(&self) -> &[Box>] { + fn stages(&self) -> &ST { &self.stages } - fn stages_mut(&mut self) -> &mut Vec>> { + fn stages_mut(&mut self) -> &mut ST { &mut self.stages } } -impl StdFuzzer +impl StdFuzzer where - EM: EventManager, - E: Executor, + ST: StagesTuple, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, { - pub fn new() -> Self { - Self { stages: vec![] } + pub fn new(stages: ST) -> Self { + Self { + stages: stages, + phantom: PhantomData, + } } } @@ -374,8 +377,6 @@ where #[cfg(test)] mod tests { - use alloc::boxed::Box; - #[cfg(feature = "std")] use std::io::stderr; @@ -388,6 +389,7 @@ mod tests { use crate::inputs::bytes::BytesInput; use crate::mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator}; use crate::stages::mutational::StdMutationalStage; + use crate::tuples::tuple_list; use crate::utils::StdRand; fn harness(_executor: &dyn Executor, _buf: &[u8]) -> ExitKind { @@ -402,18 +404,15 @@ mod tests { let testcase = Testcase::new(vec![0; 4]).into(); corpus.add(testcase); - let executor = InMemoryExecutor::::new(harness); - let mut state = State::new(); + let executor = InMemoryExecutor::::new(harness, tuple_list!()); + let mut state = State::new(tuple_list!()); let mut events_manager = LoggerEventManager::new(stderr()); let mut engine = Engine::new(executor); let mut mutator = StdScheduledMutator::new(); mutator.add_mutation(mutation_bitflip); let stage = StdMutationalStage::new(mutator); - let mut fuzzer = StdFuzzer::new(); - fuzzer.add_stage(Box::new(stage)); - - // + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); for i in 0..1000 { fuzzer diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 3c1752023a..a65b6a5347 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -14,13 +14,15 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::io::Write; +use crate::corpus::Corpus; use crate::engines::State; use crate::executors::Executor; +use crate::feedbacks::FeedbacksTuple; use crate::inputs::Input; -use crate::serde_anymap::{Ptr, PtrMut}; +use crate::observers::ObserversTuple; +use crate::serde_anymap::{Ptr, PtrMut, SerdeAny}; use crate::utils::Rand; use crate::AflError; -use crate::{corpus::Corpus, serde_anymap::SerdeAny}; /// Indicate if an event worked or not pub enum BrokerEventResult { @@ -33,7 +35,7 @@ pub enum BrokerEventResult { pub trait ShowStats {} /// A custom event, for own messages, with own handler. -pub trait CustomEvent: SerdeAny + Serialize +pub trait CustomEvent: SerdeAny + Serialize where I: Input, { @@ -48,9 +50,10 @@ where /// Events sent around in the library #[derive(Serialize, Deserialize)] #[serde(bound = "I: serde::de::DeserializeOwned")] -pub enum Event<'a, I> +pub enum Event<'a, I, OT> where I: Input, + OT: ObserversTuple, { LoadInitial { sender_id: u64, @@ -59,7 +62,7 @@ where NewTestcase { sender_id: u64, input: Ptr<'a, I>, - observers: PtrMut<'a, crate::observers::observer_serde::NamedSerdeAnyMap>, + observers: PtrMut<'a, OT>, corpus_count: usize, }, UpdateStats { @@ -90,14 +93,14 @@ where Custom { sender_id: u64, // TODO: Allow custom events - // custom_event: Box>, + // custom_event: Box>, }, } -impl<'a, I> Event<'a, I> +impl<'a, I, OT> Event<'a, I, OT> where I: Input, - //CE: CustomEvent, + OT: ObserversTuple, //CE: CustomEvent, { pub fn name(&self) -> &str { match self { @@ -159,29 +162,31 @@ where } } -pub trait EventManager +pub trait EventManager where C: Corpus, E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, I: Input, R: Rand, { /// Fire an Event - fn fire<'a>(&mut self, event: Event<'a, I>) -> Result<(), AflError>; + fn fire<'a>(&mut self, event: Event<'a, I, OT>) -> Result<(), AflError>; /// Lookup for incoming events and process them. /// Return the number of processes events or an error - fn process(&mut self, state: &mut State, corpus: &mut C) -> Result; + fn process(&mut self, state: &mut State, corpus: &mut C) -> Result; #[inline] - fn on_recv(&self, _state: &mut State, _corpus: &mut C) -> Result<(), AflError> { + fn on_recv(&self, _state: &mut State, _corpus: &mut C) -> Result<(), AflError> { // TODO: Better way to move out of testcase, or get ref //Ok(corpus.add(self.testcase.take().unwrap())) Ok(()) } // TODO the broker has a state? do we need to pass state and corpus? - fn handle_in_broker(&mut self, event: &Event) -> Result { + fn handle_in_broker(&mut self, event: &Event) -> Result { match event { Event::LoadInitial { sender_id: _, @@ -238,8 +243,8 @@ where fn handle_in_client( &mut self, - event: Event, - _state: &mut State, + event: Event, + _state: &mut State, _corpus: &mut C, ) -> Result<(), AflError> { match event { @@ -263,7 +268,7 @@ where } /*TODO - fn on_recv(&self, state: &mut State, _corpus: &mut C) -> Result<(), AflError> { + fn on_recv(&self, state: &mut State, _corpus: &mut C) -> Result<(), AflError> { println!( "#{}\t exec/s: {}", state.executions(), @@ -275,10 +280,16 @@ where */ #[cfg(feature = "std")] -pub struct LoggerEventManager +pub struct LoggerEventManager where + C: Corpus, + E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, + I: Input, + R: Rand, W: Write, - //CE: CustomEvent, + //CE: CustomEvent, { writer: W, count: usize, @@ -288,21 +299,24 @@ where execs_over_sec: u64, corpus_count: usize, - phantom: PhantomData<(C, E, I, R)>, + phantom: PhantomData<(C, E, OT, FT, I, R)>, } #[cfg(feature = "std")] -impl EventManager for LoggerEventManager +impl EventManager + for LoggerEventManager where C: Corpus, E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, I: Input, R: Rand, W: Write, - //CE: CustomEvent, + //CE: CustomEvent, { #[inline] - fn fire<'a>(&mut self, event: Event<'a, I>) -> Result<(), AflError> { + fn fire<'a>(&mut self, event: Event<'a, I, OT>) -> Result<(), AflError> { match self.handle_in_broker(&event)? { BrokerEventResult::Forward => (), //self.handle_in_client(event, state, corpus)?, // Ignore broker-only events @@ -311,13 +325,17 @@ where Ok(()) } - fn process(&mut self, _state: &mut State, _corpus: &mut C) -> Result { + fn process( + &mut self, + _state: &mut State, + _corpus: &mut C, + ) -> Result { let c = self.count; self.count = 0; Ok(c) } - fn handle_in_broker(&mut self, event: &Event) -> Result { + fn handle_in_broker(&mut self, event: &Event) -> Result { match event { Event::NewTestcase { sender_id: _, @@ -377,11 +395,13 @@ where } #[cfg(feature = "std")] -impl LoggerEventManager +impl LoggerEventManager where C: Corpus, I: Input, E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, R: Rand, W: Write, //TODO CE: CustomEvent, @@ -400,16 +420,18 @@ where /// Eventmanager for multi-processed application #[cfg(feature = "std")] -pub struct LlmpBrokerEventManager +pub struct LlmpBrokerEventManager where C: Corpus, I: Input, E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, R: Rand, - //CE: CustomEvent, + //CE: CustomEvent, { llmp_broker: llmp::LlmpBroker, - phantom: PhantomData<(C, E, I, R)>, + phantom: PhantomData<(C, E, OT, FT, I, R)>, } #[cfg(feature = "std")] @@ -424,35 +446,44 @@ const _LLMP_TAG_EVENT_TO_BOTH: llmp::Tag = 0x2B0741; /// Eventmanager for multi-processed application #[cfg(feature = "std")] -pub struct LlmpClientEventManager +pub struct LlmpClientEventManager where C: Corpus, I: Input, E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, R: Rand, - //CE: CustomEvent, + //CE: CustomEvent, { _llmp_client: llmp::LlmpClient, - phantom: PhantomData<(C, E, I, R)>, + phantom: PhantomData<(C, E, OT, FT, I, R)>, } #[cfg(feature = "std")] -impl EventManager for LlmpBrokerEventManager +impl EventManager + for LlmpBrokerEventManager where C: Corpus, E: Executor, + OT: ObserversTuple, + FT: FeedbacksTuple, I: Input, R: Rand, { /// Fire an Event - fn fire<'a>(&mut self, event: Event<'a, I>) -> Result<(), AflError> { + fn fire<'a>(&mut self, event: Event<'a, I, OT>) -> Result<(), AflError> { let serialized = postcard::to_allocvec(&event)?; self.llmp_broker .send_buf(LLMP_TAG_EVENT_TO_CLIENT, &serialized)?; Ok(()) } - fn process(&mut self, _state: &mut State, _corpus: &mut C) -> Result { + fn process( + &mut self, + _state: &mut State, + _corpus: &mut C, + ) -> Result { // TODO: iterators /* let mut handled = vec![]; @@ -481,13 +512,13 @@ where Ok(0) } - fn on_recv(&self, _state: &mut State, _corpus: &mut C) -> Result<(), AflError> { + fn on_recv(&self, _state: &mut State, _corpus: &mut C) -> Result<(), AflError> { // TODO: Better way to move out of testcase, or get ref //Ok(corpus.add(self.testcase.take().unwrap())) Ok(()) } - fn handle_in_broker(&mut self, event: &Event) -> Result { + fn handle_in_broker(&mut self, event: &Event) -> Result { match event { Event::LoadInitial { sender_id: _, @@ -542,8 +573,8 @@ where fn handle_in_client( &mut self, - event: Event, - /*client: &dyn EventManager,*/ _state: &mut State, + event: Event, + _state: &mut State, _corpus: &mut C, ) -> Result<(), AflError> { match event { @@ -572,17 +603,16 @@ mod tests { use crate::events::Event; use crate::inputs::bytes::BytesInput; - use crate::observers::observer_serde::NamedSerdeAnyMap; - use crate::observers::{Observer, StdMapObserver}; + use crate::observers::{Observer, ObserversTuple, StdMapObserver}; use crate::serde_anymap::{Ptr, PtrMut}; + use crate::tuples::{tuple_list, tuple_list_type, MatchNameAndType, Named}; static mut MAP: [u32; 4] = [0; 4]; #[test] fn test_event_serde() { - let mut map = NamedSerdeAnyMap::new(); let obv = StdMapObserver::new("test", unsafe { &mut MAP }); - map.insert(Box::new(obv), &"key".to_string()); + let mut map = tuple_list!(obv); let i = BytesInput::new(vec![0]); let e = Event::NewTestcase { @@ -594,19 +624,20 @@ mod tests { let j = serde_json::to_string(&e).unwrap(); - let d: Event = serde_json::from_str(&j).unwrap(); + let d: Event)> = + serde_json::from_str(&j).unwrap(); match d { Event::NewTestcase { sender_id: _, input: _, - observers: obs, + observers, corpus_count: _, } => { - let o = obs + let o = observers .as_ref() - .get::>(&"key".to_string()) + .match_name_type::>("test") .unwrap(); - assert_eq!("test".to_string(), *o.name()); + assert_eq!("test", o.name()); } _ => panic!("mistmatch".to_string()), }; diff --git a/afl/src/executors/inmemory.rs b/afl/src/executors/inmemory.rs index 8ce1e4b0c6..eb9989b5dd 100644 --- a/afl/src/executors/inmemory.rs +++ b/afl/src/executors/inmemory.rs @@ -1,9 +1,9 @@ use core::ffi::c_void; use core::ptr; -use crate::executors::{Executor, ExitKind}; +use crate::executors::{Executor, ExitKind, HasObservers}; use crate::inputs::{HasTargetBytes, Input}; -use crate::observers::observer_serde::NamedSerdeAnyMap; +use crate::observers::ObserversTuple; use crate::AflError; /// The (unsafe) pointer to the current inmem executor, for the current run. @@ -14,23 +14,25 @@ static mut CURRENT_INMEMORY_EXECUTOR_PTR: *const c_void = ptr::null(); type HarnessFunction = fn(&dyn Executor, &[u8]) -> ExitKind; /// The inmem executor simply calls a target function, then returns afterwards. -pub struct InMemoryExecutor +pub struct InMemoryExecutor where I: Input + HasTargetBytes, + OT: ObserversTuple, { harness: HarnessFunction, - observers: NamedSerdeAnyMap, + observers: OT, } -impl Executor for InMemoryExecutor +impl Executor for InMemoryExecutor where I: Input + HasTargetBytes, + OT: ObserversTuple, { #[inline] fn run_target(&mut self, input: &I) -> Result { let bytes = input.target_bytes(); unsafe { - CURRENT_INMEMORY_EXECUTOR_PTR = self as *const InMemoryExecutor as *const c_void; + CURRENT_INMEMORY_EXECUTOR_PTR = self as *const InMemoryExecutor as *const c_void; } let ret = (self.harness)(self, bytes.as_slice()); unsafe { @@ -38,30 +40,37 @@ where } Ok(ret) } +} +impl HasObservers for InMemoryExecutor +where + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ #[inline] - fn observers(&self) -> &NamedSerdeAnyMap { + fn observers(&self) -> &OT { &self.observers } #[inline] - fn observers_mut(&mut self) -> &mut NamedSerdeAnyMap { + fn observers_mut(&mut self) -> &mut OT { &mut self.observers } } -impl InMemoryExecutor +impl InMemoryExecutor where I: Input + HasTargetBytes, + OT: ObserversTuple, { - pub fn new(harness_fn: HarnessFunction) -> Self { + pub fn new(harness_fn: HarnessFunction, observers: OT) -> Self { #[cfg(feature = "std")] unsafe { os_signals::setup_crash_handlers::(); } Self { harness: harness_fn, - observers: NamedSerdeAnyMap::new(), + observers: observers, } } } @@ -169,6 +178,7 @@ mod tests { use crate::executors::inmemory::InMemoryExecutor; use crate::executors::{Executor, ExitKind}; use crate::inputs::{HasTargetBytes, Input, TargetBytes}; + use crate::tuples::{tuple_list, tuple_list_type}; use serde::{Deserialize, Serialize}; @@ -194,7 +204,7 @@ mod tests { #[test] fn test_inmem_exec() { - let mut in_mem_executor = InMemoryExecutor::new(test_harness_fn_nop); + let mut in_mem_executor = InMemoryExecutor::new(test_harness_fn_nop, tuple_list!()); let mut input = NopInput {}; assert!(in_mem_executor.run_target(&mut input).is_ok()); } diff --git a/afl/src/executors/mod.rs b/afl/src/executors/mod.rs index 73f8c6e8fd..ea209b8dd0 100644 --- a/afl/src/executors/mod.rs +++ b/afl/src/executors/mod.rs @@ -1,10 +1,7 @@ pub mod inmemory; -use alloc::boxed::Box; - use crate::inputs::Input; -use crate::observers::observer_serde::NamedSerdeAnyMap; -use crate::observers::Observer; +use crate::observers::ObserversTuple; use crate::AflError; /// How an execution finished. @@ -15,6 +12,29 @@ pub enum ExitKind { Timeout, } +pub trait HasObservers +where + OT: ObserversTuple, +{ + /// Get the linked observers + fn observers(&self) -> &OT; + + /// Get the linked observers + fn observers_mut(&mut self) -> &mut OT; + + /// Reset the state of all the observes linked to this executor + #[inline] + fn reset_observers(&mut self) -> Result<(), AflError> { + self.observers_mut().reset_all() + } + + /// Run the post exec hook for all the observes linked to this executor + #[inline] + fn post_exec_observers(&mut self) -> Result<(), AflError> { + self.observers_mut().post_exec_all() + } +} + /// An executor takes the given inputs, and runs the harness/target. pub trait Executor where @@ -22,31 +42,4 @@ where { /// Instruct the target about the input and run fn run_target(&mut self, input: &I) -> Result; - - /// Get the linked observers - fn observers(&self) -> &NamedSerdeAnyMap; - - /// Get the linked observers - fn observers_mut(&mut self) -> &mut NamedSerdeAnyMap; - - /// Add a linked observer - fn add_observer(&mut self, observer: Box) { - let name = observer.name().clone(); - self.observers_mut().insert(observer, &name); - } - - /// Reset the state of all the observes linked to this executor - #[inline] - fn reset_observers(&mut self) -> Result<(), AflError> { - self.observers_mut().for_each_mut(|_, x| Ok(x.reset()?))?; - Ok(()) - } - - /// Run the post exec hook for all the observes linked to this executor - #[inline] - fn post_exec_observers(&mut self) -> Result<(), AflError> { - self.observers_mut() - .for_each_mut(|_, x| Ok(x.post_exec()?))?; - Ok(()) - } } diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index 988e54f5be..4356e53b03 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -4,11 +4,11 @@ use alloc::vec::Vec; use core::marker::PhantomData; use num::Integer; +use crate::corpus::Testcase; use crate::inputs::Input; -use crate::observers::observer_serde::NamedSerdeAnyMap; -use crate::observers::MapObserver; +use crate::observers::{MapObserver, Observer, ObserversTuple}; +use crate::tuples::{MatchNameAndType, MatchType, Named, TupleList}; use crate::AflError; -use crate::{corpus::Testcase, observers::Observer}; pub type MaxMapFeedback = MapFeedback, O>; pub type MinMapFeedback = MapFeedback, O>; @@ -19,12 +19,16 @@ pub type MinMapFeedback = MapFeedback, O>; /// Feedbacks evaluate the observers. /// Basically, they reduce the information provided by an observer to a value, /// indicating the "interestingness" of the last run. -pub trait Feedback +pub trait Feedback: Named + 'static where I: Input, { /// is_interesting should return the "Interestingness" from 0 to 255 (percent times 2.55) - fn is_interesting(&mut self, input: &I, observers: &NamedSerdeAnyMap) -> Result; + fn is_interesting( + &mut self, + input: &I, + observers: &OT, + ) -> Result; /// Append to the testcase the generated metadata in case of a new corpus item #[inline] @@ -37,13 +41,82 @@ where fn discard_metadata(&mut self, _input: &I) -> Result<(), AflError> { Ok(()) } +} - /// The name of this feedback - fn name(&self) -> &String; +pub trait FeedbacksTuple: MatchType + MatchNameAndType +where + I: Input, +{ + fn is_interesting_all( + &mut self, + input: &I, + observers: &OT, + ) -> Result; + fn append_metadata_all(&mut self, testcase: &mut Testcase) -> Result<(), AflError>; + fn discard_metadata_all(&mut self, input: &I) -> Result<(), AflError>; + //fn for_each(&self, f: fn(&dyn Feedback)); + //fn for_each_mut(&mut self, f: fn(&mut dyn Feedback)); +} + +impl FeedbacksTuple for () +where + I: Input, +{ + fn is_interesting_all( + &mut self, + input: &I, + observers: &OT, + ) -> Result { + Ok(0) + } + fn append_metadata_all(&mut self, testcase: &mut Testcase) -> Result<(), AflError> { + Ok(()) + } + fn discard_metadata_all(&mut self, input: &I) -> Result<(), AflError> { + Ok(()) + } + //fn for_each(&self, f: fn(&dyn Feedback)) {} + //fn for_each_mut(&mut self, f: fn(&mut dyn Feedback)) {} +} + +impl FeedbacksTuple for (Head, Tail) +where + Head: Feedback, + Tail: FeedbacksTuple + TupleList, + I: Input, +{ + fn is_interesting_all( + &mut self, + input: &I, + observers: &OT, + ) -> Result { + Ok(self.0.is_interesting(input, observers)? + + self.1.is_interesting_all(input, observers)?) + } + + fn append_metadata_all(&mut self, testcase: &mut Testcase) -> Result<(), AflError> { + self.0.append_metadata(testcase)?; + self.1.append_metadata_all(testcase) + } + + fn discard_metadata_all(&mut self, input: &I) -> Result<(), AflError> { + self.0.discard_metadata(input)?; + self.1.discard_metadata_all(input) + } + + /*fn for_each(&self, f: fn(&dyn Feedback)) { + f(&self.0); + self.1.for_each(f) + } + + fn for_each_mut(&mut self, f: fn(&mut dyn Feedback)) { + f(self.0); + self.1.for_each_mut(f) + }*/ } /// A Reducer function is used to aggregate values for the novelty search -pub trait Reducer +pub trait Reducer: 'static where T: Integer + Copy + 'static, { @@ -102,7 +175,7 @@ where /// Contains information about untouched entries history_map: Vec, /// Name identifier of this instance - name: String, + name: &'static str, /// Phantom Data of Reducer phantom: PhantomData<(R, O)>, } @@ -114,14 +187,14 @@ where O: MapObserver + 'static, I: Input, { - fn is_interesting( + fn is_interesting( &mut self, _input: &I, - observers: &NamedSerdeAnyMap, + observers: &OT, ) -> Result { let mut interesting = 0; // TODO optimize - let observer = observers.get::(&self.name).unwrap(); + let observer = observers.match_name_type::(&self.name).unwrap(); let size = observer.map().len(); for i in 0..size { let history = self.history_map[i]; @@ -135,10 +208,17 @@ where Ok(interesting) } +} +impl Named for MapFeedback +where + T: Integer + Default + Copy + 'static, + R: Reducer, + O: MapObserver + 'static, +{ #[inline] - fn name(&self) -> &String { - &self.name + fn name(&self) -> &str { + self.name } } @@ -152,18 +232,18 @@ where pub fn new(name: &'static str, map_size: usize) -> Self { Self { history_map: vec![T::default(); map_size], - name: name.to_string(), + name: name, phantom: PhantomData, } } - pub fn new_with_observer(map_observer: &O) -> Self { + /*pub fn new_with_observer(map_observer: &O) -> Self { Self { history_map: vec![T::default(); map_observer.map().len()], - name: map_observer.name().to_string(), + name: map_observer.name(), phantom: PhantomData, } - } + }*/ } impl MapFeedback diff --git a/afl/src/lib.rs b/afl/src/lib.rs index 4af4633494..0571ff0bde 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -19,6 +19,7 @@ pub mod mutators; pub mod observers; pub mod serde_anymap; pub mod stages; +pub mod tuples; pub mod utils; use alloc::string::String; diff --git a/afl/src/observers/mod.rs b/afl/src/observers/mod.rs index 0901350791..a6be37e922 100644 --- a/afl/src/observers/mod.rs +++ b/afl/src/observers/mod.rs @@ -1,18 +1,14 @@ extern crate num; -use alloc::boxed::Box; -use alloc::string::String; -use core::any::Any; use serde::{Deserialize, Serialize}; -use crate::serde_anymap::{ArrayMut, SerdeAny}; +use crate::serde_anymap::ArrayMut; +use crate::tuples::{MatchNameAndType, MatchType, Named, TupleList}; use crate::AflError; -// TODO register each observer in the Registry in new() - /// Observers observe different information about the target. /// They can then be used by various sorts of feedback. -pub trait Observer: SerdeAny + 'static { +pub trait Observer: Named + serde::Serialize + serde::de::DeserializeOwned + 'static { /// The testcase finished execution, calculate any changes. #[inline] fn flush(&mut self) -> Result<(), AflError> { @@ -25,11 +21,53 @@ pub trait Observer: SerdeAny + 'static { fn post_exec(&mut self) -> Result<(), AflError> { Ok(()) } - - fn name(&self) -> &String; } -crate::create_serde_registry_for_trait!(observer_serde, crate::observers::Observer); +pub trait ObserversTuple: + MatchNameAndType + MatchType + serde::Serialize + serde::de::DeserializeOwned +{ + fn reset_all(&mut self) -> Result<(), AflError>; + fn post_exec_all(&mut self) -> Result<(), AflError>; + //fn for_each(&self, f: fn(&dyn Observer)); + //fn for_each_mut(&mut self, f: fn(&mut dyn Observer)); +} + +impl ObserversTuple for () { + fn reset_all(&mut self) -> Result<(), AflError> { + Ok(()) + } + fn post_exec_all(&mut self) -> Result<(), AflError> { + Ok(()) + } + //fn for_each(&self, f: fn(&dyn Observer)) { } + //fn for_each_mut(&mut self, f: fn(&mut dyn Observer)) { } +} + +impl ObserversTuple for (Head, Tail) +where + Head: Observer, + Tail: ObserversTuple + TupleList, +{ + fn reset_all(&mut self) -> Result<(), AflError> { + self.0.reset()?; + self.1.reset_all() + } + + fn post_exec_all(&mut self) -> Result<(), AflError> { + self.0.post_exec()?; + self.1.post_exec_all() + } + + /*fn for_each(&self, f: fn(&dyn Observer)) { + f(&self.0); + self.1.for_each(f) + } + + fn for_each_mut(&mut self, f: fn(&mut dyn Observer)) { + f(&mut self.0); + self.1.for_each_mut(f) + }*/ +} /// A MapObserver observes the static map, as oftentimes used for afl-like coverage information pub trait MapObserver @@ -85,25 +123,15 @@ where fn reset(&mut self) -> Result<(), AflError> { self.reset_map() } - - #[inline] - fn name(&self) -> &String { - &self.name - } } -impl SerdeAny for StdMapObserver +impl Named for StdMapObserver where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { #[inline] - fn as_any(&self) -> &dyn Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn Any { - self + fn name(&self) -> &str { + self.name.as_str() } } @@ -143,7 +171,6 @@ where { /// Creates a new MapObserver pub fn new(name: &'static str, map: &'static mut [T]) -> Self { - observer_serde::RegistryBuilder::register::(); let initial = if map.len() > 0 { map[0] } else { T::default() }; Self { map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), @@ -154,7 +181,6 @@ where /// Creates a new MapObserver from a raw pointer pub fn new_from_ptr(name: &'static str, map_ptr: *mut T, len: usize) -> Self { - observer_serde::RegistryBuilder::register::(); unsafe { let initial = if len > 0 { *map_ptr } else { T::default() }; StdMapObserver { @@ -166,6 +192,7 @@ where } } +/* #[cfg(feature = "std")] #[cfg(test)] mod tests { @@ -183,3 +210,4 @@ mod tests { assert_eq!(d.name(), o.name()); } } +*/ diff --git a/afl/src/stages/mod.rs b/afl/src/stages/mod.rs index 202d539056..f63dc2b855 100644 --- a/afl/src/stages/mod.rs +++ b/afl/src/stages/mod.rs @@ -4,17 +4,22 @@ pub use mutational::StdMutationalStage; use crate::corpus::Corpus; use crate::engines::{Engine, State}; use crate::events::EventManager; -use crate::executors::Executor; +use crate::executors::{Executor, HasObservers}; +use crate::feedbacks::FeedbacksTuple; use crate::inputs::Input; +use crate::observers::ObserversTuple; +use crate::tuples::{MatchType, TupleList}; use crate::utils::Rand; use crate::AflError; /// A stage is one step in the fuzzing process. /// Multiple stages will be scheduled one by one for each input. -pub trait Stage +pub trait Stage where - EM: EventManager, - E: Executor, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, @@ -23,10 +28,96 @@ where fn perform( &mut self, rand: &mut R, - state: &mut State, + state: &mut State, corpus: &mut C, - engine: &mut Engine, + engine: &mut Engine, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError>; } + +pub trait StagesTuple +where + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, + C: Corpus, + I: Input, + R: Rand, +{ + fn perform_all( + &mut self, + rand: &mut R, + state: &mut State, + corpus: &mut C, + engine: &mut Engine, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), AflError>; + fn for_each(&self, f: fn(&dyn Stage)); + fn for_each_mut(&mut self, f: fn(&mut dyn Stage)); +} + +impl StagesTuple for () +where + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, + C: Corpus, + I: Input, + R: Rand, +{ + fn perform_all( + &mut self, + rand: &mut R, + state: &mut State, + corpus: &mut C, + engine: &mut Engine, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), AflError> { + Ok(()) + } + fn for_each(&self, f: fn(&dyn Stage)) {} + fn for_each_mut(&mut self, f: fn(&mut dyn Stage)) {} +} + +impl StagesTuple for (Head, Tail) +where + Head: Stage, + Tail: StagesTuple + TupleList, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, + C: Corpus, + I: Input, + R: Rand, +{ + fn perform_all( + &mut self, + rand: &mut R, + state: &mut State, + corpus: &mut C, + engine: &mut Engine, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), AflError> { + self.0 + .perform(rand, state, corpus, engine, manager, corpus_idx)?; + self.1 + .perform_all(rand, state, corpus, engine, manager, corpus_idx) + } + + fn for_each(&self, f: fn(&dyn Stage)) { + f(&self.0); + self.1.for_each(f) + } + + fn for_each_mut(&mut self, f: fn(&mut dyn Stage)) { + f(&mut self.0); + self.1.for_each_mut(f) + } +} diff --git a/afl/src/stages/mutational.rs b/afl/src/stages/mutational.rs index a7d79a9051..804326e020 100644 --- a/afl/src/stages/mutational.rs +++ b/afl/src/stages/mutational.rs @@ -1,9 +1,11 @@ use core::marker::PhantomData; use crate::events::EventManager; -use crate::executors::Executor; +use crate::executors::{Executor, HasObservers}; +use crate::feedbacks::FeedbacksTuple; use crate::inputs::Input; use crate::mutators::Mutator; +use crate::observers::ObserversTuple; use crate::stages::Corpus; use crate::stages::{Engine, Stage}; use crate::utils::Rand; @@ -17,11 +19,13 @@ use crate::serde_anymap::{Ptr, PtrMut}; /// A Mutational stage is the stage in a fuzzing run that mutates inputs. /// Mutational stages will usually have a range of mutations that are /// being applied to the input one by one, between executions. -pub trait MutationalStage: Stage +pub trait MutationalStage: Stage where M: Mutator, - EM: EventManager, - E: Executor, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, @@ -43,9 +47,9 @@ where fn perform_mutational( &mut self, rand: &mut R, - state: &mut State, + state: &mut State, corpus: &mut C, - engine: &mut Engine, + engine: &mut Engine, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError> { @@ -82,24 +86,29 @@ where } /// The default mutational stage -pub struct StdMutationalStage +pub struct StdMutationalStage where M: Mutator, - EM: EventManager, - E: Executor, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, { mutator: M, - phantom: PhantomData<(EM, E, C, I, R)>, + phantom: PhantomData<(EM, E, OT, FT, C, I, R)>, } -impl MutationalStage for StdMutationalStage +impl MutationalStage + for StdMutationalStage where M: Mutator, - EM: EventManager, - E: Executor, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, @@ -117,11 +126,14 @@ where } } -impl Stage for StdMutationalStage +impl Stage + for StdMutationalStage where M: Mutator, - EM: EventManager, - E: Executor, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, @@ -130,9 +142,9 @@ where fn perform( &mut self, rand: &mut R, - state: &mut State, + state: &mut State, corpus: &mut C, - engine: &mut Engine, + engine: &mut Engine, manager: &mut EM, corpus_idx: usize, ) -> Result<(), AflError> { @@ -140,11 +152,13 @@ where } } -impl StdMutationalStage +impl StdMutationalStage where M: Mutator, - EM: EventManager, - E: Executor, + EM: EventManager, + E: Executor + HasObservers, + OT: ObserversTuple, + FT: FeedbacksTuple, C: Corpus, I: Input, R: Rand, diff --git a/afl/src/tuples.rs b/afl/src/tuples.rs index 2972a9723e..d7209b8bab 100644 --- a/afl/src/tuples.rs +++ b/afl/src/tuples.rs @@ -1,16 +1,21 @@ -use tuple_list::TupleList; -use tuple_list::tuple_list; -use core::any::{TypeId, Any}; +pub use tuple_list::tuple_list; +pub use tuple_list::tuple_list_type; +pub use tuple_list::TupleList; + +use core::any::TypeId; pub trait HasLen { fn len(&self) -> usize; } impl HasLen for () { - fn len(&self) -> usize { 0 } + fn len(&self) -> usize { + 0 + } } - -impl HasLen for (Head, Tail) where + +impl HasLen for (Head, Tail) +where Tail: TupleList + HasLen, { fn len(&self) -> usize { @@ -20,13 +25,20 @@ impl HasLen for (Head, Tail) where pub trait MatchFirstType { fn match_first_type(&self) -> Option<&T>; + fn match_first_type_mut(&mut self) -> Option<&mut T>; } impl MatchFirstType for () { - fn match_first_type(&self) -> Option<&T> { None } + fn match_first_type(&self) -> Option<&T> { + None + } + fn match_first_type_mut(&mut self) -> Option<&mut T> { + None + } } - -impl MatchFirstType for (Head, Tail) where + +impl MatchFirstType for (Head, Tail) +where Head: 'static, Tail: TupleList + MatchFirstType, { @@ -37,17 +49,32 @@ impl MatchFirstType for (Head, Tail) where self.1.match_first_type::() } } + + fn match_first_type_mut(&mut self) -> Option<&mut T> { + if TypeId::of::() == TypeId::of::() { + unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() } + } else { + self.1.match_first_type_mut::() + } + } } pub trait MatchType { fn match_type(&self, f: fn(t: &T)); + fn match_type_mut(&mut self, f: fn(t: &mut T)); } impl MatchType for () { - fn match_type(&self, f: fn(t: &T)) { () } + fn match_type(&self, f: fn(t: &T)) { + () + } + fn match_type_mut(&mut self, f: fn(t: &mut T)) { + () + } } - -impl MatchType for (Head, Tail) where + +impl MatchType for (Head, Tail) +where Head: 'static, Tail: TupleList + MatchType, { @@ -57,21 +84,35 @@ impl MatchType for (Head, Tail) where } self.1.match_type::(f); } + + fn match_type_mut(&mut self, f: fn(t: &mut T)) { + if TypeId::of::() == TypeId::of::() { + f(unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() }.unwrap()); + } + self.1.match_type_mut::(f); + } } pub trait Named { - fn name(&self) -> &'static str; + fn name(&self) -> &str; } pub trait MatchNameAndType { fn match_name_type(&self, name: &'static str) -> Option<&T>; + fn match_name_type_mut(&mut self, name: &'static str) -> Option<&mut T>; } impl MatchNameAndType for () { - fn match_name_type(&self, name: &'static str) -> Option<&T> { None } + fn match_name_type(&self, name: &'static str) -> Option<&T> { + None + } + fn match_name_type_mut(&mut self, name: &'static str) -> Option<&mut T> { + None + } } - -impl MatchNameAndType for (Head, Tail) where + +impl MatchNameAndType for (Head, Tail) +where Head: 'static + Named, Tail: TupleList + MatchNameAndType, { @@ -82,6 +123,14 @@ impl MatchNameAndType for (Head, Tail) where self.1.match_name_type::(name) } } + + fn match_name_type_mut(&mut self, name: &'static str) -> Option<&mut T> { + if TypeId::of::() == TypeId::of::() && name == self.0.name() { + unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() } + } else { + self.1.match_name_type_mut::(name) + } + } } pub trait Prepend: TupleList { @@ -91,35 +140,41 @@ pub trait Prepend: TupleList { } /// Implement prepend for tuple list. -impl Prepend for Tail where Tail: TupleList { +impl Prepend for Tail +where + Tail: TupleList, +{ type PreprendResult = Self; fn prepend(self, value: T) -> (T, Self::PreprendResult) { (value, self) } } - + pub trait Append: TupleList { type AppendResult: TupleList; - + fn append(self, value: T) -> Self::AppendResult; } - + /// Implement append for an empty tuple list. impl Append for () { type AppendResult = (T, ()); - - fn append(self, value: T) -> Self::AppendResult { (value, ()) } + + fn append(self, value: T) -> Self::AppendResult { + (value, ()) + } } - + /// Implement append for non-empty tuple list. -impl Append for (Head, Tail) where +impl Append for (Head, Tail) +where Self: TupleList, Tail: Append, (Head, Tail::AppendResult): TupleList, { type AppendResult = (Head, Tail::AppendResult); - + fn append(self, value: T) -> Self::AppendResult { let (head, tail) = self; return (head, tail.append(value)); @@ -134,13 +189,13 @@ trait PlusOne { } impl PlusOne for i32 { fn plus_one(&mut self) { *self += 1; } } impl PlusOne for String { fn plus_one(&mut self) { self.push('1'); } } - + // Now we have to implement trait for an empty tuple, // thus defining initial condition. impl PlusOne for () { fn plus_one(&mut self) {} } - + // Now we can implement trait for a non-empty tuple list, // thus defining recursion and supporting tuple lists of arbitrary length. impl PlusOne for (Head, Tail) where diff --git a/fuzzers/libfuzzer/src/lib.rs b/fuzzers/libfuzzer/src/lib.rs index c4e17e4923..70c020a273 100644 --- a/fuzzers/libfuzzer/src/lib.rs +++ b/fuzzers/libfuzzer/src/lib.rs @@ -22,8 +22,9 @@ use afl::mutators::HasMaxSize; use afl::observers::StdMapObserver; use afl::stages::mutational::StdMutationalStage; use afl::utils::StdRand; +use afl::tuples::tuple_list; -// const MAP_SIZE: usize = 65536; +const MAP_SIZE: usize = 65536; #[no_mangle] extern "C" { @@ -59,18 +60,12 @@ pub extern "C" fn afl_libfuzzer_main() { StdMapObserver::new_from_ptr(&NAME_COV_MAP, unsafe { __lafl_edges_map }, unsafe { __lafl_max_edges_size as usize }); - let edges_feedback = MaxMapFeedback::new_with_observer(&edges_observer); - - let mut executor = InMemoryExecutor::new(harness); - let mut state = State::new(); - executor.add_observer(Box::new(edges_observer)); - state.add_feedback(Box::new(edges_feedback)); + let edges_feedback = MaxMapFeedback::>::new(&NAME_COV_MAP, MAP_SIZE); + let executor = InMemoryExecutor::new(harness, tuple_list!(edges_observer)); + let mut state = State::new(tuple_list!(edges_feedback)); + let mut engine = Engine::new(executor); - let mut mutator = HavocBytesMutator::new_default(); - mutator.set_max_size(4096); - - let stage = StdMutationalStage::new(mutator); state .generate_initial_inputs( @@ -83,9 +78,11 @@ pub extern "C" fn afl_libfuzzer_main() { ) .expect("Failed to load initial inputs"); - let mut fuzzer = StdFuzzer::new(); + let mut mutator = HavocBytesMutator::new_default(); + mutator.set_max_size(4096); - fuzzer.add_stage(Box::new(stage)); + let stage = StdMutationalStage::new(mutator); + let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); fuzzer .fuzz_loop(&mut rand, &mut state, &mut corpus, &mut engine, &mut events)