diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs
index c718b1ad2e..16eb76ae28 100644
--- a/fuzzers/libfuzzer_stb_image/src/main.rs
+++ b/fuzzers/libfuzzer_stb_image/src/main.rs
@@ -10,7 +10,7 @@ use libafl::{
QueueCorpusScheduler,
},
events::setup_restarting_mgr_std,
- executors::{inprocess::InProcessExecutor, ExitKind},
+ executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor},
feedback_or,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
@@ -18,7 +18,7 @@ use libafl::{
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::I2SRandReplace,
observers::{StdMapObserver, TimeObserver},
- stages::{StdMutationalStage, TracingStage},
+ stages::{ShadowTracingStage, StdMutationalStage},
state::{HasCorpus, StdState},
stats::MultiStats,
Error,
@@ -123,13 +123,16 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
};
// Create the executor for an in-process function with just one observer for edge coverage
- let mut executor = InProcessExecutor::new(
- &mut harness,
- tuple_list!(edges_observer, time_observer),
- &mut fuzzer,
- &mut state,
- &mut restarting_mgr,
- )?;
+ let mut executor = ShadowExecutor::new(
+ InProcessExecutor::new(
+ &mut harness,
+ tuple_list!(edges_observer, time_observer),
+ &mut fuzzer,
+ &mut state,
+ &mut restarting_mgr,
+ )?,
+ tuple_list!(cmplog_observer),
+ );
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
@@ -151,22 +154,8 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
println!("We imported {} inputs from disk.", state.corpus().count());
}
- // Secondary harness due to mut ownership
- let mut harness = |input: &BytesInput| {
- let target = input.target_bytes();
- let buf = target.as_slice();
- libfuzzer_test_one_input(buf);
- ExitKind::Ok
- };
-
// Setup a tracing stage in which we log comparisons
- let tracing = TracingStage::new(InProcessExecutor::new(
- &mut harness,
- tuple_list!(cmplog_observer),
- &mut fuzzer,
- &mut state,
- &mut restarting_mgr,
- )?);
+ let tracing = ShadowTracingStage::new(&mut executor);
// Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs
index a4ae7b3ccb..9d1b3225e8 100644
--- a/libafl/src/executors/combined.rs
+++ b/libafl/src/executors/combined.rs
@@ -15,7 +15,12 @@ pub struct CombinedExecutor {
impl CombinedExecutor {
/// Create a new `CombinedExecutor`, wrapping the given `executor`s.
- pub fn new(primary: A, secondary: B) -> Self {
+ pub fn new(primary: A, secondary: B) -> Self
+ where
+ A: Executor,
+ B: Executor,
+ I: Input,
+ {
Self { primary, secondary }
}
diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs
index 25bcaf3c89..4dfa0d4cdc 100644
--- a/libafl/src/executors/mod.rs
+++ b/libafl/src/executors/mod.rs
@@ -13,6 +13,9 @@ pub use forkserver::{Forkserver, ForkserverExecutor, OutFile, TimeoutForkserverE
pub mod combined;
pub use combined::CombinedExecutor;
+pub mod shadow;
+pub use shadow::{HasShadowObserverHooks, ShadowExecutor};
+
use crate::{
bolts::serdeany::SerdeAny,
inputs::{HasTargetBytes, Input},
diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs
new file mode 100644
index 0000000000..2f15dc1888
--- /dev/null
+++ b/libafl/src/executors/shadow.rs
@@ -0,0 +1,186 @@
+//! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager
+
+use crate::{
+ executors::{Executor, ExitKind, HasExecHooksTuple, HasObservers, HasObserversHooks},
+ inputs::Input,
+ observers::ObserversTuple,
+ Error,
+};
+
+pub trait HasShadowObserverHooks {
+ /// Run the pre exec hook for all the shadow [`crate::observers::Observer`]`s`
+ fn pre_exec_shadow_observers(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result<(), Error>;
+
+ /// Run the post exec hook for all the shadow [`crate::observers::Observer`]`s`
+ fn post_exec_shadow_observers(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result<(), Error>;
+}
+
+/// A [`ShadowExecutor`] wraps an executor and a set of shadow observers
+pub struct ShadowExecutor {
+ executor: E,
+ shadow_observers: SOT,
+ // Enable the execution of the shadow observers hooks with the regular observers hooks
+ shadow_hooks: bool,
+}
+
+impl ShadowExecutor
+where
+ SOT: ObserversTuple,
+{
+ /// Create a new `ShadowExecutor`, wrapping the given `executor`.
+ pub fn new(executor: E, shadow_observers: SOT) -> Self {
+ Self {
+ executor,
+ shadow_observers,
+ shadow_hooks: false,
+ }
+ }
+
+ /// Create a new `ShadowExecutor`, wrapping the given `executor`.
+ pub fn with_shadow_hooks(
+ executor: E,
+ shadow_observers: SOT,
+ shadow_hooks: bool,
+ ) -> Self {
+ Self {
+ executor,
+ shadow_observers,
+ shadow_hooks,
+ }
+ }
+
+ #[inline]
+ pub fn shadow_observers(&self) -> &SOT {
+ &self.shadow_observers
+ }
+
+ #[inline]
+ pub fn shadow_observers_mut(&mut self) -> &mut SOT {
+ &mut self.shadow_observers
+ }
+
+ pub fn shadow_hooks(&self) -> &bool {
+ &self.shadow_hooks
+ }
+
+ pub fn shadow_hooks_mut(&mut self) -> &mut bool {
+ &mut self.shadow_hooks
+ }
+}
+
+impl HasShadowObserverHooks for ShadowExecutor
+where
+ I: Input,
+ SOT: ObserversTuple + HasExecHooksTuple,
+{
+ #[inline]
+ fn pre_exec_shadow_observers(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result<(), Error> {
+ self.shadow_observers
+ .pre_exec_all(fuzzer, state, mgr, input)
+ }
+
+ #[inline]
+ fn post_exec_shadow_observers(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result<(), Error> {
+ self.shadow_observers
+ .post_exec_all(fuzzer, state, mgr, input)
+ }
+}
+
+impl Executor for ShadowExecutor
+where
+ E: Executor,
+ I: Input,
+ SOT: ObserversTuple,
+{
+ fn run_target(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result {
+ self.executor.run_target(fuzzer, state, mgr, input)
+ }
+}
+
+impl HasObservers for ShadowExecutor
+where
+ E: HasObservers,
+ OT: ObserversTuple,
+ SOT: ObserversTuple,
+{
+ #[inline]
+ fn observers(&self) -> &OT {
+ self.executor.observers()
+ }
+
+ #[inline]
+ fn observers_mut(&mut self) -> &mut OT {
+ self.executor.observers_mut()
+ }
+}
+
+impl HasObserversHooks for ShadowExecutor
+where
+ E: HasObservers,
+ I: Input,
+ OT: ObserversTuple + HasExecHooksTuple,
+ SOT: ObserversTuple + HasExecHooksTuple,
+{
+ /// Run the pre exec hook for all [`crate::observers::Observer`]`s` linked to this [`Executor`].
+ #[inline]
+ fn pre_exec_observers(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result<(), Error> {
+ if self.shadow_hooks {
+ self.shadow_observers
+ .pre_exec_all(fuzzer, state, mgr, input)?;
+ }
+ self.observers_mut().pre_exec_all(fuzzer, state, mgr, input)
+ }
+
+ /// Run the post exec hook for all the [`crate::observers::Observer`]`s` linked to this [`Executor`].
+ #[inline]
+ fn post_exec_observers(
+ &mut self,
+ fuzzer: &mut Z,
+ state: &mut S,
+ mgr: &mut EM,
+ input: &I,
+ ) -> Result<(), Error> {
+ if self.shadow_hooks {
+ self.shadow_observers
+ .post_exec_all(fuzzer, state, mgr, input)?;
+ }
+ self.observers_mut()
+ .post_exec_all(fuzzer, state, mgr, input)
+ }
+}
diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs
index a24b3fb924..300441cdfa 100644
--- a/libafl/src/fuzzer.rs
+++ b/libafl/src/fuzzer.rs
@@ -106,6 +106,18 @@ pub trait Evaluator {
manager: &mut EM,
input: I,
) -> Result<(bool, Option), Error>;
+
+ /// Runs the input and triggers observers and feedback.
+ /// Adds an input, to the corpus even if it's not considered `interesting` by the `feedback`.
+ /// Returns the `index` of the new testcase in the corpus.
+ /// Usually, you want to use [`Evaluator::evaluate_input`], unless you know what you are doing.
+ fn add_input(
+ &mut self,
+ state: &mut S,
+ executor: &mut E,
+ manager: &mut EM,
+ input: I,
+ ) -> Result;
}
/// The main fuzzer trait.
@@ -391,6 +403,42 @@ where
}
}
}
+
+ /// Adds an input, even if it's not conisered `interesting` by any of the executors
+ fn add_input(
+ &mut self,
+ state: &mut S,
+ executor: &mut E,
+ manager: &mut EM,
+ input: I,
+ ) -> Result {
+ let _ = self.execute_input(state, executor, manager, &input)?;
+ let observers = executor.observers();
+ // Always consider this to be "interesting"
+
+ // Not a solution
+ self.objective_mut().discard_metadata(state, &input)?;
+
+ // Add the input to the main corpus
+ let mut testcase = Testcase::new(input.clone());
+ self.feedback_mut().append_metadata(state, &mut testcase)?;
+ let idx = state.corpus_mut().add(testcase)?;
+ self.scheduler_mut().on_add(state, idx)?;
+
+ let observers_buf = manager.serialize_observers(observers)?;
+ manager.fire(
+ state,
+ Event::NewTestcase {
+ input,
+ observers_buf,
+ corpus_size: state.corpus().count(),
+ client_config: "TODO".into(),
+ time: current_time(),
+ executions: *state.executions(),
+ },
+ )?;
+ Ok(idx)
+ }
}
impl Fuzzer
diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs
index a9eebc8316..5042d8e680 100644
--- a/libafl/src/stages/mod.rs
+++ b/libafl/src/stages/mod.rs
@@ -9,7 +9,7 @@ pub mod mutational;
pub use mutational::{MutationalStage, StdMutationalStage};
pub mod tracing;
-pub use tracing::TracingStage;
+pub use tracing::{ShadowTracingStage, TracingStage};
//pub mod power;
//pub use power::PowerMutationalStage;
diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs
index 4e37dce554..1a564c116e 100644
--- a/libafl/src/stages/tracing.rs
+++ b/libafl/src/stages/tracing.rs
@@ -2,7 +2,7 @@ use core::{marker::PhantomData, mem::drop};
use crate::{
corpus::Corpus,
- executors::{Executor, HasExecHooksTuple, HasObservers, HasObserversHooks},
+ executors::{Executor, HasExecHooksTuple, HasObservers, HasObserversHooks, ShadowExecutor},
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
@@ -15,7 +15,7 @@ use crate::{
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
-/// The default mutational stage
+/// A stage that runs a tracer executor
#[derive(Clone, Debug)]
pub struct TracingStage
where
@@ -87,7 +87,7 @@ where
OT: ObserversTuple + HasExecHooksTuple,
S: HasClientPerfStats + HasExecutions + HasCorpus,
{
- /// Creates a new default mutational stage
+ /// Creates a new default stage
pub fn new(tracer_executor: TE) -> Self {
Self {
tracer_executor,
@@ -95,3 +95,78 @@ where
}
}
}
+
+/// A stage that runs the shadow executor using also the shadow observers
+#[derive(Clone, Debug)]
+pub struct ShadowTracingStage {
+ #[allow(clippy::type_complexity)]
+ phantom: PhantomData<(C, E, EM, I, OT, S, SOT, Z)>,
+}
+
+impl Stage, EM, S, Z>
+ for ShadowTracingStage
+where
+ I: Input,
+ C: Corpus,
+ E: Executor + HasObservers + HasObserversHooks,
+ OT: ObserversTuple + HasExecHooksTuple,
+ SOT: ObserversTuple + HasExecHooksTuple,
+ S: HasClientPerfStats + HasExecutions + HasCorpus,
+{
+ #[inline]
+ fn perform(
+ &mut self,
+ fuzzer: &mut Z,
+ executor: &mut ShadowExecutor,
+ state: &mut S,
+ manager: &mut EM,
+ corpus_idx: usize,
+ ) -> Result<(), Error> {
+ start_timer!(state);
+ let input = state
+ .corpus()
+ .get(corpus_idx)?
+ .borrow_mut()
+ .load_input()?
+ .clone();
+ mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
+
+ let prev_shadow_hooks = *executor.shadow_hooks();
+ *executor.shadow_hooks_mut() = true;
+
+ start_timer!(state);
+ executor.pre_exec_observers(fuzzer, state, manager, &input)?;
+ mark_feature_time!(state, PerfFeature::PreExecObservers);
+
+ start_timer!(state);
+ drop(executor.run_target(fuzzer, state, manager, &input)?);
+ mark_feature_time!(state, PerfFeature::TargetExecution);
+
+ *state.executions_mut() += 1;
+
+ start_timer!(state);
+ executor.post_exec_observers(fuzzer, state, manager, &input)?;
+ mark_feature_time!(state, PerfFeature::PostExecObservers);
+
+ *executor.shadow_hooks_mut() = prev_shadow_hooks;
+
+ Ok(())
+ }
+}
+
+impl ShadowTracingStage
+where
+ I: Input,
+ C: Corpus,
+ E: Executor + HasObservers + HasObserversHooks,
+ OT: ObserversTuple + HasExecHooksTuple,
+ SOT: ObserversTuple + HasExecHooksTuple,
+ S: HasClientPerfStats + HasExecutions + HasCorpus,
+{
+ /// Creates a new default stage
+ pub fn new(_executor: &mut ShadowExecutor) -> Self {
+ Self {
+ phantom: PhantomData,
+ }
+ }
+}
diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs
index 4b7d791df5..5a387fd17f 100644
--- a/libafl/src/state/mod.rs
+++ b/libafl/src/state/mod.rs
@@ -355,12 +355,15 @@ where
SC: Corpus,
{
/// loads inputs from a directory
+ /// If `forced` is `true`, the value will be loaded,
+ /// even if it's not considered to be `interesting`.
fn load_from_directory(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dir: &Path,
+ forced: bool,
) -> Result<(), Error>
where
Z: Evaluator,
@@ -379,18 +382,69 @@ where
if attr.is_file() && attr.len() > 0 {
println!("Loading file {:?} ...", &path);
let input = I::from_file(&path)?;
- let (is_interesting, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
- if !is_interesting {
- println!("File {:?} was not interesting, skipped.", &path);
+ if forced {
+ let _ = fuzzer.add_input(self, executor, manager, input)?;
+ } else {
+ let (is_interesting, _) =
+ fuzzer.evaluate_input(self, executor, manager, input)?;
+ if !is_interesting {
+ println!("File {:?} was not interesting, skipped.", &path);
+ }
}
} else if attr.is_dir() {
- self.load_from_directory(fuzzer, executor, manager, &path)?;
+ self.load_from_directory(fuzzer, executor, manager, &path, forced)?;
}
}
Ok(())
}
+ /// Loads initial inputs from the passed-in `in_dirs`.
+ /// If `forced` is true, will add all testcases, no matter what.
+ fn load_initial_inputs_internal(
+ &mut self,
+ fuzzer: &mut Z,
+ executor: &mut E,
+ manager: &mut EM,
+ in_dirs: &[PathBuf],
+ forced: bool,
+ ) -> Result<(), Error>
+ where
+ Z: Evaluator,
+ EM: EventManager,
+ {
+ for in_dir in in_dirs {
+ self.load_from_directory(fuzzer, executor, manager, in_dir, forced)?;
+ }
+ manager.fire(
+ self,
+ Event::Log {
+ severity_level: LogSeverity::Debug,
+ message: format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count
+ phantom: PhantomData,
+ },
+ )?;
+ manager.process(fuzzer, self, executor)?;
+ Ok(())
+ }
+
+ /// Loads all intial inputs, even if they are not consiered `intesting`.
+ /// This is rarely the right method, use `load_initial_inputs`,
+ /// and potentially fix your `Feedback`, instead.
+ pub fn load_initial_inputs_forced(
+ &mut self,
+ fuzzer: &mut Z,
+ executor: &mut E,
+ manager: &mut EM,
+ in_dirs: &[PathBuf],
+ ) -> Result<(), Error>
+ where
+ Z: Evaluator,
+ EM: EventManager,
+ {
+ self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, true)
+ }
+
/// Loads initial inputs from the passed-in `in_dirs`.
pub fn load_initial_inputs(
&mut self,
@@ -403,19 +457,7 @@ where
Z: Evaluator,
EM: EventManager,
{
- for in_dir in in_dirs {
- self.load_from_directory(fuzzer, executor, manager, in_dir)?;
- }
- manager.fire(
- self,
- Event::Log {
- severity_level: LogSeverity::Debug,
- message: format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count
- phantom: PhantomData,
- },
- )?;
- manager.process(fuzzer, self, executor)?;
- Ok(())
+ self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, false)
}
}
diff --git a/libafl_frida/src/lib.rs b/libafl_frida/src/lib.rs
index 8fcdc4745b..23a8978911 100644
--- a/libafl_frida/src/lib.rs
+++ b/libafl_frida/src/lib.rs
@@ -109,7 +109,6 @@ impl FridaOptions {
}
"cmplog" => {
options.enable_cmplog = value.parse().unwrap();
-
#[cfg(not(target_arch = "aarch64"))]
if options.enable_cmplog {
panic!(