* rerevert

* add comments
This commit is contained in:
Dongjia "toka" Zhang 2025-03-11 19:52:24 +01:00 committed by GitHub
parent ff8a5b3baa
commit 63e82d898e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 137 additions and 119 deletions

View File

@ -312,6 +312,9 @@ impl<EM, H, HB, HT, I, OT, S, Z> HasInProcessHooks<I, S>
#[inline] #[inline]
/// Save state if it is an objective /// 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<E, EM, I, OF, S, Z>( pub fn run_observers_and_save_state<E, EM, I, OF, S, Z>(
executor: &mut E, executor: &mut E,
state: &mut S, state: &mut S,

View File

@ -191,7 +191,7 @@ pub trait Evaluator<E, EM, I, S> {
executor: &mut E, executor: &mut E,
manager: &mut EM, manager: &mut EM,
input: I, input: I,
) -> Result<CorpusId, Error>; ) -> Result<(CorpusId, ExecuteInputResult), Error>;
/// Adds the input to the corpus as a disabled input. /// Adds the input to the corpus as a disabled input.
/// Used during initial corpus loading. /// Used during initial corpus loading.
@ -248,15 +248,44 @@ pub trait Fuzzer<E, EM, I, S, ST> {
) -> Result<CorpusId, Error>; ) -> Result<CorpusId, Error>;
} }
/// The result of harness execution /// The corpus this input should be added to
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Default)]
pub enum ExecuteInputResult { pub struct ExecuteInputResult {
/// No special input is_corpus: bool,
None, is_solution: bool,
/// This input should be stored in the corpus }
Corpus,
/// This input leads to a solution impl ExecuteInputResult {
Solution, /// 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. /// Your default fuzzer instance, for everyday use.
@ -339,7 +368,7 @@ where
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<ExecuteInputResult, Error> { ) -> Result<ExecuteInputResult, Error> {
let mut res = ExecuteInputResult::None; let mut res = ExecuteInputResult::default();
#[cfg(not(feature = "introspection"))] #[cfg(not(feature = "introspection"))]
let is_solution = self let is_solution = self
@ -352,8 +381,9 @@ where
.is_interesting_introspection(state, manager, input, observers, exit_kind)?; .is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if is_solution { if is_solution {
res = ExecuteInputResult::Solution; res.set_is_solution(true);
} else { }
#[cfg(not(feature = "introspection"))] #[cfg(not(feature = "introspection"))]
let corpus_worthy = self let corpus_worthy = self
.feedback_mut() .feedback_mut()
@ -364,8 +394,7 @@ where
.is_interesting_introspection(state, manager, input, observers, exit_kind)?; .is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if corpus_worthy { if corpus_worthy {
res = ExecuteInputResult::Corpus; res.set_is_corpus(true);
}
} }
Ok(res) Ok(res)
@ -373,6 +402,7 @@ where
/// Post process a testcase depending the testcase execution results /// Post process a testcase depending the testcase execution results
/// returns corpus id if it put something into corpus (not solution) /// 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( fn process_execution(
&mut self, &mut self,
state: &mut S, state: &mut S,
@ -381,10 +411,7 @@ where
exec_res: &ExecuteInputResult, exec_res: &ExecuteInputResult,
observers: &OT, observers: &OT,
) -> Result<Option<CorpusId>, Error> { ) -> Result<Option<CorpusId>, Error> {
match exec_res { let corpus = if exec_res.is_corpus() {
ExecuteInputResult::None => Ok(None),
ExecuteInputResult::Corpus => {
// Not a solution
// Add the input to the main corpus // Add the input to the main corpus
let mut testcase = Testcase::from(input.clone()); let mut testcase = Testcase::from(input.clone());
#[cfg(feature = "track_hit_feedbacks")] #[cfg(feature = "track_hit_feedbacks")]
@ -394,10 +421,12 @@ where
.append_metadata(state, manager, observers, &mut testcase)?; .append_metadata(state, manager, observers, &mut testcase)?;
let id = state.corpus_mut().add(testcase)?; let id = state.corpus_mut().add(testcase)?;
self.scheduler_mut().on_add(state, id)?; self.scheduler_mut().on_add(state, id)?;
Ok(Some(id)) Ok(Some(id))
} } else {
ExecuteInputResult::Solution => { Ok(None)
};
if exec_res.is_solution() {
// The input is a solution, add it to the respective corpus // The input is a solution, add it to the respective corpus
let mut testcase = Testcase::from(input.clone()); let mut testcase = Testcase::from(input.clone());
testcase.set_parent_id_optional(*state.corpus().current()); testcase.set_parent_id_optional(*state.corpus().current());
@ -410,10 +439,8 @@ where
self.objective_mut() self.objective_mut()
.append_metadata(state, manager, observers, &mut testcase)?; .append_metadata(state, manager, observers, &mut testcase)?;
state.solutions_mut().add(testcase)?; state.solutions_mut().add(testcase)?;
Ok(None)
}
} }
corpus
} }
fn serialize_and_dispatch( fn serialize_and_dispatch(
@ -426,20 +453,14 @@ where
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Now send off the event // Now send off the event
let observers_buf = match exec_res { let observers_buf = if exec_res.is_corpus()
ExecuteInputResult::Corpus => { && manager.should_send()
if manager.should_send() { && manager.configuration() != EventConfig::AlwaysUnique
{
// TODO set None for fast targets // TODO set None for fast targets
if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(postcard::to_allocvec(observers)?) Some(postcard::to_allocvec(observers)?)
}
} else { } else {
None None
}
}
_ => None,
}; };
self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?; self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?;
@ -456,10 +477,8 @@ where
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Now send off the event // Now send off the event
match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() { if manager.should_send() {
if exec_res.is_corpus() {
manager.fire( manager.fire(
state, state,
Event::NewTestcase { Event::NewTestcase {
@ -475,9 +494,7 @@ where
}, },
)?; )?;
} }
} if exec_res.is_solution() {
ExecuteInputResult::Solution => {
if manager.should_send() {
manager.fire( manager.fire(
state, state,
Event::Objective { Event::Objective {
@ -488,8 +505,7 @@ where
)?; )?;
} }
} }
ExecuteInputResult::None => (),
}
Ok(()) Ok(())
} }
@ -507,7 +523,7 @@ where
if send_events { if send_events {
self.serialize_and_dispatch(state, manager, input, &exec_res, observers, exit_kind)?; 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(); *state.last_found_time_mut() = current_time();
} }
Ok((exec_res, corpus_id)) Ok((exec_res, corpus_id))
@ -614,7 +630,7 @@ where
if self.input_filter.should_execute(input) { if self.input_filter.should_execute(input) {
self.evaluate_input(state, executor, manager, input) self.evaluate_input(state, executor, manager, input)
} else { } 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 /// 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( fn add_input(
&mut self, &mut self,
state: &mut S, state: &mut S,
executor: &mut E, executor: &mut E,
manager: &mut EM, manager: &mut EM,
input: I, input: I,
) -> Result<CorpusId, Error> { ) -> Result<(CorpusId, ExecuteInputResult), Error> {
*state.last_found_time_mut() = current_time(); *state.last_found_time_mut() = current_time();
let exit_kind = self.execute_input(state, executor, manager, &input)?; let exit_kind = self.execute_input(state, executor, manager, &input)?;
@ -667,7 +685,7 @@ where
self.objective_mut() self.objective_mut()
.append_metadata(state, manager, &*observers, &mut testcase)?; .append_metadata(state, manager, &*observers, &mut testcase)?;
// we don't care about solution id // we don't care about solution id
let id = state.solutions_mut().add(testcase)?; let _ = state.solutions_mut().add(testcase.clone())?;
manager.fire( manager.fire(
state, state,
@ -677,22 +695,17 @@ where
time: current_time(), 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 // several is_interesting implementations collect some data about the run, later used in
// append_metadata; we *must* invoke is_interesting here to collect it // append_metadata; we *must* invoke is_interesting here to collect it
#[cfg(not(feature = "introspection"))] #[cfg(not(feature = "introspection"))]
let _is_corpus = let corpus_worthy =
self.feedback_mut() self.feedback_mut()
.is_interesting(state, manager, &input, &*observers, &exit_kind)?; .is_interesting(state, manager, &input, &*observers, &exit_kind)?;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
let _is_corpus = self.feedback_mut().is_interesting_introspection( let corpus_worthy = self.feedback_mut().is_interesting_introspection(
state, state,
manager, manager,
&input, &input,
@ -728,7 +741,7 @@ where
node_id: None, 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<CorpusId, Error> { fn add_disabled_input(&mut self, state: &mut S, input: I) -> Result<CorpusId, Error> {

View File

@ -26,6 +26,8 @@ use serde::{Deserialize, Serialize, de::DeserializeOwned};
mod stack; mod stack;
pub use stack::StageStack; pub use stack::StageStack;
#[cfg(feature = "std")]
use crate::fuzzer::ExecuteInputResult;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
use crate::monitors::stats::ClientPerfStats; use crate::monitors::stats::ClientPerfStats;
use crate::{ use crate::{
@ -33,7 +35,7 @@ use crate::{
corpus::{Corpus, CorpusId, HasCurrentCorpusId, HasTestcase, InMemoryCorpus, Testcase}, corpus::{Corpus, CorpusId, HasCurrentCorpusId, HasTestcase, InMemoryCorpus, Testcase},
events::{Event, EventFirer, LogSeverity}, events::{Event, EventFirer, LogSeverity},
feedbacks::StateInitializer, feedbacks::StateInitializer,
fuzzer::{Evaluator, ExecuteInputResult}, fuzzer::Evaluator,
generators::Generator, generators::Generator,
inputs::{Input, NopInput}, inputs::{Input, NopInput},
stages::StageId, stages::StageId,
@ -715,15 +717,15 @@ where
Ok(input) => input, Ok(input) => input,
Err(err) => { Err(err) => {
log::error!("Skipping input that we could not load from {path:?}: {err:?}"); log::error!("Skipping input that we could not load from {path:?}: {err:?}");
return Ok(ExecuteInputResult::None); return Ok(ExecuteInputResult::default());
} }
}; };
if config.forced { if config.forced {
let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?; let (_id, result) = fuzzer.add_input(self, executor, manager, input)?;
Ok(ExecuteInputResult::Corpus) Ok(result)
} else { } else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, &input)?; 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)?; fuzzer.add_disabled_input(self, input)?;
log::warn!("input {:?} was not interesting, adding as disabled.", &path); log::warn!("input {:?} was not interesting, adding as disabled.", &path);
} }
@ -748,7 +750,7 @@ where
match self.next_file() { match self.next_file() {
Ok(path) => { Ok(path) => {
let res = self.load_file(&path, manager, fuzzer, executor, &mut config)?; 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!( return Err(Error::invalid_corpus(format!(
"Input {} resulted in a solution.", "Input {} resulted in a solution.",
path.display() path.display()
@ -1052,11 +1054,11 @@ where
for _ in 0..num { for _ in 0..num {
let input = generator.generate(self)?; let input = generator.generate(self)?;
if forced { if forced {
let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?; let (_, _) = fuzzer.add_input(self, executor, manager, input)?;
added += 1; added += 1;
} else { } else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, &input)?; let (res, _) = fuzzer.evaluate_input(self, executor, manager, &input)?;
if res != ExecuteInputResult::None { if res.is_corpus() {
added += 1; added += 1;
} }
} }