diff --git a/fuzzers/libfuzzer_stb_image/Cargo.toml b/fuzzers/libfuzzer_stb_image/Cargo.toml index d7ef610490..ea49909a65 100644 --- a/fuzzers/libfuzzer_stb_image/Cargo.toml +++ b/fuzzers/libfuzzer_stb_image/Cargo.toml @@ -17,8 +17,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } - -libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "sancov_cmplog", "libfuzzer"] } [build-dependencies] cc = { version = "1.0", features = ["parallel"] } diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index c718b1ad2e..14256237a9 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -12,11 +12,11 @@ use libafl::{ events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, - feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, + feedbacks::{CmpFeedback, CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - mutators::token_mutations::I2SRandReplace, + mutators::token_mutations::{I2SRandReplace, Tokens}, observers::{StdMapObserver, TimeObserver}, stages::{StdMutationalStage, TracingStage}, state::{HasCorpus, StdState}, @@ -72,6 +72,9 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); + let cmplog = unsafe { &mut CMPLOG_MAP }; + let cmplog_observer = CmpLogObserver::new("cmplog", cmplog); + let cmplog = unsafe { &mut CMPLOG_MAP }; let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true); diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index e9ae0f0c83..8f9ab3d289 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -361,9 +361,12 @@ where } /// Merge two `TupeList` -pub trait Merge { +pub trait Merge: TupleList +where + T: TupleList, +{ /// The Resulting [`TupleList`], of an [`Merge::merge()`] call - type MergeResult; + type MergeResult: TupleList; /// Merge and return the merged tuple #[must_use] @@ -371,7 +374,10 @@ pub trait Merge { } /// Implement merge for an empty tuple list. -impl Merge for () { +impl Merge for () +where + T: TupleList, +{ type MergeResult = T; fn merge(self, value: T) -> Self::MergeResult { @@ -382,7 +388,10 @@ impl Merge for () { /// Implement merge for non-empty tuple list. impl Merge for (Head, Tail) where + T: TupleList, + Self: TupleList, Tail: Merge, + (Head, Tail::MergeResult): TupleList, { type MergeResult = (Head, Tail::MergeResult); diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index 3fbd4b7681..61e84944a6 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -115,7 +115,7 @@ impl<'a, CM> StdCmpObserver<'a, CM> where CM: CmpMap, { - /// Creates a new [`CmpObserver`] with the given name. + /// Creates a new [`StdCmpObserver`] with the given name. #[must_use] pub fn new(name: &'static str, map: &'a mut CM) -> Self { Self { diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index 4e37dce554..5e9801ded7 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -1,96 +1,163 @@ -use core::{marker::PhantomData, mem::drop}; +use core::marker::PhantomData; use crate::{ + bolts::rands::Rand, corpus::Corpus, - executors::{Executor, HasExecHooksTuple, HasObservers, HasObserversHooks}, + fuzzer::Evaluator, inputs::Input, mark_feature_time, - observers::ObserversTuple, + mutators::Mutator, stages::Stage, start_timer, - state::{HasClientPerfStats, HasCorpus, HasExecutions}, + state::{HasClientPerfStats, HasCorpus, HasRand}, Error, }; #[cfg(feature = "introspection")] use crate::stats::PerfFeature; -/// The default mutational stage -#[derive(Clone, Debug)] -pub struct TracingStage +// TODO multi mutators stage + +/// A Mutational stage is the stage in a fuzzing run that mutates inputs. +/// Mutational stages will usually have a range of mutations that are +/// being applied to the input one by one, between executions. +pub trait TracingStage: Stage where - I: Input, C: Corpus, - TE: Executor + HasObservers + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - S: HasClientPerfStats + HasExecutions + HasCorpus, + M: Mutator, + I: Input, + S: HasClientPerfStats + HasCorpus, + Z: Evaluator, { - tracer_executor: TE, - #[allow(clippy::type_complexity)] - phantom: PhantomData<(C, EM, I, OT, S, TE, Z)>, + /// The mutator registered for this stage + fn mutator(&self) -> &M; + + /// The mutator registered for this stage (mutable) + fn mutator_mut(&mut self) -> &mut M; + + /// Gets the number of iterations this mutator should run for. + fn iterations(&self, state: &mut S) -> usize; + + /// Runs this (mutational) stage for the given testcase + #[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely... + fn perform_mutational( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + let num = self.iterations(state); + + for i in 0..num { + start_timer!(state); + let mut input = state + .corpus() + .get(corpus_idx)? + .borrow_mut() + .load_input()? + .clone(); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + start_timer!(state); + self.mutator_mut().mutate(state, &mut input, i as i32)?; + mark_feature_time!(state, PerfFeature::Mutate); + + // Time is measured directly the `evaluate_input` function + let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input)?; + + start_timer!(state); + self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; + mark_feature_time!(state, PerfFeature::MutatePostExec); + } + Ok(()) + } } -impl Stage for TracingStage +/// Default value, how many iterations each stage gets, as an upper bound +/// It may randomly continue earlier. +pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128; + +/// The default mutational stage +#[derive(Clone, Debug)] +pub struct StdMutationalStage where - I: Input, C: Corpus, - TE: Executor + HasObservers + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - S: HasClientPerfStats + HasExecutions + HasCorpus, + M: Mutator, + I: Input, + R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, +{ + mutator: M, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(C, E, EM, I, R, S, Z)>, +} + +impl MutationalStage + for StdMutationalStage +where + C: Corpus, + M: Mutator, + I: Input, + R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, +{ + /// The mutator, added to this stage + #[inline] + fn mutator(&self) -> &M { + &self.mutator + } + + /// The list of mutators, added to this stage (as mutable ref) + #[inline] + fn mutator_mut(&mut self) -> &mut M { + &mut self.mutator + } + + /// Gets the number of iterations as a random number + fn iterations(&self, state: &mut S) -> usize { + 1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize + } +} + +impl Stage for StdMutationalStage +where + C: Corpus, + M: Mutator, + I: Input, + R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, { #[inline] fn perform( &mut self, fuzzer: &mut Z, - _executor: &mut E, + executor: &mut E, 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); - - start_timer!(state); - self.tracer_executor - .pre_exec_observers(fuzzer, state, manager, &input)?; - mark_feature_time!(state, PerfFeature::PreExecObservers); - - start_timer!(state); - drop( - self.tracer_executor - .run_target(fuzzer, state, manager, &input)?, - ); - mark_feature_time!(state, PerfFeature::TargetExecution); - - *state.executions_mut() += 1; - - start_timer!(state); - self.tracer_executor - .post_exec_observers(fuzzer, state, manager, &input)?; - mark_feature_time!(state, PerfFeature::PostExecObservers); - - Ok(()) + self.perform_mutational(fuzzer, executor, state, manager, corpus_idx) } } -impl TracingStage +impl StdMutationalStage where - I: Input, C: Corpus, - TE: Executor + HasObservers + HasObserversHooks, - OT: ObserversTuple + HasExecHooksTuple, - S: HasClientPerfStats + HasExecutions + HasCorpus, + M: Mutator, + I: Input, + R: Rand, + S: HasClientPerfStats + HasCorpus + HasRand, + Z: Evaluator, { /// Creates a new default mutational stage - pub fn new(tracer_executor: TE) -> Self { + pub fn new(mutator: M) -> Self { Self { - tracer_executor, + mutator, phantom: PhantomData, } } diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs index f74d91ae7c..182ea2539c 100644 --- a/libafl_targets/src/cmplog.rs +++ b/libafl_targets/src/cmplog.rs @@ -2,9 +2,12 @@ //! The values will then be used in subsequent mutations. use libafl::{ - observers::{CmpMap, CmpValues}, + bolts::{ownedref::OwnedRefMut, tuples::Named}, + executors::HasExecHooks, + observers::{CmpMap, CmpObserver, CmpValues, Observer}, Error, }; + use serde::{Deserialize, Serialize}; // TODO compile time flag @@ -140,12 +143,11 @@ pub static mut libafl_cmplog_enabled: u8 = 0; pub use libafl_cmplog_enabled as CMPLOG_ENABLED; -/// A [`CmpObserver`] observer for `CmpLog` +/// A [`CmpObserver`] observer for CmpLog #[derive(Serialize, Deserialize, Debug)] pub struct CmpLogObserver<'a> { map: OwnedRefMut<'a, CmpLogMap>, size: Option>, - add_meta: bool, name: String, } @@ -169,10 +171,7 @@ impl<'a> CmpObserver for CmpLogObserver<'a> { impl<'a> Observer for CmpLogObserver<'a> {} -impl<'a, EM, I, S, Z> HasExecHooks for CmpLogObserver<'a> -where - S: HasMetadata, -{ +impl<'a, EM, I, S, Z> HasExecHooks for CmpLogObserver<'a> { fn pre_exec( &mut self, _fuzzer: &mut Z, @@ -190,16 +189,13 @@ where fn post_exec( &mut self, _fuzzer: &mut Z, - state: &mut S, + _state: &mut S, _mgr: &mut EM, _input: &I, ) -> Result<(), Error> { unsafe { CMPLOG_ENABLED = 0; } - if self.add_meta { - self.add_cmpvalues_meta(state); - } Ok(()) } } @@ -213,11 +209,11 @@ impl<'a> Named for CmpLogObserver<'a> { impl<'a> CmpLogObserver<'a> { /// Creates a new [`CmpLogObserver`] with the given name. #[must_use] - pub fn new(name: &'static str, map: &'a mut CmpLogMap, add_meta: bool) -> Self { + pub fn new(name: &'static str, map: &'a mut CmpLogMap) -> Self { Self { name: name.to_string(), size: None, - add_meta, + map: OwnedRefMut::Ref(map), } } diff --git a/libafl_targets/src/sancov_cmp.c b/libafl_targets/src/sancov_cmp.c index 440f06c4b5..de795b4e45 100644 --- a/libafl_targets/src/sancov_cmp.c +++ b/libafl_targets/src/sancov_cmp.c @@ -33,13 +33,13 @@ void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); -#ifdef SANCOV_VALUE_PROFILE - k &= MAP_SIZE - 1; #ifdef SANCOV_VALUE_PROFILE + k &= MAP_SIZE - 1; __libafl_targets_value_profile1(k, arg1, arg2); #endif #ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; __libafl_targets_cmplog(k, 1, (uint64_t)arg1, (uint64_t)arg2); #endif @@ -50,13 +50,13 @@ void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); -#ifdef SANCOV_VALUE_PROFILE - k &= MAP_SIZE - 1; #ifdef SANCOV_VALUE_PROFILE + k &= MAP_SIZE - 1; __libafl_targets_value_profile2(k, arg1, arg2); #endif #ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; __libafl_targets_cmplog(k, 2, (uint64_t)arg1, (uint64_t)arg2); #endif @@ -67,13 +67,13 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); -#ifdef SANCOV_VALUE_PROFILE - k &= MAP_SIZE - 1; #ifdef SANCOV_VALUE_PROFILE + k &= MAP_SIZE - 1; __libafl_targets_value_profile4(k, arg1, arg2); #endif #ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; __libafl_targets_cmplog(k, 4, (uint64_t)arg1, (uint64_t)arg2); #endif @@ -84,6 +84,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); + #ifdef SANCOV_VALUE_PROFILE k &= MAP_SIZE - 1; __libafl_targets_value_profile8(k, arg1, arg2); @@ -93,13 +94,6 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { __libafl_targets_cmplog(k, 8, (uint64_t)arg1, (uint64_t)arg2); #endif -#ifdef SANCOV_VALUE_PROFILE - __libafl_targets_value_profile8(k, arg1, arg2); -#endif -#ifdef SANCOV_CMPLOG - __libafl_targets_cmplog(k, 8, (uint64_t)arg1, (uint64_t)arg2); -#endif - } void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { @@ -112,9 +106,9 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { uintptr_t k = rt + i; k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; // val , cases[i + 2] #ifdef SANCOV_VALUE_PROFILE + k &= MAP_SIZE - 1; switch (cases[1]) { case 8: __libafl_targets_value_profile1(k, (uint8_t)val, (uint8_t)cases[i + 2]); @@ -131,6 +125,7 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { } #endif #ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; __libafl_targets_cmplog(k, cases[1] / 8, val, cases[i + 2]); #endif