From ee6385c25b22ec1bfa701b8e892a3a403f5cc229 Mon Sep 17 00:00:00 2001 From: "Dongjia \"toka\" Zhang" Date: Fri, 15 Mar 2024 13:24:26 +0100 Subject: [PATCH] Fire events in append_metadata not in is_interesting (#1936) * stuff * ok * Recalc filled slightly differently... (#1939) * Recalc filled slightly differently... * Make requested changes as per PR review * unused * fix --------- Co-authored-by: Dan Blackwell --- fuzzers/tutorial/src/metadata.rs | 3 +- libafl/src/executors/inprocess/mod.rs | 2 +- libafl/src/feedbacks/concolic.rs | 4 +- libafl/src/feedbacks/map.rs | 141 ++++++++---------- libafl/src/feedbacks/mod.rs | 34 +++-- libafl/src/feedbacks/nautilus.rs | 3 +- libafl/src/fuzzer/mod.rs | 8 +- libafl/src/stages/tmin.rs | 2 +- libafl_frida/src/asan/errors.rs | 3 +- .../libafl_libfuzzer_runtime/src/feedbacks.rs | 3 +- 10 files changed, 106 insertions(+), 97 deletions(-) diff --git a/fuzzers/tutorial/src/metadata.rs b/fuzzers/tutorial/src/metadata.rs index 785a0e701c..3123ddfe94 100644 --- a/fuzzers/tutorial/src/metadata.rs +++ b/fuzzers/tutorial/src/metadata.rs @@ -62,9 +62,10 @@ where } #[inline] - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, _observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> { diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 903a03d372..27295815ed 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -455,7 +455,7 @@ pub fn run_observers_and_save_state( new_testcase.set_parent_id_optional(*state.corpus().current()); fuzzer .objective_mut() - .append_metadata(state, observers, &mut new_testcase) + .append_metadata(state, event_mgr, observers, &mut new_testcase) .expect("Failed adding metadata"); state .solutions_mut() diff --git a/libafl/src/feedbacks/concolic.rs b/libafl/src/feedbacks/concolic.rs index 42f16b433a..a768b4c61e 100644 --- a/libafl/src/feedbacks/concolic.rs +++ b/libafl/src/feedbacks/concolic.rs @@ -67,14 +67,16 @@ where Ok(false) } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { if let Some(metadata) = observers .match_name::(&self.name) diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 6954429384..e7df0a2992 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -318,6 +318,8 @@ where { /// Contains information about untouched entries pub history_map: Vec, + /// Tells us how many non-initial entries there are in `history_map` + pub num_covered_map_indexes: usize, } libafl_bolts::impl_serdeany!( @@ -327,21 +329,29 @@ libafl_bolts::impl_serdeany!( impl MapFeedbackMetadata where - T: Default + Copy + 'static + Serialize + DeserializeOwned, + T: Default + Copy + 'static + Serialize + DeserializeOwned + PartialEq, { /// Create new `MapFeedbackMetadata` #[must_use] pub fn new(map_size: usize) -> Self { Self { history_map: vec![T::default(); map_size], + num_covered_map_indexes: 0, } } /// Create new `MapFeedbackMetadata` using a name and a map. /// The map can be shared. + /// `initial_elem_value` is used to calculate `Self.num_covered_map_indexes` #[must_use] - pub fn with_history_map(history_map: Vec) -> Self { - Self { history_map } + pub fn with_history_map(history_map: Vec, initial_elem_value: T) -> Self { + let num_covered_map_indexes = history_map + .iter() + .fold(0, |acc, x| acc + usize::from(*x != initial_elem_value)); + Self { + history_map, + num_covered_map_indexes, + } } /// Reset the map @@ -350,6 +360,7 @@ where for i in 0..cnt { self.history_map[i] = T::default(); } + self.num_covered_map_indexes = 0; Ok(()) } @@ -359,6 +370,9 @@ where for i in 0..cnt { self.history_map[i] = value; } + // assume that resetting the map should indicate no coverage, + // regardless of value + self.num_covered_map_indexes = 0; Ok(()) } } @@ -366,8 +380,6 @@ where /// The most common AFL-like feedback type #[derive(Clone, Debug)] pub struct MapFeedback { - /// For tracking, always keep indexes and/or novelties, even if the map isn't considered `interesting`. - always_track: bool, /// Indexes used in the last observation indexes: bool, /// New indexes observed in the last observation @@ -418,7 +430,7 @@ where EM: EventFirer, OT: ObserversTuple, { - self.is_interesting_default(state, manager, input, observers, exit_kind) + Ok(self.is_interesting_default(state, manager, input, observers, exit_kind)) } #[rustversion::not(nightly)] @@ -434,17 +446,19 @@ where EM: EventFirer, OT: ObserversTuple, { - self.is_interesting_default(state, manager, input, observers, exit_kind) + Ok(self.is_interesting_default(state, manager, input, observers, exit_kind)) } - fn append_metadata( + fn append_metadata( &mut self, state: &mut S, + manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { if let Some(novelties) = self.novelties.as_mut().map(core::mem::take) { let meta = MapNoveltiesMetadata::new(novelties); @@ -471,6 +485,9 @@ where .enumerate() .filter(|(_, value)| *value != initial) { + if history_map[i] == initial { + map_state.num_covered_map_indexes += 1; + } history_map[i] = R::reduce(history_map[i], value); indices.push(i); } @@ -483,9 +500,43 @@ where .enumerate() .filter(|(_, value)| *value != initial) { + if history_map[i] == initial { + map_state.num_covered_map_indexes += 1; + } history_map[i] = R::reduce(history_map[i], value); } } + + debug_assert!( + history_map + .iter() + .fold(0, |acc, x| acc + usize::from(*x != initial)) + == map_state.num_covered_map_indexes, + "history_map had {} filled, but map_state.num_covered_map_indexes was {}", + history_map + .iter() + .fold(0, |acc, x| acc + usize::from(*x != initial)), + map_state.num_covered_map_indexes, + ); + + // at this point you are executing this code, the testcase is always interesting + let covered = map_state.num_covered_map_indexes; + let len = history_map.len(); + // opt: if not tracking optimisations, we technically don't show the *current* history + // map but the *last* history map; this is better than walking over and allocating + // unnecessarily + manager.fire( + state, + Event::UpdateUserStats { + name: self.stats_name.to_string(), + value: UserStats::new( + UserStatsValue::Ratio(covered as u64, len as u64), + AggregatorOps::Avg, + ), + phantom: PhantomData, + }, + )?; + Ok(()) } } @@ -503,7 +554,7 @@ where fn is_interesting( &mut self, state: &mut S, - manager: &mut EM, + _manager: &mut EM, _input: &S::Input, observers: &OT, _exit_kind: &ExitKind, @@ -604,32 +655,6 @@ where } } - let initial = observer.initial(); - if interesting { - let len = history_map.len(); - let filled = history_map.iter().filter(|&&i| i != initial).count(); - // opt: if not tracking optimisations, we technically don't show the *current* history - // map but the *last* history map; this is better than walking over and allocating - // unnecessarily - manager.fire( - state, - Event::UpdateUserStats { - name: self.stats_name.to_string(), - value: UserStats::new( - UserStatsValue::Ratio( - self.novelties - .as_ref() - .map_or(filled, |novelties| filled + novelties.len()) - as u64, - len as u64, - ), - AggregatorOps::Avg, - ), - phantom: PhantomData, - }, - )?; - } - Ok(interesting) } } @@ -678,7 +703,6 @@ where name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), observer_name: map_observer.name().to_string(), stats_name: create_stats_name(map_observer.name()), - always_track: false, phantom: PhantomData, } } @@ -692,7 +716,6 @@ where name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), observer_name: map_observer.name().to_string(), stats_name: create_stats_name(map_observer.name()), - always_track: false, phantom: PhantomData, } } @@ -707,17 +730,9 @@ where observer_name: observer_name.to_string(), stats_name: create_stats_name(name), phantom: PhantomData, - always_track: false, } } - /// For tracking, enable `always_track` mode, that also adds `novelties` or `indexes`, - /// even if the map is not novel for this feedback. - /// This is useful in combination with `load_initial_inputs_forced`, or other feedbacks. - pub fn set_always_track(&mut self, always_track: bool) { - self.always_track = always_track; - } - /// Creating a new `MapFeedback` with a specific name. This is usefully whenever the same /// feedback is needed twice, but with a different history. Using `new()` always results in the /// same name and therefore also the same history. @@ -729,7 +744,6 @@ where name: name.to_string(), observer_name: map_observer.name().to_string(), stats_name: create_stats_name(name), - always_track: false, phantom: PhantomData, } } @@ -748,7 +762,6 @@ where observer_name: observer_name.to_string(), stats_name: create_stats_name(name), name: name.to_string(), - always_track: false, phantom: PhantomData, } } @@ -759,11 +772,11 @@ where fn is_interesting_default( &mut self, state: &mut S, - manager: &mut EM, + _manager: &mut EM, _input: &S::Input, observers: &OT, _exit_kind: &ExitKind, - ) -> Result + ) -> bool where EM: EventFirer, OT: ObserversTuple, @@ -816,32 +829,7 @@ where } } - if interesting || self.always_track { - let len = history_map.len(); - let filled = history_map.iter().filter(|&&i| i != initial).count(); - // opt: if not tracking optimisations, we technically don't show the *current* history - // map but the *last* history map; this is better than walking over and allocating - // unnecessarily - manager.fire( - state, - Event::UpdateUserStats { - name: self.stats_name.to_string(), - value: UserStats::new( - UserStatsValue::Ratio( - self.novelties - .as_ref() - .map_or(filled, |novelties| filled + novelties.len()) - as u64, - len as u64, - ), - AggregatorOps::Avg, - ), - phantom: PhantomData, - }, - )?; - } - - Ok(interesting) + interesting } } @@ -915,9 +903,10 @@ where } } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, _observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index f61a35b3a1..d030a75b3b 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -109,14 +109,16 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] #[allow(unused_variables)] - fn append_metadata( + fn append_metadata( &mut self, state: &mut S, + manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { Ok(()) } @@ -245,17 +247,21 @@ where } #[inline] - fn append_metadata( + fn append_metadata( &mut self, state: &mut S, + manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { - self.first.append_metadata(state, observers, testcase)?; - self.second.append_metadata(state, observers, testcase) + self.first + .append_metadata(state, manager, observers, testcase)?; + self.second + .append_metadata(state, manager, observers, testcase) } #[inline] @@ -659,16 +665,19 @@ where } #[inline] - fn append_metadata( + fn append_metadata( &mut self, state: &mut S, + manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { - self.first.append_metadata(state, observers, testcase) + self.first + .append_metadata(state, manager, observers, testcase) } #[inline] @@ -915,14 +924,16 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { let observer = observers.match_name::(self.name()).unwrap(); *testcase.exec_time_mut() = *observer.last_runtime(); @@ -1212,9 +1223,10 @@ pub mod pybind { })?) } - fn append_metadata( + fn append_metadata( &mut self, state: &mut PythonStdState, + _manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> @@ -1655,17 +1667,19 @@ pub mod pybind { }) } - fn append_metadata( + fn append_metadata( &mut self, state: &mut PythonStdState, + manager: &mut EM, observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> where OT: ObserversTuple, + EM: EventFirer, { unwrap_me_mut!(self.wrapper, f, { - f.append_metadata(state, observers, testcase) + f.append_metadata(state, manager, observers, testcase) }) } diff --git a/libafl/src/feedbacks/nautilus.rs b/libafl/src/feedbacks/nautilus.rs index 2646487608..bdb919ee22 100644 --- a/libafl/src/feedbacks/nautilus.rs +++ b/libafl/src/feedbacks/nautilus.rs @@ -99,9 +99,10 @@ where Ok(false) } - fn append_metadata( + fn append_metadata( &mut self, state: &mut S, + _manager: &mut EM, _observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 3613e598a5..6f5051de76 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -381,7 +381,7 @@ where // Add the input to the main corpus let mut testcase = Testcase::with_executions(input.clone(), *state.executions()); self.feedback_mut() - .append_metadata(state, observers, &mut testcase)?; + .append_metadata(state, manager, observers, &mut testcase)?; let idx = state.corpus_mut().add(testcase)?; self.scheduler_mut().on_add(state, idx)?; @@ -419,7 +419,7 @@ where let mut testcase = Testcase::with_executions(input, *state.executions()); testcase.set_parent_id_optional(*state.corpus().current()); self.objective_mut() - .append_metadata(state, observers, &mut testcase)?; + .append_metadata(state, manager, observers, &mut testcase)?; state.solutions_mut().add(testcase)?; if send_events { @@ -517,7 +517,7 @@ where if is_solution { self.objective_mut() - .append_metadata(state, observers, &mut testcase)?; + .append_metadata(state, manager, observers, &mut testcase)?; let idx = state.solutions_mut().add(testcase)?; manager.fire( @@ -546,7 +546,7 @@ where // Add the input to the main corpus self.feedback_mut() - .append_metadata(state, observers, &mut testcase)?; + .append_metadata(state, manager, observers, &mut testcase)?; let idx = state.corpus_mut().add(testcase)?; self.scheduler_mut().on_add(state, idx)?; diff --git a/libafl/src/stages/tmin.rs b/libafl/src/stages/tmin.rs index 65594f257d..93851498f6 100644 --- a/libafl/src/stages/tmin.rs +++ b/libafl/src/stages/tmin.rs @@ -166,7 +166,7 @@ where let mut testcase = Testcase::with_executions(base, *state.executions()); fuzzer .feedback_mut() - .append_metadata(state, observers, &mut testcase)?; + .append_metadata(state, manager, observers, &mut testcase)?; let prev = state.corpus_mut().replace(base_corpus_idx, testcase)?; fuzzer .scheduler_mut() diff --git a/libafl_frida/src/asan/errors.rs b/libafl_frida/src/asan/errors.rs index 4fc791b8f9..170d126053 100644 --- a/libafl_frida/src/asan/errors.rs +++ b/libafl_frida/src/asan/errors.rs @@ -651,9 +651,10 @@ where } } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, _observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error> diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs index 9b7d5dbcb1..704d6f1b54 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs @@ -131,9 +131,10 @@ where Ok(false) } - fn append_metadata( + fn append_metadata( &mut self, _state: &mut S, + _manager: &mut EM, _observers: &OT, testcase: &mut Testcase, ) -> Result<(), Error>