diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index dc2b23c90f..49869a4a63 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -18,10 +18,10 @@ use crate::{ }, corpus::Corpus, events::{BrokerEventResult, Event, EventManager}, + executors::ExitKind, feedbacks::FeedbacksTuple, inputs::Input, state::State, - executors::ExitKind, stats::Stats, utils::Rand, AflError, diff --git a/afl/src/executors/inprocess.rs b/afl/src/executors/inprocess.rs index 9662d9cced..93b5d36bbf 100644 --- a/afl/src/executors/inprocess.rs +++ b/afl/src/executors/inprocess.rs @@ -71,7 +71,12 @@ where #[cfg(unix)] #[cfg(feature = "std")] unsafe { - set_oncrash_ptrs::(_state, _event_mgr, _input); + set_oncrash_ptrs::( + _state, + _event_mgr, + self.observers(), + _input, + ); } Ok(()) } @@ -216,6 +221,7 @@ pub mod unix_signals { use crate::{ corpus::Corpus, events::{Event, EventManager}, + executors::ExitKind, feedbacks::FeedbacksTuple, inputs::Input, observers::ObserversTuple, @@ -231,6 +237,7 @@ pub mod unix_signals { /// we should (tm) be okay with raw pointers here, static mut STATE_PTR: *mut c_void = ptr::null_mut(); static mut EVENT_MGR_PTR: *mut c_void = ptr::null_mut(); + static mut OBSERVERS_PTR: *const c_void = ptr::null(); /// The (unsafe) pointer to the current inmem input, for the current run. /// This is neede for certain non-rust side effects, as well as unix signal handling. static mut CURRENT_INPUT_PTR: *const c_void = ptr::null(); @@ -260,6 +267,8 @@ pub mod unix_signals { Ok(maps) => println!("maps:\n{}", maps), Err(e) => println!("Couldn't load mappings: {:?}", e), }; + + // TODO tell the parent to not restart std::process::exit(1); } @@ -275,6 +284,18 @@ pub mod unix_signals { .as_mut() .unwrap(); let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap(); + + let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap(); + let obj_fitness = state + .objective_feedbacks_mut() + .is_interesting_all(&input, observers, ExitKind::Crash) + .expect("In crash handler objective feedbacks failure.".into()); + if obj_fitness > 0 { + state + .add_if_objective(input.clone(), obj_fitness) + .expect("In crash handler objective corpus add failure.".into()); + } + mgr.fire( state, Event::Crash { @@ -323,6 +344,17 @@ pub mod unix_signals { .unwrap(); let mgr = (EVENT_MGR_PTR as *mut EM).as_mut().unwrap(); + let observers = (OBSERVERS_PTR as *const OT).as_ref().unwrap(); + let obj_fitness = state + .objective_feedbacks_mut() + .is_interesting_all(&input, observers, ExitKind::Timeout) + .expect("In timeout handler objective feedbacks failure.".into()); + if obj_fitness > 0 { + state + .add_if_objective(input.clone(), obj_fitness) + .expect("In timeout handler objective corpus add failure.".into()); + } + mgr.fire( state, Event::Timeout { @@ -342,6 +374,7 @@ pub mod unix_signals { pub unsafe fn set_oncrash_ptrs( state: &mut State, event_mgr: &mut EM, + observers: &OT, input: &I, ) where EM: EventManager, @@ -356,6 +389,7 @@ pub mod unix_signals { CURRENT_INPUT_PTR = input as *const _ as *const c_void; STATE_PTR = state as *mut _ as *mut c_void; EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void; + OBSERVERS_PTR = observers as *const _ as *const c_void; } #[inline] @@ -363,6 +397,7 @@ pub mod unix_signals { CURRENT_INPUT_PTR = ptr::null(); STATE_PTR = ptr::null_mut(); EVENT_MGR_PTR = ptr::null_mut(); + OBSERVERS_PTR = ptr::null_mut(); } // TODO clearly state that manager should be static (maybe put the 'static lifetime?) diff --git a/afl/src/executors/mod.rs b/afl/src/executors/mod.rs index da0e2b94e7..0ade5b6728 100644 --- a/afl/src/executors/mod.rs +++ b/afl/src/executors/mod.rs @@ -5,6 +5,7 @@ pub use inprocess::InProcessExecutor; #[cfg(feature = "runtime")] pub mod runtime; +use core::cmp::PartialEq; use core::marker::PhantomData; use crate::{ @@ -20,7 +21,7 @@ use crate::{ }; /// How an execution finished. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum ExitKind { Ok, Crash, diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index 8d3651ae9c..b4872e8e4c 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize}; use crate::{ bolts::tuples::{Named, TupleList}, - executors::ExitKind, corpus::Testcase, + executors::ExitKind, inputs::Input, observers::{MapObserver, Observer, ObserversTuple}, AflError, @@ -106,7 +106,12 @@ where I: Input, { #[inline] - fn is_interesting_all(&mut self, _: &I, _: &OT, _: ExitKind,) -> Result { + fn is_interesting_all( + &mut self, + _: &I, + _: &OT, + _: ExitKind, + ) -> Result { Ok(0) } @@ -161,6 +166,41 @@ where */ } +/// Is a crash feedback +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct CrashFeedback {} + +impl Feedback for CrashFeedback +where + I: Input, +{ + fn is_interesting( + &mut self, + _input: &I, + _observers: &OT, + exit_kind: ExitKind, + ) -> Result { + if exit_kind == ExitKind::Crash { + Ok(1) + } else { + Ok(0) + } + } +} + +impl Named for CrashFeedback { + #[inline] + fn name(&self) -> &str { + "CrashFeedback" + } +} + +impl CrashFeedback { + pub fn new() -> Self { + Self {} + } +} + /// A Reducer function is used to aggregate values for the novelty search pub trait Reducer: Serialize + serde::de::DeserializeOwned + 'static where diff --git a/afl/src/mutators/mod.rs b/afl/src/mutators/mod.rs index b8c6e57fd1..8f9a3e59c8 100644 --- a/afl/src/mutators/mod.rs +++ b/afl/src/mutators/mod.rs @@ -41,7 +41,6 @@ where &mut self, _state: &mut S, _is_interesting: u32, - _input: &I, _stage_idx: i32, ) -> Result<(), AflError> { Ok(()) diff --git a/afl/src/stages/mutational.rs b/afl/src/stages/mutational.rs index f18421f3a8..f776abb29f 100644 --- a/afl/src/stages/mutational.rs +++ b/afl/src/stages/mutational.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use crate::{ - events::{Event, EventManager}, + events::EventManager, executors::{Executor, HasObservers}, feedbacks::FeedbacksTuple, inputs::Input, @@ -66,37 +66,9 @@ where self.mutator_mut() .mutate(rand, state, &mut input_mut, i as i32)?; - let fitness = state.evaluate_input(&input_mut, executor, manager)?; + let fitness = state.process_input(input_mut, executor, manager)?; - self.mutator_mut() - .post_exec(state, fitness, &input_mut, i as i32)?; - - let observers = executor.observers(); - - // put all this shit in some overridable function in engine maybe? or in corpus. - // consider a corpus that strores new testcases in a temporary queue, for later processing - // in a late stage, NewTestcase should be triggere donly after the processing in the later stage - // So by default we shoudl trigger it in corpus.add, so that the user can override it and remove - // if needed by particular cases - if fitness > 0 { - let observers_buf = manager.serialize_observers(observers)?; - - manager.fire( - state, - Event::NewTestcase { - input: input_mut.clone(), - observers_buf, - corpus_size: state.corpus().count() + 1, - client_config: "TODO".into(), - time: crate::utils::current_time(), - executions: state.executions(), - }, - )?; - state.add_if_interesting(input_mut, fitness)?; - // let _ = corpus.add(testcase); - } else { - state.discard_input(&input_mut)?; - } + self.mutator_mut().post_exec(state, fitness, i as i32)?; } Ok(()) } diff --git a/afl/src/state/mod.rs b/afl/src/state/mod.rs index c32d6b9836..72dc25351a 100644 --- a/afl/src/state/mod.rs +++ b/afl/src/state/mod.rs @@ -12,7 +12,7 @@ use crate::{ bolts::serdeany::{SerdeAny, SerdeAnyMap}, corpus::{Corpus, Testcase}, events::{Event, EventManager, LogSeverity}, - executors::{Executor, HasObservers, ExitKind}, + executors::{Executor, ExitKind, HasObservers}, feedbacks::FeedbacksTuple, generators::Generator, inputs::Input, @@ -121,10 +121,13 @@ where println!("Loading file {:?} ...", &path); let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); - let fitness = self.evaluate_input(&input, executor, manager)?; + let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?; if self.add_if_interesting(input, fitness)?.is_none() { println!("File {:?} was not interesting, skipped.", &path); } + if obj_fitness > 0 { + println!("File {:?} is an objective, however will be not added as an initial testcase.", &path); + } } else if attr.is_dir() { self.load_from_directory(executor, manager, &path)?; } @@ -246,13 +249,32 @@ where &mut self.feedbacks } + /// Returns vector of objective feebacks + #[inline] + pub fn objective_feedbacks(&self) -> &OFT { + &self.objective_feedbacks + } + + /// Returns vector of objective feebacks (mutable) + #[inline] + pub fn objective_feedbacks_mut(&mut self) -> &mut OFT { + &mut self.objective_feedbacks + } + // TODO move some of these, like evaluate_input, to FuzzingEngine #[inline] - pub fn is_interesting(&mut self, input: &I, observers: &OT, exit_kind: ExitKind) -> Result + pub fn is_interesting( + &mut self, + input: &I, + observers: &OT, + exit_kind: ExitKind, + ) -> Result where OT: ObserversTuple, { - Ok(self.feedbacks_mut().is_interesting_all(input, observers, exit_kind)?) + Ok(self + .feedbacks_mut() + .is_interesting_all(input, observers, exit_kind)?) } /// Runs the input and triggers observers and feedback @@ -261,7 +283,7 @@ where input: &I, executor: &mut E, event_mgr: &mut EM, - ) -> Result + ) -> Result<(u32, u32), AflError> where E: Executor + HasObservers, OT: ObserversTuple, @@ -278,8 +300,13 @@ where executor.post_exec_observers()?; let observers = executor.observers(); - let fitness = self.feedbacks_mut().is_interesting_all(&input, observers, exit_kind)?; - Ok(fitness) + let objective_fitness = + self.objective_feedbacks + .is_interesting_all(&input, observers, exit_kind.clone())?; + let fitness = self + .feedbacks_mut() + .is_interesting_all(&input, observers, exit_kind)?; + Ok((fitness, objective_fitness)) } /// Resets all current feedbacks @@ -328,6 +355,64 @@ where } } + /// Adds this input to the objective corpus, if it's an objective + #[inline] + pub fn add_if_objective(&mut self, input: I, fitness: u32) -> Result, AflError> + where + C: Corpus, + { + if fitness > 0 { + let testcase = self.input_to_testcase(input, fitness)?; + Ok(Some(self.objective_corpus.add(testcase))) + } else { + self.discard_input(&input)?; + Ok(None) + } + } + + /// Process one input, adding to the respective corpuses if needed and firing the right events + #[inline] + pub fn process_input( + &mut self, + // TODO probably we can take a ref to input and pass a cloned one to add_if_interesting + input: I, + executor: &mut E, + manager: &mut EM, + ) -> Result + where + E: Executor + HasObservers, + OT: ObserversTuple, + C: Corpus, + EM: EventManager, + { + let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?; + let observers = executor.observers(); + + if obj_fitness > 0 { + self.add_if_objective(input.clone(), obj_fitness)?; + } + + if fitness > 0 { + let observers_buf = manager.serialize_observers(observers)?; + manager.fire( + self, + Event::NewTestcase { + input: input.clone(), + observers_buf, + corpus_size: self.corpus().count() + 1, + client_config: "TODO".into(), + time: crate::utils::current_time(), + executions: self.executions(), + }, + )?; + self.add_if_interesting(input, fitness)?; + } else { + self.discard_input(&input)?; + } + + Ok(fitness) + } + pub fn generate_initial_inputs( &mut self, rand: &mut R, @@ -346,8 +431,8 @@ where let mut added = 0; for _ in 0..num { let input = generator.generate(rand)?; - let fitness = self.evaluate_input(&input, executor, manager)?; - if !self.add_if_interesting(input, fitness)?.is_none() { + let fitness = self.process_input(input, executor, manager)?; + if fitness > 0 { added += 1; } } diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng/harness.cc index 892f9c8c70..8d276ed86a 100644 --- a/fuzzers/libfuzzer_libpng/harness.cc +++ b/fuzzers/libfuzzer_libpng/harness.cc @@ -20,7 +20,7 @@ #include -//#define HAS_BUG 1 +#define HAS_BUG 1 #define PNG_INTERNAL #include "png.h" @@ -159,9 +159,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // This is going to be too slow. if (width && height > 100000000 / width) { PNG_CLEANUP -#ifdef HAS_BUG - asm("ud2"); -#endif + if (HAS_BUG) + asm("ud2"); return 0; } diff --git a/fuzzers/libfuzzer_libpng/src/mod.rs b/fuzzers/libfuzzer_libpng/src/mod.rs index fdcaa9bd6a..c503d91680 100644 --- a/fuzzers/libfuzzer_libpng/src/mod.rs +++ b/fuzzers/libfuzzer_libpng/src/mod.rs @@ -8,7 +8,7 @@ use afl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::setup_restarting_mgr, executors::{inprocess::InProcessExecutor, Executor, ExitKind}, - feedbacks::MaxMapFeedback, + feedbacks::{CrashFeedback, MaxMapFeedback}, inputs::Input, mutators::scheduled::HavocBytesMutator, mutators::token_mutations::TokensMetadata, @@ -96,7 +96,7 @@ fn fuzz( &edges_observer )), OnDiskCorpus::new(objective_dir), - tuple_list!(), + tuple_list!(CrashFeedback::new()), )); println!("We're a client, let's fuzz :)");