From 156ed08905369394eed906d7a949797585a47c57 Mon Sep 17 00:00:00 2001 From: Gal Tashma Date: Fri, 4 Jun 2021 15:07:02 +0300 Subject: [PATCH] Eager and Fast FeedbackTuple Implementations (#144) * Introduce eager and fast feedback_or! implementations (issue #135) * rename FeedbackTuple to CombinedFeedback (as it is a struct not tuple) and add fast/eager AND --- libafl/src/feedbacks/mod.rs | 492 ++++++++++++++++++++++++++---------- 1 file changed, 361 insertions(+), 131 deletions(-) diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index b7e001b8f0..2103a894cb 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -108,26 +108,54 @@ where { } -/// Compose [`Feedback`]`s` with an `AND` operation -pub struct AndFeedback +pub struct CombinedFeedback where A: Feedback, B: Feedback, + FL: FeedbackLogic, I: Input, { - /// The first [`Feedback`] to `AND`. pub first: A, - /// The second [`Feedback`] to `AND`. pub second: B, - /// The name name: String, - phantom: PhantomData<(I, S)>, + phantom: PhantomData<(I, S, FL)>, } -impl Feedback for AndFeedback +impl Named for CombinedFeedback where A: Feedback, B: Feedback, + FL: FeedbackLogic, + I: Input, +{ + fn name(&self) -> &str { + self.name.as_ref() + } +} + +impl CombinedFeedback +where + A: Feedback, + B: Feedback, + FL: FeedbackLogic, + I: Input, +{ + pub fn new(first: A, second: B) -> Self { + let name = format!("{} ({},{})", FL::name(), first.name(), second.name()); + Self { + first, + second, + name, + phantom: PhantomData, + } + } +} + +impl Feedback for CombinedFeedback +where + A: Feedback, + B: Feedback, + FL: FeedbackLogic, I: Input, { fn is_interesting( @@ -142,14 +170,15 @@ where EM: EventFirer, OT: ObserversTuple, { - let a = self - .first - .is_interesting(state, manager, input, observers, exit_kind)?; - let b = a - && self - .second - .is_interesting(state, manager, input, observers, exit_kind)?; - Ok(b) + FL::is_pair_interesting( + &mut self.first, + &mut self.second, + state, + manager, + input, + observers, + exit_kind, + ) } #[cfg(feature = "introspection")] @@ -167,27 +196,17 @@ where EM: EventFirer, OT: ObserversTuple, { - // Execute this feedback - let a = self.first.is_interesting_with_perf( + FL::is_pair_interesting_with_perf( + &mut self.first, + &mut self.second, state, manager, input, observers, - &exit_kind, + exit_kind, feedback_stats, feedback_index, - )?; - let b = a - && self.second.is_interesting_with_perf( - state, - manager, - input, - observers, - &exit_kind, - feedback_stats, - feedback_index + 1, - )?; - Ok(b) + ) } #[inline] @@ -203,60 +222,62 @@ where } } -impl Named for AndFeedback +pub trait FeedbackLogic: 'static where A: Feedback, B: Feedback, I: Input, { - #[inline] - fn name(&self) -> &str { - &self.name + fn name() -> &'static str; + + fn is_pair_interesting( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple; + + #[cfg(feature = "introspection")] + fn is_pair_interesting_with_perf( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple; +} + +pub struct LogicEagerOr {} +pub struct LogicFastOr {} +pub struct LogicEagerAnd {} +pub struct LogicFastAnd {} + +impl FeedbackLogic for LogicEagerOr +where + A: Feedback, + B: Feedback, + I: Input, +{ + fn name() -> &'static str { + "Eager OR" } -} -impl AndFeedback -where - A: Feedback, - B: Feedback, - I: Input, -{ - /// Creates a new [`AndFeedback`], resulting in the `AND` of two feedbacks. - pub fn new(first: A, second: B) -> Self { - let name = format!("And({}, {})", first.name(), second.name()); - Self { - first, - second, - name, - phantom: PhantomData, - } - } -} - -/// Compose feedbacks with an OR operation -pub struct OrFeedback -where - A: Feedback, - B: Feedback, - I: Input, -{ - /// The first [`Feedback`] - pub first: A, - /// The second [`Feedback`], `OR`ed with the first. - pub second: B, - /// The name - name: String, - phantom: PhantomData<(I, S)>, -} - -impl Feedback for OrFeedback -where - A: Feedback, - B: Feedback, - I: Input, -{ - fn is_interesting( - &mut self, + fn is_pair_interesting( + first: &mut A, + second: &mut B, state: &mut S, manager: &mut EM, input: &I, @@ -267,19 +288,15 @@ where EM: EventFirer, OT: ObserversTuple, { - let a = self - .first - .is_interesting(state, manager, input, observers, exit_kind)?; - let b = a - || self - .second - .is_interesting(state, manager, input, observers, exit_kind)?; - Ok(b) + let a = first.is_interesting(state, manager, input, observers, exit_kind)?; + let b = second.is_interesting(state, manager, input, observers, exit_kind)?; + Ok(a || b) } #[cfg(feature = "introspection")] - fn is_interesting_with_perf( - &mut self, + fn is_pair_interesting_with_perf( + first: &mut A, + second: &mut B, state: &mut S, manager: &mut EM, input: &I, @@ -293,7 +310,7 @@ where OT: ObserversTuple, { // Execute this feedback - let a = self.first.is_interesting_with_perf( + let a = first.is_interesting_with_perf( state, manager, input, @@ -302,62 +319,254 @@ where feedback_stats, feedback_index, )?; - let b = a - || self.second.is_interesting_with_perf( - state, - manager, - input, - observers, - &exit_kind, - feedback_stats, - feedback_index + 1, - )?; - Ok(b) - } - #[inline] - fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { - self.first.append_metadata(state, testcase)?; - self.second.append_metadata(state, testcase) - } - - #[inline] - fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> { - self.first.discard_metadata(state, input)?; - self.second.discard_metadata(state, input) + let b = second.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index + 1, + )?; + Ok(a || b) } } -impl Named for OrFeedback +impl FeedbackLogic for LogicFastOr where A: Feedback, B: Feedback, I: Input, { - #[inline] - fn name(&self) -> &str { - &self.name + fn name() -> &'static str { + "Fast OR" } -} -impl OrFeedback -where - A: Feedback, - B: Feedback, - I: Input, -{ - /// Creates a new [`OrFeedback`] for two feedbacks. - pub fn new(first: A, second: B) -> Self { - let name = format!("Or({}, {})", first.name(), second.name()); - Self { - first, - second, - name, - phantom: PhantomData, + fn is_pair_interesting( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let a = first.is_interesting(state, manager, input, observers, exit_kind)?; + if a { + return Ok(true); } + + second.is_interesting(state, manager, input, observers, exit_kind) + } + + #[cfg(feature = "introspection")] + fn is_pair_interesting_with_perf( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // Execute this feedback + let a = first.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index, + )?; + + if a { + return Ok(true); + } + + second.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index + 1, + ) } } +impl FeedbackLogic for LogicEagerAnd +where + A: Feedback, + B: Feedback, + I: Input, +{ + fn name() -> &'static str { + "Eager AND" + } + + fn is_pair_interesting( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let a = first.is_interesting(state, manager, input, observers, exit_kind)?; + let b = second.is_interesting(state, manager, input, observers, exit_kind)?; + Ok(a && b) + } + + #[cfg(feature = "introspection")] + fn is_pair_interesting_with_perf( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // Execute this feedback + let a = first.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index, + )?; + + let b = second.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index + 1, + )?; + Ok(a && b) + } +} + +impl FeedbackLogic for LogicFastAnd +where + A: Feedback, + B: Feedback, + I: Input, +{ + fn name() -> &'static str { + "Fast AND" + } + + fn is_pair_interesting( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let a = first.is_interesting(state, manager, input, observers, exit_kind)?; + if a == false { + return Ok(false); + } + + second.is_interesting(state, manager, input, observers, exit_kind) + } + + #[cfg(feature = "introspection")] + fn is_pair_interesting_with_perf( + first: &mut A, + second: &mut B, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // Execute this feedback + let a = first.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index, + )?; + + if a == false { + return Ok(false); + } + + second.is_interesting_with_perf( + state, + manager, + input, + observers, + &exit_kind, + feedback_stats, + feedback_index + 1, + ) + } +} + +/// Combine two feedbacks with an eager AND operation, +/// will call all feedbacks functions even if not necessery to conclude the result +pub type EagerAndFeedback = CombinedFeedback; + +/// Combine two feedbacks with an fast AND operation, +/// might skip calling feedbacks functions if not necessery to conclude the result +pub type FastAndFeedback = CombinedFeedback; + +/// Combine two feedbacks with an eager OR operation, +/// will call all feedbacks functions even if not necessery to conclude the result +pub type EagerOrFeedback = CombinedFeedback; + +/// Combine two feedbacks with an fast OR operation, +/// might skip calling feedbacks functions if not necessery to conclude the result +/// This means any feedback that is not first might be skipped, use caution when using with +/// `TimeFeedback` +pub type FastOrFeedback = CombinedFeedback; + /// Compose feedbacks with an OR operation pub struct NotFeedback where @@ -438,7 +647,18 @@ macro_rules! feedback_and { ( $head:expr, $($tail:expr), +) => { // recursive call - $crate::feedbacks::AndFeedback::new($head , feedback_and!($($tail),+)) + $crate::feedbacks::EagerAndFeedback::new($head , feedback_and!($($tail),+)) + }; +} +/// +/// Variadic macro to create a chain of (fast) AndFeedback +#[macro_export] +macro_rules! feedback_and_fast { + ( $last:expr ) => { $last }; + + ( $head:expr, $($tail:expr), +) => { + // recursive call + $crate::feedbacks::FastAndFeedback::new($head , feedback_and_fast!($($tail),+)) }; } @@ -449,7 +669,17 @@ macro_rules! feedback_or { ( $head:expr, $($tail:expr), +) => { // recursive call - $crate::feedbacks::OrFeedback::new($head , feedback_or!($($tail),+)) + $crate::feedbacks::EagerOrFeedback::new($head , feedback_or!($($tail),+)) + }; +} + +#[macro_export] +macro_rules! feedback_or_fast { + ( $last:expr ) => { $last }; + + ( $head:expr, $($tail:expr), +) => { + // recursive call + $crate::feedbacks::FastOrFeedback::new($head , feedback_or_fast!($($tail),+)) }; }