* 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]
/// 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>(
executor: &mut E,
state: &mut S,

View File

@ -191,7 +191,7 @@ pub trait Evaluator<E, EM, I, S> {
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<CorpusId, Error>;
) -> 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<E, EM, I, S, ST> {
) -> Result<CorpusId, Error>;
}
/// 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<ExecuteInputResult, Error> {
let mut res = ExecuteInputResult::None;
let mut res = ExecuteInputResult::default();
#[cfg(not(feature = "introspection"))]
let is_solution = self
@ -352,8 +381,9 @@ where
.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if is_solution {
res = ExecuteInputResult::Solution;
} else {
res.set_is_solution(true);
}
#[cfg(not(feature = "introspection"))]
let corpus_worthy = self
.feedback_mut()
@ -364,8 +394,7 @@ where
.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if corpus_worthy {
res = ExecuteInputResult::Corpus;
}
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,10 +411,7 @@ where
exec_res: &ExecuteInputResult,
observers: &OT,
) -> Result<Option<CorpusId>, Error> {
match exec_res {
ExecuteInputResult::None => Ok(None),
ExecuteInputResult::Corpus => {
// Not a solution
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")]
@ -394,10 +421,12 @@ where
.append_metadata(state, manager, observers, &mut testcase)?;
let id = state.corpus_mut().add(testcase)?;
self.scheduler_mut().on_add(state, id)?;
Ok(Some(id))
}
ExecuteInputResult::Solution => {
} else {
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());
@ -410,10 +439,8 @@ where
self.objective_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
state.solutions_mut().add(testcase)?;
Ok(None)
}
}
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() {
let observers_buf = if exec_res.is_corpus()
&& manager.should_send()
&& manager.configuration() != EventConfig::AlwaysUnique
{
// TODO set None for fast targets
if manager.configuration() == EventConfig::AlwaysUnique {
None
} else {
Some(postcard::to_allocvec(observers)?)
}
} else {
None
}
}
_ => None,
};
self.dispatch_event(state, manager, input, exec_res, observers_buf, exit_kind)?;
@ -456,10 +477,8 @@ where
exit_kind: &ExitKind,
) -> Result<(), Error> {
// Now send off the event
match exec_res {
ExecuteInputResult::Corpus => {
if manager.should_send() {
if exec_res.is_corpus() {
manager.fire(
state,
Event::NewTestcase {
@ -475,9 +494,7 @@ where
},
)?;
}
}
ExecuteInputResult::Solution => {
if manager.should_send() {
if exec_res.is_solution() {
manager.fire(
state,
Event::Objective {
@ -488,8 +505,7 @@ where
)?;
}
}
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<CorpusId, Error> {
) -> 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<CorpusId, Error> {

View File

@ -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;
}
}