diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index a34dade086..857c2dea28 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -312,6 +312,9 @@ impl HasInProcessHooks #[inline] /// Save state if it is an objective +/// Note that unlike the logic in fuzzer/mod.rs +/// This will *NOT* put any testcase into the corpus. +/// As it totally does not make any sense to put when we use inprocess executor or its descendants. pub fn run_observers_and_save_state( executor: &mut E, state: &mut S, diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 7196917700..1951890f4d 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -191,7 +191,7 @@ pub trait Evaluator { executor: &mut E, manager: &mut EM, input: I, - ) -> Result; + ) -> Result<(CorpusId, ExecuteInputResult), Error>; /// Adds the input to the corpus as a disabled input. /// Used during initial corpus loading. @@ -248,15 +248,44 @@ pub trait Fuzzer { ) -> Result; } -/// The result of harness execution -#[derive(Debug, PartialEq, Eq)] -pub enum ExecuteInputResult { - /// No special input - None, - /// This input should be stored in the corpus - Corpus, - /// This input leads to a solution - Solution, +/// The corpus this input should be added to +#[derive(Debug, PartialEq, Eq, Default)] +pub struct ExecuteInputResult { + is_corpus: bool, + is_solution: bool, +} + +impl ExecuteInputResult { + /// Constructor + #[must_use] + pub fn new(is_corpus: bool, is_solution: bool) -> Self { + Self { + is_corpus, + is_solution, + } + } + + /// if this is corpus worthy + #[must_use] + pub fn is_corpus(&self) -> bool { + self.is_corpus + } + + /// if this is solution worthy + #[must_use] + pub fn is_solution(&self) -> bool { + self.is_solution + } + + /// tell that this is corpus + pub fn set_is_corpus(&mut self, v: bool) { + self.is_corpus = v; + } + + /// tell that this is solution + pub fn set_is_solution(&mut self, v: bool) { + self.is_solution = v; + } } /// Your default fuzzer instance, for everyday use. @@ -339,7 +368,7 @@ where observers: &OT, exit_kind: &ExitKind, ) -> Result { - let mut res = ExecuteInputResult::None; + let mut res = ExecuteInputResult::default(); #[cfg(not(feature = "introspection"))] let is_solution = self @@ -352,20 +381,20 @@ where .is_interesting_introspection(state, manager, input, observers, exit_kind)?; if is_solution { - res = ExecuteInputResult::Solution; - } else { - #[cfg(not(feature = "introspection"))] - let corpus_worthy = self - .feedback_mut() - .is_interesting(state, manager, input, observers, exit_kind)?; - #[cfg(feature = "introspection")] - let corpus_worthy = self - .feedback_mut() - .is_interesting_introspection(state, manager, input, observers, exit_kind)?; + res.set_is_solution(true); + } - if corpus_worthy { - res = ExecuteInputResult::Corpus; - } + #[cfg(not(feature = "introspection"))] + let corpus_worthy = self + .feedback_mut() + .is_interesting(state, manager, input, observers, exit_kind)?; + #[cfg(feature = "introspection")] + let corpus_worthy = self + .feedback_mut() + .is_interesting_introspection(state, manager, input, observers, exit_kind)?; + + if corpus_worthy { + res.set_is_corpus(true); } Ok(res) @@ -373,6 +402,7 @@ where /// Post process a testcase depending the testcase execution results /// returns corpus id if it put something into corpus (not solution) + /// This code will not be reached by inprocess executor if crash happened. fn process_execution( &mut self, state: &mut S, @@ -381,39 +411,36 @@ where exec_res: &ExecuteInputResult, observers: &OT, ) -> Result, Error> { - match exec_res { - ExecuteInputResult::None => Ok(None), - ExecuteInputResult::Corpus => { - // Not a solution - // Add the input to the main corpus - let mut testcase = Testcase::from(input.clone()); - #[cfg(feature = "track_hit_feedbacks")] - self.feedback_mut() - .append_hit_feedbacks(testcase.hit_feedbacks_mut())?; - self.feedback_mut() - .append_metadata(state, manager, observers, &mut testcase)?; - let id = state.corpus_mut().add(testcase)?; - self.scheduler_mut().on_add(state, id)?; + let corpus = if exec_res.is_corpus() { + // Add the input to the main corpus + let mut testcase = Testcase::from(input.clone()); + #[cfg(feature = "track_hit_feedbacks")] + self.feedback_mut() + .append_hit_feedbacks(testcase.hit_feedbacks_mut())?; + self.feedback_mut() + .append_metadata(state, manager, observers, &mut testcase)?; + let id = state.corpus_mut().add(testcase)?; + self.scheduler_mut().on_add(state, id)?; + Ok(Some(id)) + } else { + Ok(None) + }; - Ok(Some(id)) - } - ExecuteInputResult::Solution => { - // The input is a solution, add it to the respective corpus - let mut testcase = Testcase::from(input.clone()); - testcase.set_parent_id_optional(*state.corpus().current()); - if let Ok(mut tc) = state.current_testcase_mut() { - tc.found_objective(); - } - #[cfg(feature = "track_hit_feedbacks")] - self.objective_mut() - .append_hit_feedbacks(testcase.hit_objectives_mut())?; - self.objective_mut() - .append_metadata(state, manager, observers, &mut testcase)?; - state.solutions_mut().add(testcase)?; - - Ok(None) + if exec_res.is_solution() { + // The input is a solution, add it to the respective corpus + let mut testcase = Testcase::from(input.clone()); + testcase.set_parent_id_optional(*state.corpus().current()); + if let Ok(mut tc) = state.current_testcase_mut() { + tc.found_objective(); } + #[cfg(feature = "track_hit_feedbacks")] + self.objective_mut() + .append_hit_feedbacks(testcase.hit_objectives_mut())?; + self.objective_mut() + .append_metadata(state, manager, observers, &mut testcase)?; + state.solutions_mut().add(testcase)?; } + corpus } fn serialize_and_dispatch( @@ -426,20 +453,14 @@ where exit_kind: &ExitKind, ) -> Result<(), Error> { // Now send off the event - let observers_buf = match exec_res { - ExecuteInputResult::Corpus => { - if manager.should_send() { - // TODO set None for fast targets - if manager.configuration() == EventConfig::AlwaysUnique { - None - } else { - Some(postcard::to_allocvec(observers)?) - } - } else { - None - } - } - _ => None, + let observers_buf = if exec_res.is_corpus() + && manager.should_send() + && manager.configuration() != EventConfig::AlwaysUnique + { + // TODO set None for fast targets + Some(postcard::to_allocvec(observers)?) + } else { + None }; self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?; @@ -456,40 +477,35 @@ where exit_kind: &ExitKind, ) -> Result<(), Error> { // Now send off the event - - match exec_res { - ExecuteInputResult::Corpus => { - if manager.should_send() { - manager.fire( - state, - Event::NewTestcase { - input: input.clone(), - observers_buf, - exit_kind: *exit_kind, - corpus_size: state.corpus().count(), - client_config: manager.configuration(), - time: current_time(), - forward_id: None, - #[cfg(all(unix, feature = "std", feature = "multi_machine"))] - node_id: None, - }, - )?; - } + if manager.should_send() { + if exec_res.is_corpus() { + manager.fire( + state, + Event::NewTestcase { + input: input.clone(), + observers_buf, + exit_kind: *exit_kind, + corpus_size: state.corpus().count(), + client_config: manager.configuration(), + time: current_time(), + forward_id: None, + #[cfg(all(unix, feature = "std", feature = "multi_machine"))] + node_id: None, + }, + )?; } - ExecuteInputResult::Solution => { - if manager.should_send() { - manager.fire( - state, - Event::Objective { - input: self.share_objectives.then_some(input.clone()), - objective_size: state.solutions().count(), - time: current_time(), - }, - )?; - } + if exec_res.is_solution() { + manager.fire( + state, + Event::Objective { + input: self.share_objectives.then_some(input.clone()), + objective_size: state.solutions().count(), + time: current_time(), + }, + )?; } - ExecuteInputResult::None => (), } + Ok(()) } @@ -507,7 +523,7 @@ where if send_events { self.serialize_and_dispatch(state, manager, input, &exec_res, observers, exit_kind)?; } - if exec_res != ExecuteInputResult::None { + if exec_res.is_corpus() || exec_res.is_solution() { *state.last_found_time_mut() = current_time(); } Ok((exec_res, corpus_id)) @@ -614,7 +630,7 @@ where if self.input_filter.should_execute(input) { self.evaluate_input(state, executor, manager, input) } else { - Ok((ExecuteInputResult::None, None)) + Ok((ExecuteInputResult::default(), None)) } } @@ -631,13 +647,15 @@ where } /// Adds an input, even if it's not considered `interesting` by any of the executors + /// If you are using inprocess executor, be careful. + /// Your crash-causing testcase will *NOT* be added into the corpus (only to solution) fn add_input( &mut self, state: &mut S, executor: &mut E, manager: &mut EM, input: I, - ) -> Result { + ) -> Result<(CorpusId, ExecuteInputResult), Error> { *state.last_found_time_mut() = current_time(); let exit_kind = self.execute_input(state, executor, manager, &input)?; @@ -667,7 +685,7 @@ where self.objective_mut() .append_metadata(state, manager, &*observers, &mut testcase)?; // we don't care about solution id - let id = state.solutions_mut().add(testcase)?; + let _ = state.solutions_mut().add(testcase.clone())?; manager.fire( state, @@ -677,22 +695,17 @@ where time: current_time(), }, )?; - - // if it is a solution then early return - return Ok(id); } - // not a solution - // several is_interesting implementations collect some data about the run, later used in // append_metadata; we *must* invoke is_interesting here to collect it #[cfg(not(feature = "introspection"))] - let _is_corpus = + let corpus_worthy = self.feedback_mut() .is_interesting(state, manager, &input, &*observers, &exit_kind)?; #[cfg(feature = "introspection")] - let _is_corpus = self.feedback_mut().is_interesting_introspection( + let corpus_worthy = self.feedback_mut().is_interesting_introspection( state, manager, &input, @@ -728,7 +741,7 @@ where node_id: None, }, )?; - Ok(id) + Ok((id, ExecuteInputResult::new(corpus_worthy, is_solution))) } fn add_disabled_input(&mut self, state: &mut S, input: I) -> Result { diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 7cfdbb8323..4308e192bf 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -26,6 +26,8 @@ use serde::{Deserialize, Serialize, de::DeserializeOwned}; mod stack; pub use stack::StageStack; +#[cfg(feature = "std")] +use crate::fuzzer::ExecuteInputResult; #[cfg(feature = "introspection")] use crate::monitors::stats::ClientPerfStats; use crate::{ @@ -33,7 +35,7 @@ use crate::{ corpus::{Corpus, CorpusId, HasCurrentCorpusId, HasTestcase, InMemoryCorpus, Testcase}, events::{Event, EventFirer, LogSeverity}, feedbacks::StateInitializer, - fuzzer::{Evaluator, ExecuteInputResult}, + fuzzer::Evaluator, generators::Generator, inputs::{Input, NopInput}, stages::StageId, @@ -715,15 +717,15 @@ where Ok(input) => input, Err(err) => { log::error!("Skipping input that we could not load from {path:?}: {err:?}"); - return Ok(ExecuteInputResult::None); + return Ok(ExecuteInputResult::default()); } }; if config.forced { - let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?; - Ok(ExecuteInputResult::Corpus) + let (_id, result) = fuzzer.add_input(self, executor, manager, input)?; + Ok(result) } else { let (res, _) = fuzzer.evaluate_input(self, executor, manager, &input)?; - if res == ExecuteInputResult::None { + if !(res.is_corpus() || res.is_solution()) { fuzzer.add_disabled_input(self, input)?; log::warn!("input {:?} was not interesting, adding as disabled.", &path); } @@ -748,7 +750,7 @@ where match self.next_file() { Ok(path) => { let res = self.load_file(&path, manager, fuzzer, executor, &mut config)?; - if config.exit_on_solution && matches!(res, ExecuteInputResult::Solution) { + if config.exit_on_solution && res.is_solution() { return Err(Error::invalid_corpus(format!( "Input {} resulted in a solution.", path.display() @@ -1052,11 +1054,11 @@ where for _ in 0..num { let input = generator.generate(self)?; if forced { - let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?; + let (_, _) = fuzzer.add_input(self, executor, manager, input)?; added += 1; } else { let (res, _) = fuzzer.evaluate_input(self, executor, manager, &input)?; - if res != ExecuteInputResult::None { + if res.is_corpus() { added += 1; } }