parent
ff8a5b3baa
commit
63e82d898e
@ -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,
|
||||||
|
@ -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,20 +381,20 @@ 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"))]
|
|
||||||
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 {
|
#[cfg(not(feature = "introspection"))]
|
||||||
res = ExecuteInputResult::Corpus;
|
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)
|
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,39 +411,36 @@ 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),
|
// Add the input to the main corpus
|
||||||
ExecuteInputResult::Corpus => {
|
let mut testcase = Testcase::from(input.clone());
|
||||||
// Not a solution
|
#[cfg(feature = "track_hit_feedbacks")]
|
||||||
// Add the input to the main corpus
|
self.feedback_mut()
|
||||||
let mut testcase = Testcase::from(input.clone());
|
.append_hit_feedbacks(testcase.hit_feedbacks_mut())?;
|
||||||
#[cfg(feature = "track_hit_feedbacks")]
|
self.feedback_mut()
|
||||||
self.feedback_mut()
|
.append_metadata(state, manager, observers, &mut testcase)?;
|
||||||
.append_hit_feedbacks(testcase.hit_feedbacks_mut())?;
|
let id = state.corpus_mut().add(testcase)?;
|
||||||
self.feedback_mut()
|
self.scheduler_mut().on_add(state, id)?;
|
||||||
.append_metadata(state, manager, observers, &mut testcase)?;
|
Ok(Some(id))
|
||||||
let id = state.corpus_mut().add(testcase)?;
|
} else {
|
||||||
self.scheduler_mut().on_add(state, id)?;
|
Ok(None)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Some(id))
|
if exec_res.is_solution() {
|
||||||
}
|
// The input is a solution, add it to the respective corpus
|
||||||
ExecuteInputResult::Solution => {
|
let mut testcase = Testcase::from(input.clone());
|
||||||
// The input is a solution, add it to the respective corpus
|
testcase.set_parent_id_optional(*state.corpus().current());
|
||||||
let mut testcase = Testcase::from(input.clone());
|
if let Ok(mut tc) = state.current_testcase_mut() {
|
||||||
testcase.set_parent_id_optional(*state.corpus().current());
|
tc.found_objective();
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
#[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(
|
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
|
{
|
||||||
if manager.configuration() == EventConfig::AlwaysUnique {
|
// TODO set None for fast targets
|
||||||
None
|
Some(postcard::to_allocvec(observers)?)
|
||||||
} else {
|
} else {
|
||||||
Some(postcard::to_allocvec(observers)?)
|
None
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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,40 +477,35 @@ where
|
|||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Now send off the event
|
// Now send off the event
|
||||||
|
if manager.should_send() {
|
||||||
match exec_res {
|
if exec_res.is_corpus() {
|
||||||
ExecuteInputResult::Corpus => {
|
manager.fire(
|
||||||
if manager.should_send() {
|
state,
|
||||||
manager.fire(
|
Event::NewTestcase {
|
||||||
state,
|
input: input.clone(),
|
||||||
Event::NewTestcase {
|
observers_buf,
|
||||||
input: input.clone(),
|
exit_kind: *exit_kind,
|
||||||
observers_buf,
|
corpus_size: state.corpus().count(),
|
||||||
exit_kind: *exit_kind,
|
client_config: manager.configuration(),
|
||||||
corpus_size: state.corpus().count(),
|
time: current_time(),
|
||||||
client_config: manager.configuration(),
|
forward_id: None,
|
||||||
time: current_time(),
|
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
|
||||||
forward_id: None,
|
node_id: None,
|
||||||
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
|
},
|
||||||
node_id: None,
|
)?;
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ExecuteInputResult::Solution => {
|
if exec_res.is_solution() {
|
||||||
if manager.should_send() {
|
manager.fire(
|
||||||
manager.fire(
|
state,
|
||||||
state,
|
Event::Objective {
|
||||||
Event::Objective {
|
input: self.share_objectives.then_some(input.clone()),
|
||||||
input: self.share_objectives.then_some(input.clone()),
|
objective_size: state.solutions().count(),
|
||||||
objective_size: state.solutions().count(),
|
time: current_time(),
|
||||||
time: current_time(),
|
},
|
||||||
},
|
)?;
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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> {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user