From d634d018cb4c65da67072be996ac62993d575620 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 4 May 2021 15:05:10 +0200 Subject: [PATCH 1/3] composing feedbacks as logic operations and bump to 0.2 --- fuzzers/baby_fuzzer/Cargo.toml | 2 +- fuzzers/frida_libpng/Cargo.toml | 2 +- fuzzers/libfuzzer_libmozjpeg/Cargo.toml | 2 +- fuzzers/libfuzzer_libpng/Cargo.toml | 2 +- fuzzers/libfuzzer_stb_image/Cargo.toml | 2 +- libafl/Cargo.toml | 2 +- libafl/src/corpus/testcase.rs | 36 ---- libafl/src/events/llmp.rs | 9 +- libafl/src/executors/inprocess.rs | 72 +++---- libafl/src/feedbacks/map.rs | 12 +- libafl/src/feedbacks/mod.rs | 247 ++++++++++++++++++++---- libafl/src/state/mod.rs | 210 ++++++++++---------- libafl_frida/Cargo.toml | 4 +- 13 files changed, 364 insertions(+), 238 deletions(-) diff --git a/fuzzers/baby_fuzzer/Cargo.toml b/fuzzers/baby_fuzzer/Cargo.toml index b464bafe45..cddd5b7b6e 100644 --- a/fuzzers/baby_fuzzer/Cargo.toml +++ b/fuzzers/baby_fuzzer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "baby_fuzzer" -version = "0.1.0" +version = "0.2.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 23d7c9146b..25aedd7c3b 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frida_libpng" -version = "0.1.0" +version = "0.2.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" build = "build.rs" diff --git a/fuzzers/libfuzzer_libmozjpeg/Cargo.toml b/fuzzers/libfuzzer_libmozjpeg/Cargo.toml index 76658ebb41..93d64aa431 100644 --- a/fuzzers/libfuzzer_libmozjpeg/Cargo.toml +++ b/fuzzers/libfuzzer_libmozjpeg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_libmozjpeg" -version = "0.1.0" +version = "0.2.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 589fc6bb6b..e678766469 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_libpng" -version = "0.1.0" +version = "0.2.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" diff --git a/fuzzers/libfuzzer_stb_image/Cargo.toml b/fuzzers/libfuzzer_stb_image/Cargo.toml index 3b3cd143ce..db0a4b5e25 100644 --- a/fuzzers/libfuzzer_stb_image/Cargo.toml +++ b/fuzzers/libfuzzer_stb_image/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_stb_image" -version = "0.1.0" +version = "0.2.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" build = "build.rs" diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 8b2d3cd75f..b0296623b0 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl" -version = "0.1.0" +version = "0.2.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] description = "Slot your own fuzzers together and extend their features using Rust" documentation = "https://docs.rs/libafl" diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index bae9860ed6..8de755ebef 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -23,8 +23,6 @@ where input: Option, /// Filename, if this testcase is backed by a file in the filesystem filename: Option, - /// Accumulated fitness from all the feedbacks - fitness: u32, /// Map of metadata associated with this testcase metadata: SerdeAnyMap, /// Time needed to execute the input @@ -120,24 +118,6 @@ where self.filename = Some(filename); } - /// Get the fitness - #[inline] - pub fn fitness(&self) -> u32 { - self.fitness - } - - /// Get the fitness (mutable) - #[inline] - pub fn fitness_mut(&mut self) -> &mut u32 { - &mut self.fitness - } - - /// Set the fitness - #[inline] - pub fn set_fitness(&mut self, fitness: u32) { - self.fitness = fitness; - } - /// Get the execution time of the testcase pub fn exec_time(&self) -> &Option { &self.exec_time @@ -157,7 +137,6 @@ where Testcase { input: Some(input.into()), filename: None, - fitness: 0, metadata: SerdeAnyMap::new(), exec_time: None, cached_len: None, @@ -170,20 +149,6 @@ where Testcase { input: Some(input), filename: Some(filename), - fitness: 0, - metadata: SerdeAnyMap::new(), - exec_time: None, - cached_len: None, - } - } - - /// Create a new Testcase instace given an input and a fitness - #[inline] - pub fn with_fitness(input: I, fitness: u32) -> Self { - Testcase { - input: Some(input), - filename: None, - fitness, metadata: SerdeAnyMap::new(), exec_time: None, cached_len: None, @@ -195,7 +160,6 @@ where Testcase { input: None, filename: None, - fitness: 0, metadata: SerdeAnyMap::new(), exec_time: None, cached_len: None, diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index bf9b8a1830..a57532866e 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -292,11 +292,10 @@ where let observers: OT = postcard::from_bytes(&observers_buf)?; // TODO include ExitKind in NewTestcase - let fitness = state.is_interesting(&input, &observers, &ExitKind::Ok)?; - if fitness > 0 - && state - .add_if_interesting(&input, fitness, scheduler)? - .is_some() + let is_interesting = state.is_interesting(&input, &observers, &ExitKind::Ok)?; + if state + .add_if_interesting(&input, is_interesting, scheduler)? + .is_some() { #[cfg(feature = "std")] println!("Added received Testcase"); diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 68c1fca89a..4720c9a436 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -19,10 +19,10 @@ use crate::{ executors::{ Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, }, - feedbacks::FeedbacksTuple, + feedbacks::Feedback, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::{HasObjectives, HasSolutions}, + state::{HasObjective, HasSolutions}, Error, }; @@ -158,7 +158,7 @@ where /// * `harness_fn` - the harness, executiong the function /// * `observers` - the observers observing the target during execution /// This may return an error on unix, if signal handler setup fails - pub fn new( + pub fn new( harness_fn: &'a mut H, observers: OT, _state: &mut S, @@ -167,19 +167,19 @@ where where EM: EventManager, OC: Corpus, - OFT: FeedbacksTuple, - S: HasObjectives + HasSolutions, + OF: Feedback, + S: HasObjective + HasSolutions, { #[cfg(unix)] unsafe { let data = &mut unix_signal_handler::GLOBAL_STATE; write_volatile( &mut data.crash_handler, - unix_signal_handler::inproc_crash_handler::, + unix_signal_handler::inproc_crash_handler::, ); write_volatile( &mut data.timeout_handler, - unix_signal_handler::inproc_timeout_handler::, + unix_signal_handler::inproc_timeout_handler::, ); setup_signal_handler(data)?; @@ -190,11 +190,11 @@ where let data = &mut windows_exception_handler::GLOBAL_STATE; write_volatile( &mut data.crash_handler, - windows_exception_handler::inproc_crash_handler::, + windows_exception_handler::inproc_crash_handler::, ); //write_volatile( // &mut data.timeout_handler, - // windows_exception_handler::inproc_timeout_handler::, + // windows_exception_handler::inproc_timeout_handler::, //); setup_exception_handler(data)?; @@ -234,10 +234,10 @@ mod unix_signal_handler { corpus::{Corpus, Testcase}, events::{Event, EventManager}, executors::ExitKind, - feedbacks::FeedbacksTuple, + feedbacks::Feedback, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::{HasObjectives, HasSolutions}, + state::{HasObjective, HasSolutions}, }; // TODO merge GLOBAL_STATE with the Windows one @@ -308,7 +308,7 @@ mod unix_signal_handler { } #[cfg(unix)] - pub unsafe fn inproc_timeout_handler( + pub unsafe fn inproc_timeout_handler( _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t, @@ -317,8 +317,8 @@ mod unix_signal_handler { EM: EventManager, OT: ObserversTuple, OC: Corpus, - OFT: FeedbacksTuple, - S: HasObjectives + HasSolutions, + OF: Feedback, + S: HasObjective + HasSolutions, I: Input + HasTargetBytes, { let state = (data.state_ptr as *mut S).as_mut().unwrap(); @@ -337,11 +337,11 @@ mod unix_signal_handler { let input = (data.current_input_ptr as *const I).as_ref().unwrap(); data.current_input_ptr = ptr::null(); - let obj_fitness = state - .objectives_mut() - .is_interesting_all(&input, observers, &ExitKind::Timeout) - .expect("In timeout handler objectives failure."); - if obj_fitness > 0 { + let interesting = state + .objective_mut() + .is_interesting(&input, observers, &ExitKind::Timeout) + .expect("In timeout handler objective failure."); + if interesting { state .solutions_mut() .add(Testcase::new(input.clone())) @@ -374,7 +374,7 @@ mod unix_signal_handler { /// Will be used for signal handling. /// It will store the current State to shmem, then exit. #[allow(clippy::too_many_lines)] - pub unsafe fn inproc_crash_handler( + pub unsafe fn inproc_crash_handler( _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t, @@ -383,8 +383,8 @@ mod unix_signal_handler { EM: EventManager, OT: ObserversTuple, OC: Corpus, - OFT: FeedbacksTuple, - S: HasObjectives + HasSolutions, + OF: Feedback, + S: HasObjective + HasSolutions, I: Input + HasTargetBytes, { #[cfg(all(target_os = "android", target_arch = "aarch64"))] @@ -446,11 +446,11 @@ mod unix_signal_handler { // Make sure we don't crash in the crash handler forever. data.current_input_ptr = ptr::null(); - let obj_fitness = state - .objectives_mut() - .is_interesting_all(&input, observers, &ExitKind::Crash) - .expect("In crash handler objectives failure."); - if obj_fitness > 0 { + let interesting = state + .objective_mut() + .is_interesting(&input, observers, &ExitKind::Crash) + .expect("In crash handler objective failure."); + if interesting { let new_input = input.clone(); state .solutions_mut() @@ -528,7 +528,7 @@ mod windows_exception_handler { corpus::{Corpus, Testcase}, events::{Event, EventManager}, executors::ExitKind, - feedbacks::FeedbacksTuple, + feedback::Feedback, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, state::{HasObjectives, HasSolutions}, @@ -582,7 +582,7 @@ mod windows_exception_handler { } } - pub unsafe fn inproc_crash_handler( + pub unsafe fn inproc_crash_handler( code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS, data: &mut InProcessExecutorHandlerData, @@ -590,8 +590,8 @@ mod windows_exception_handler { EM: EventManager, OT: ObserversTuple, OC: Corpus, - OFT: FeedbacksTuple, - S: HasObjectives + HasSolutions, + OF: Feedback, + S: HasObjective + HasSolutions, I: Input + HasTargetBytes, { #[cfg(feature = "std")] @@ -610,11 +610,11 @@ mod windows_exception_handler { // Make sure we don't crash in the crash handler forever. data.current_input_ptr = ptr::null(); - let obj_fitness = state - .objectives_mut() - .is_interesting_all(&input, observers, &ExitKind::Crash) - .expect("In crash handler objectives failure."); - if obj_fitness > 0 { + let interesting = state + .objective_mut() + .is_interesting(&input, observers, &ExitKind::Crash) + .expect("In crash handler objective failure."); + if interesting { let new_input = input.clone(); state .solutions_mut() diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 6ac2882f49..340d8a9c87 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -139,11 +139,11 @@ where _input: &I, observers: &OT, _exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple, { - let mut interesting = 0; + let mut interesting = false; // TODO optimize let observer = observers.match_name_type::(&self.name).unwrap(); let size = observer.usable_count(); @@ -157,7 +157,7 @@ where let reduced = R::reduce(history, item); if history != reduced { self.history_map[i] = reduced; - interesting += 1; + interesting = true; } } } else if self.indexes.is_some() && self.novelties.is_none() { @@ -171,7 +171,7 @@ where let reduced = R::reduce(history, item); if history != reduced { self.history_map[i] = reduced; - interesting += 1; + interesting = true; } } } else if self.indexes.is_none() && self.novelties.is_some() { @@ -182,7 +182,7 @@ where let reduced = R::reduce(history, item); if history != reduced { self.history_map[i] = reduced; - interesting += 1; + interesting = true; self.novelties.as_mut().unwrap().push(i); } } @@ -197,7 +197,7 @@ where let reduced = R::reduce(history, item); if history != reduced { self.history_map[i] = reduced; - interesting += 1; + interesting = true; self.novelties.as_mut().unwrap().push(i); } } diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index e4d2f00814..3a5a7d2400 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -15,12 +15,12 @@ use crate::{ Error, }; -use core::time::Duration; +use core::{marker::PhantomData, time::Duration}; /// Feedbacks evaluate the observers. /// Basically, they reduce the information provided by an observer to a value, /// indicating the "interestingness" of the last run. -pub trait Feedback: Named + serde::Serialize + serde::de::DeserializeOwned + 'static +pub trait Feedback: Named + serde::Serialize + serde::de::DeserializeOwned where I: Input, { @@ -30,7 +30,7 @@ where input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple; @@ -47,77 +47,239 @@ where } } -pub trait FeedbacksTuple: serde::Serialize + serde::de::DeserializeOwned +/// Compose feedbacks with an AND operation +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: serde::de::DeserializeOwned")] +pub struct AndFeedback +where + A: Feedback, + B: Feedback, + I: Input, +{ + pub first: A, + pub second: B, + phantom: PhantomData, +} + +impl Feedback for AndFeedback where + A: Feedback, + B: Feedback, I: Input, { - /// Get the total interestingness value from all feedbacks - fn is_interesting_all( + fn is_interesting( &mut self, input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where - OT: ObserversTuple; + OT: ObserversTuple, + { + let a = self.first.is_interesting(input, observers, exit_kind)?; + let b = self.second.is_interesting(input, observers, exit_kind)?; + Ok(a && b) + } +} - /// Write metadata for this testcase - fn append_metadata_all(&mut self, testcase: &mut Testcase) -> Result<(), Error>; +impl Named for AndFeedback +where + A: Feedback, + B: Feedback, + I: Input, +{ + #[inline] + fn name(&self) -> &str { + //format!("And({}, {})", self.first.name(), self.second.name()) + "AndFeedback" + } +} - /// Discards metadata - the end of this input's execution - fn discard_metadata_all(&mut self, input: &I) -> Result<(), Error>; +impl AndFeedback +where + A: Feedback, + B: Feedback, + I: Input, +{ + pub fn new(first: A, second: B) -> Self { + Self { + first, + second, + phantom: PhantomData, + } + } } -impl FeedbacksTuple for () +/// Compose feedbacks with an OR operation +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: serde::de::DeserializeOwned")] +pub struct OrFeedback where + A: Feedback, + B: Feedback, I: Input, { - #[inline] - fn is_interesting_all(&mut self, _: &I, _: &OT, _: &ExitKind) -> Result + pub first: A, + pub second: B, + phantom: PhantomData, +} + +impl Feedback for OrFeedback +where + A: Feedback, + B: Feedback, + I: Input, +{ + fn is_interesting( + &mut self, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result where OT: ObserversTuple, { - Ok(0) + let a = self.first.is_interesting(input, observers, exit_kind)?; + let b = self.second.is_interesting(input, observers, exit_kind)?; + Ok(a || b) } +} +impl Named for OrFeedback +where + A: Feedback, + B: Feedback, + I: Input, +{ #[inline] - fn append_metadata_all(&mut self, _testcase: &mut Testcase) -> Result<(), Error> { - Ok(()) + fn name(&self) -> &str { + //format!("Or({}, {})", self.first.name(), self.second.name()) + "OrFeedback" } +} - #[inline] - fn discard_metadata_all(&mut self, _input: &I) -> Result<(), Error> { - Ok(()) +impl OrFeedback +where + A: Feedback, + B: Feedback, + I: Input, +{ + pub fn new(first: A, second: B) -> Self { + Self { + first, + second, + phantom: PhantomData, + } } } -impl FeedbacksTuple for (Head, Tail) +/// Compose feedbacks with an OR operation +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: serde::de::DeserializeOwned")] +pub struct NotFeedback +where + A: Feedback, + I: Input, +{ + pub first: A, + phantom: PhantomData, +} + +impl Feedback for NotFeedback where - Head: Feedback, - Tail: FeedbacksTuple, + A: Feedback, I: Input, { - fn is_interesting_all( + fn is_interesting( &mut self, input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple, { - Ok(self.0.is_interesting(input, observers, exit_kind)? - + self.1.is_interesting_all(input, observers, exit_kind)?) + Ok(!self.first.is_interesting(input, observers, exit_kind)?) + } +} + +impl Named for NotFeedback +where + A: Feedback, + I: Input, +{ + #[inline] + fn name(&self) -> &str { + //format!("Not({})", self.first.name()) + "NotFeedback" } +} - fn append_metadata_all(&mut self, testcase: &mut Testcase) -> Result<(), Error> { - self.0.append_metadata(testcase)?; - self.1.append_metadata_all(testcase) +impl NotFeedback +where + A: Feedback, + I: Input, +{ + pub fn new(first: A) -> Self { + Self { + first, + phantom: PhantomData, + } } +} + +/// Variadic macro to create a chain of AndFeedback +#[macro_export] +macro_rules! feedback_and { + ( $last:expr ) => { $last }; - fn discard_metadata_all(&mut self, input: &I) -> Result<(), Error> { - self.0.discard_metadata(input)?; - self.1.discard_metadata_all(input) + ( $head:expr, $($tail:expr), +) => { + // recursive call + AndFeedback::new($head , feedback_and!($($tail),+)) + }; +} + +/// Variadic macro to create a chain of OrFeedback +#[macro_export] +macro_rules! feedback_or { + ( $last:expr ) => { $last }; + + ( $head:expr, $($tail:expr), +) => { + // recursive call + OrFeedback::new($head , feedback_or!($($tail),+)) + }; +} + +/// Variadic macro to create a NotFeedback +#[macro_export] +macro_rules! feedback_not { + ( $last:expr ) => { + NotFeedback::new($last) + }; +} + +/// Hack to use () as empty Feedback +impl Feedback for () +where + I: Input, +{ + fn is_interesting( + &mut self, + _input: &I, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + OT: ObserversTuple, + { + Ok(false) + } +} + +impl Named for () { + #[inline] + fn name(&self) -> &str { + "Empty" } } @@ -134,14 +296,14 @@ where _input: &I, _observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple, { if let ExitKind::Crash = exit_kind { - Ok(1) + Ok(true) } else { - Ok(0) + Ok(false) } } } @@ -177,14 +339,14 @@ where _input: &I, _observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple, { if let ExitKind::Timeout = exit_kind { - Ok(1) + Ok(true) } else { - Ok(0) + Ok(false) } } } @@ -209,6 +371,7 @@ impl Default for TimeoutFeedback { } /// Nop feedback that annotates execution time in the new testcase, if any +/// For this Feedback, the testcase is never interesting (use with an OR) #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TimeFeedback { exec_time: Option, @@ -223,13 +386,13 @@ where _input: &I, observers: &OT, _exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple, { let observer = observers.match_first_type::().unwrap(); self.exec_time = *observer.last_runtime(); - Ok(0) + Ok(false) } /// Append to the testcase the generated metadata in case of a new corpus item diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 54a7c1873f..5f2876d45d 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -15,7 +15,7 @@ use crate::{ executors::{ Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, }, - feedbacks::FeedbacksTuple, + feedbacks::Feedback, generators::Generator, inputs::Input, observers::ObserversTuple, @@ -98,30 +98,30 @@ pub trait HasMetadata { } } -/// Trait for elements offering a feedbacks tuple -pub trait HasFeedbacks: Sized +/// Trait for elements offering a feedback +pub trait HasFeedback: Sized where - FT: FeedbacksTuple, + F: Feedback, I: Input, { - /// The feedbacks tuple - fn feedbacks(&self) -> &FT; + /// The feedback + fn feedback(&self) -> &F; - /// The feedbacks tuple (mut) - fn feedbacks_mut(&mut self) -> &mut FT; + /// The feedback (mut) + fn feedback_mut(&mut self) -> &mut F; } -/// Trait for elements offering an objective feedbacks tuple -pub trait HasObjectives: Sized +/// Trait for elements offering an objective feedback tuple +pub trait HasObjective: Sized where - FT: FeedbacksTuple, + OF: Feedback, I: Input, { - /// The objective feedbacks tuple - fn objectives(&self) -> &FT; + /// The objective feedback + fn objective(&self) -> &OF; - /// The objective feedbacks tuple (mut) - fn objectives_mut(&mut self) -> &mut FT; + /// The objective feedback (mut) + fn objective_mut(&mut self) -> &mut OF; } /// Trait for the execution counter @@ -153,7 +153,7 @@ where input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple; @@ -161,7 +161,7 @@ where fn add_if_interesting( &mut self, input: &I, - fitness: u32, + is_interesting: bool, scheduler: &CS, ) -> Result, Error> where @@ -169,7 +169,7 @@ where Self: Sized; } -/// Evaluate an input modyfing the state of the fuzzer and returning a fitness +/// Evaluate an input modyfing the state of the fuzzer pub trait Evaluator: Sized where I: Input, @@ -181,7 +181,7 @@ where executor: &mut E, manager: &mut EM, scheduler: &CS, - ) -> Result<(u32, Option), Error> + ) -> Result<(bool, Option), Error> where E: Executor + HasObservers @@ -194,15 +194,15 @@ where /// The state a fuzz run. #[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(bound = "FT: serde::de::DeserializeOwned")] -pub struct State +#[serde(bound = "F: serde::de::DeserializeOwned")] +pub struct State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// RNG instance rand: R, @@ -213,11 +213,11 @@ where /// The corpus corpus: C, /// Feedbacks used to evaluate an input - feedbacks: FT, + feedback: F, // Solutions corpus solutions: SC, /// Objective Feedbacks - objectives: OFT, + objective: OF, /// Metadata stored for this state by one of the components metadata: SerdeAnyMap, /// MaxSize testcase size for mutators that appreciate it @@ -226,14 +226,14 @@ where phantom: PhantomData, } -impl HasRand for State +impl HasRand for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// The rand instance #[inline] @@ -248,14 +248,14 @@ where } } -impl HasCorpus for State +impl HasCorpus for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// Returns the corpus #[inline] @@ -270,14 +270,14 @@ where } } -impl HasSolutions for State +impl HasSolutions for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// Returns the solutions corpus #[inline] @@ -292,14 +292,14 @@ where } } -impl HasMetadata for State +impl HasMetadata for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// Get all the metadata into an HashMap #[inline] @@ -314,58 +314,58 @@ where } } -impl HasFeedbacks for State +impl HasFeedback for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { - /// The feedbacks tuple + /// The feedback #[inline] - fn feedbacks(&self) -> &FT { - &self.feedbacks + fn feedback(&self) -> &F { + &self.feedback } - /// The feedbacks tuple (mut) + /// The feedback (mut) #[inline] - fn feedbacks_mut(&mut self) -> &mut FT { - &mut self.feedbacks + fn feedback_mut(&mut self) -> &mut F { + &mut self.feedback } } -impl HasObjectives for State +impl HasObjective for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { - /// The objective feedbacks tuple + /// The objective feedback #[inline] - fn objectives(&self) -> &OFT { - &self.objectives + fn objective(&self) -> &OF { + &self.objective } - /// The objective feedbacks tuple (mut) + /// The objective feedback (mut) #[inline] - fn objectives_mut(&mut self) -> &mut OFT { - &mut self.objectives + fn objective_mut(&mut self) -> &mut OF { + &mut self.objective } } -impl HasExecutions for State +impl HasExecutions for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// The executions counter #[inline] @@ -380,14 +380,14 @@ where } } -impl HasMaxSize for State +impl HasMaxSize for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { fn max_size(&self) -> usize { self.max_size @@ -398,14 +398,14 @@ where } } -impl HasStartTime for State +impl HasStartTime for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// The starting time #[inline] @@ -420,14 +420,14 @@ where } } -impl IfInteresting for State +impl IfInteresting for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// Evaluate if a set of observation channels has an interesting state fn is_interesting( @@ -435,12 +435,12 @@ where input: &I, observers: &OT, exit_kind: &ExitKind, - ) -> Result + ) -> Result where OT: ObserversTuple, { - self.feedbacks_mut() - .is_interesting_all(input, observers, exit_kind) + self.feedback_mut() + .is_interesting(input, observers, exit_kind) } /// Adds this input to the corpus, if it's intersting, and return the index @@ -448,33 +448,33 @@ where fn add_if_interesting( &mut self, input: &I, - fitness: u32, + is_interesting: bool, scheduler: &CS, ) -> Result, Error> where CS: CorpusScheduler, { - if fitness > 0 { - let mut testcase = Testcase::with_fitness(input.clone(), fitness); - self.feedbacks_mut().append_metadata_all(&mut testcase)?; + if is_interesting { + let mut testcase = Testcase::new(input.clone()); + self.feedback_mut().append_metadata(&mut testcase)?; let idx = self.corpus.add(testcase)?; scheduler.on_add(self, idx)?; Ok(Some(idx)) } else { - self.feedbacks_mut().discard_metadata_all(&input)?; + self.feedback_mut().discard_metadata(&input)?; Ok(None) } } } -impl Evaluator for State +impl Evaluator for State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// Process one input, adding to the respective corpuses if needed and firing the right events #[inline] @@ -485,7 +485,7 @@ where executor: &mut E, manager: &mut EM, scheduler: &CS, - ) -> Result<(u32, Option), Error> + ) -> Result<(bool, Option), Error> where E: Executor + HasObservers @@ -496,19 +496,19 @@ where EM: EventManager, CS: CorpusScheduler, { - let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; + let (is_interesting, is_solution) = self.execute_input(&input, executor, manager)?; let observers = executor.observers(); if is_solution { // If the input is a solution, add it to the respective corpus let mut testcase = Testcase::new(input.clone()); - self.objectives_mut().append_metadata_all(&mut testcase)?; + self.objective_mut().append_metadata(&mut testcase)?; self.solutions_mut().add(testcase)?; } else { - self.objectives_mut().discard_metadata_all(&input)?; + self.objective_mut().discard_metadata(&input)?; } - let corpus_idx = self.add_if_interesting(&input, fitness, scheduler)?; + let corpus_idx = self.add_if_interesting(&input, is_interesting, scheduler)?; if corpus_idx.is_some() { let observers_buf = manager.serialize_observers(observers)?; manager.fire( @@ -524,18 +524,18 @@ where )?; } - Ok((fitness, corpus_idx)) + Ok((is_interesting, corpus_idx)) } } #[cfg(feature = "std")] -impl State +impl State where C: Corpus, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { pub fn load_from_directory( &mut self, @@ -568,9 +568,10 @@ where println!("Loading file {:?} ...", &path); let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); - let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; + let (is_interesting, is_solution) = + self.execute_input(&input, executor, manager)?; if self - .add_if_interesting(&input, fitness, scheduler)? + .add_if_interesting(&input, is_interesting, scheduler)? .is_none() { println!("File {:?} was not interesting, skipped.", &path); @@ -618,14 +619,14 @@ where } } -impl State +impl State where C: Corpus, I: Input, R: Rand, - FT: FeedbacksTuple, + F: Feedback, SC: Corpus, - OFT: FeedbacksTuple, + OF: Feedback, { /// Runs the input and triggers observers and feedback pub fn execute_input( @@ -633,7 +634,7 @@ where input: &I, executor: &mut E, event_mgr: &mut EM, - ) -> Result<(u32, bool), Error> + ) -> Result<(bool, bool), Error> where E: Executor + HasObservers @@ -653,15 +654,14 @@ where executor.post_exec_observers(self, event_mgr, input)?; let observers = executor.observers(); - let fitness = self - .feedbacks_mut() - .is_interesting_all(&input, observers, &exit_kind)?; + let is_interesting = self + .feedback_mut() + .is_interesting(&input, observers, &exit_kind)?; let is_solution = self - .objectives_mut() - .is_interesting_all(&input, observers, &exit_kind)? - > 0; - Ok((fitness, is_solution)) + .objective_mut() + .is_interesting(&input, observers, &exit_kind)?; + Ok((is_interesting, is_solution)) } pub fn generate_initial_inputs( @@ -686,8 +686,8 @@ where let mut added = 0; for _ in 0..num { let input = generator.generate(self.rand_mut())?; - let (fitness, _) = self.evaluate_input(input, executor, manager, scheduler)?; - if fitness > 0 { + let (is_interesting, _) = self.evaluate_input(input, executor, manager, scheduler)?; + if is_interesting { added += 1; } } @@ -703,16 +703,16 @@ where Ok(()) } - pub fn new(rand: R, corpus: C, feedbacks: FT, solutions: SC, objectives: OFT) -> Self { + pub fn new(rand: R, corpus: C, feedback: F, solutions: SC, objective: OF) -> Self { Self { rand, executions: 0, start_time: Duration::from_millis(0), metadata: SerdeAnyMap::default(), corpus, - feedbacks, + feedback, solutions, - objectives, + objective, max_size: DEFAULT_MAX_SIZE, phantom: PhantomData, } diff --git a/libafl_frida/Cargo.toml b/libafl_frida/Cargo.toml index 15ec573dea..2852fa8210 100644 --- a/libafl_frida/Cargo.toml +++ b/libafl_frida/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_frida" -version = "0.1.0" +version = "0.2.0" authors = ["s1341 "] description = "Frida backend library for LibAFL" documentation = "https://docs.rs/libafl_frida" @@ -13,7 +13,7 @@ edition = "2018" cc = { version = "1.0", features = ["parallel"] } [dependencies] -libafl = { path = "../libafl", version = "0.1.0", features = ["std", "libafl_derive"] } +libafl = { path = "../libafl", version = "0.2.0", features = ["std", "libafl_derive"] } libafl_targets = { path = "../libafl_targets", version = "0.1.0" } nix = "0.20.0" libc = "0.2.92" From 2766d2b2d62bf5180e9b5cd1e41a8165b46754e5 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 4 May 2021 15:12:41 +0200 Subject: [PATCH 2/3] adapt fuzzers and libafl_frida --- fuzzers/baby_fuzzer/src/main.rs | 6 +++--- fuzzers/frida_libpng/Cargo.toml | 2 +- fuzzers/frida_libpng/src/fuzzer.rs | 9 +++------ fuzzers/libfuzzer_libmozjpeg/src/lib.rs | 5 +++-- fuzzers/libfuzzer_libpng/src/lib.rs | 5 +++-- fuzzers/libfuzzer_stb_image/src/main.rs | 7 ++++--- libafl/src/feedbacks/mod.rs | 6 +++--- libafl_frida/src/asan_rt.rs | 8 ++++---- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/fuzzers/baby_fuzzer/src/main.rs b/fuzzers/baby_fuzzer/src/main.rs index 39302c5d4f..e5f026fc42 100644 --- a/fuzzers/baby_fuzzer/src/main.rs +++ b/fuzzers/baby_fuzzer/src/main.rs @@ -56,13 +56,13 @@ pub fn main() { StdRand::with_seed(current_nanos()), // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), - // Feedbacks to rate the interestingness of an input - tuple_list!(MaxMapFeedback::new_with_observer(&observer)), + // Feedback to rate the interestingness of an input + MaxMapFeedback::new_with_observer(&observer), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(), // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new()), + CrashFeedback::new(), ); // Setup a basic mutator with a mutational stage diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 25aedd7c3b..572a42b0bb 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -25,7 +25,7 @@ libafl = { path = "../../libafl/", features = [ "std", "llmp_compression" ] } #, capstone = "0.8.0" frida-gum = { version = "0.4", git = "https://github.com/s1341/frida-rust", features = [ "auto-download", "event-sink", "invocation-listener"] } #frida-gum = { version = "0.4", path = "../../../frida-rust/frida-gum", features = [ "auto-download", "event-sink", "invocation-listener"] } -libafl_frida = { path = "../../libafl_frida", version = "0.1.0" } +libafl_frida = { path = "../../libafl_frida", version = "0.2.0" } lazy_static = "1.4.0" libc = "0.2" libloading = "0.7.0" diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index 89d317ea3d..caeb3e279f 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -12,6 +12,7 @@ use libafl::{ inprocess::InProcessExecutor, timeout::TimeoutExecutor, Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, }, + feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{HasTargetBytes, Input}, @@ -276,17 +277,13 @@ unsafe fn fuzz( // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Feedbacks to rate the interestingness of an input - tuple_list!(MaxMapFeedback::new_with_observer_track( - &edges_observer, - true, - false - )), + MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty)) .unwrap(), // Feedbacks to recognize an input as solution - tuple_list!( + feedback_or!( CrashFeedback::new(), TimeoutFeedback::new(), AsanErrorsFeedback::new() diff --git a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs index 79ad03acb7..dd94e71205 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -8,6 +8,7 @@ use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, + feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, @@ -76,7 +77,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Feedbacks to rate the interestingness of an input - tuple_list!( + feedback_or!( MaxMapFeedback::new_with_observer(&edges_observer), MaxMapFeedback::new_with_observer(&cmps_observer), MaxMapFeedback::new_with_observer(&allocs_observer) @@ -85,7 +86,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new()), + CrashFeedback::new(), ) }); diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 4ddd2b5b70..578c9287ad 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -12,6 +12,7 @@ use libafl::{ }, events::{setup_restarting_mgr_std, EventManager}, executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, @@ -76,7 +77,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Feedbacks to rate the interestingness of an input - tuple_list!( + feedback_or!( MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), TimeFeedback::new() ), @@ -84,7 +85,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), + feedback_or!(CrashFeedback::new(), TimeoutFeedback::new()), ) }); diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index 965a7ed42d..3221034b8b 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -11,6 +11,7 @@ use libafl::{ }, events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, + feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, @@ -73,15 +74,15 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Corpus that will be evolved, we keep it in memory for performance InMemoryCorpus::new(), // Feedbacks to rate the interestingness of an input - tuple_list!( + feedback_or!( MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), TimeFeedback::new() ), // Corpus in which we store solutions (crashes in this example), // on disk so the user can get them after stopping the fuzzer OnDiskCorpus::new(objective_dir).unwrap(), - // Feedbacks to recognize an input as solution - tuple_list!(CrashFeedback::new()), + // Feedback to recognize an input as solution + CrashFeedback::new(), ) }); diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 3a5a7d2400..a50d397534 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -235,7 +235,7 @@ macro_rules! feedback_and { ( $head:expr, $($tail:expr), +) => { // recursive call - AndFeedback::new($head , feedback_and!($($tail),+)) + $crate::feedbacks::AndFeedback::new($head , feedback_and!($($tail),+)) }; } @@ -246,7 +246,7 @@ macro_rules! feedback_or { ( $head:expr, $($tail:expr), +) => { // recursive call - OrFeedback::new($head , feedback_or!($($tail),+)) + $crate::feedbacks::OrFeedback::new($head , feedback_or!($($tail),+)) }; } @@ -254,7 +254,7 @@ macro_rules! feedback_or { #[macro_export] macro_rules! feedback_not { ( $last:expr ) => { - NotFeedback::new($last) + $crate::feedbacks::NotFeedback::new($last) }; } diff --git a/libafl_frida/src/asan_rt.rs b/libafl_frida/src/asan_rt.rs index ea60b06258..5cc7b034ee 100644 --- a/libafl_frida/src/asan_rt.rs +++ b/libafl_frida/src/asan_rt.rs @@ -1687,18 +1687,18 @@ where _input: &I, observers: &OT, _exit_kind: &ExitKind, - ) -> Result { + ) -> Result { let observer = observers .match_first_type::() .expect("An AsanErrorsFeedback needs an AsanErrorsObserver"); match observer.errors() { - None => Ok(0), + None => Ok(false), Some(errors) => { if !errors.errors.is_empty() { self.errors = Some(errors.clone()); - Ok(1) + Ok(true) } else { - Ok(0) + Ok(false) } } } From c517a9900e7f7430b3116a7cb5686a39b2c0ffb8 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Tue, 4 May 2021 15:20:05 +0200 Subject: [PATCH 3/3] fix windows build --- libafl/src/executors/inprocess.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 4720c9a436..8bc152bbb1 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -528,10 +528,10 @@ mod windows_exception_handler { corpus::{Corpus, Testcase}, events::{Event, EventManager}, executors::ExitKind, - feedback::Feedback, + feedbacks::Feedback, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::{HasObjectives, HasSolutions}, + state::{HasObjective, HasSolutions}, }; /// Signal handling on unix systems needs some nasty unsafe.