From a939f052d3b72a0e664ace3e54804e7bb307b292 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Fri, 19 Feb 2021 15:46:39 +0100 Subject: [PATCH] fuzzer arg missing atm --- libafl/src/corpus/mod.rs | 55 +- libafl/src/corpus/testcase.rs | 12 + libafl/src/events/llmp.rs | 109 ++-- libafl/src/events/logger.rs | 42 +- libafl/src/events/mod.rs | 68 +-- libafl/src/executors/inprocess.rs | 97 +--- libafl/src/executors/mod.rs | 22 +- libafl/src/fuzzer.rs | 8 +- libafl/src/lib.rs | 2 + libafl/src/mutators/scheduled.rs | 5 +- libafl/src/mutators/token_mutations.rs | 2 +- libafl/src/stages/mutational.rs | 8 +- libafl/src/state/mod.rs | 685 +++++++++++++++---------- 13 files changed, 530 insertions(+), 585 deletions(-) diff --git a/libafl/src/corpus/mod.rs b/libafl/src/corpus/mod.rs index 3ac1b49265..126c2b2898 100644 --- a/libafl/src/corpus/mod.rs +++ b/libafl/src/corpus/mod.rs @@ -5,6 +5,7 @@ pub use testcase::Testcase; use alloc::{vec::Vec}; use core::{cell::RefCell}; +use serde::{Serialize, Deserialize}; use crate::{ inputs::Input, @@ -101,6 +102,8 @@ impl CorpusScheduler for RandCorpusScheduler { } } +#[derive(Default, Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "I: serde::de::DeserializeOwned")] pub struct InMemoryCorpus where I: Input, @@ -108,7 +111,7 @@ where entries: Vec>>, } -impl Corpus for InMemoryCorpus +impl Corpus for InMemoryCorpus where I: Input, { @@ -151,54 +154,4 @@ where fn get(&self, idx: usize) -> Result<&RefCell>, Error> { Ok(&self.entries[idx]) } - - /*/// Add an entry to the corpus and return its index - #[inline] - fn add(state: &mut S, testcase: Testcase) -> Result - where - S: HasCorpus + HasRand, - R: Rand - { - state.corpus_mut().entries.push(RefCell::new(testcase)); - let idx = state.corpus().entries.len() - 1; - // Scheduler hook - SC::on_add(state, idx, state.corpus().entries[idx].borrow())?; - Ok(idx) - } - - /// Replaces the testcase at the given idx - #[inline] - fn replace(state: &mut S, idx: usize, testcase: Testcase) -> Result<(), Error> - where - S: HasCorpus + HasRand, - R: Rand - { - if state.corpus().entries.len() < idx { - return Err(Error::KeyNotFound(format!("Index {} out of bounds", idx))); - } - state.corpus_mut().entries[idx] = RefCell::new(testcase); - // Scheduler hook - SC::on_replace(state, idx, state.corpus().entries[idx])?; - Ok(()) - } - - /// Removes an entry from the corpus, returning it if it was present. - #[inline] - fn remove(state: &mut S, idx: usize) -> Result>, Error> - where - S: HasCorpus + HasRand, - R: Rand - { - let testcase = match state.corpus_mut() - .entries - .iter() - .position(|x| ptr::eq(x.as_ptr(), entry)) - { - Some(i) => Some(state.corpus_mut().entries.remove(i).into_inner()), - None => None, - }; - // Scheduler hook - SC::on_remove(state, idx, &testcase)?; - Ok(testcase) - }*/ } diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index d43812d8b3..984f5530e2 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -159,6 +159,18 @@ where } } + /// 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.into()), + filename: None, + fitness: fitness, + metadatas: SerdeAnyMap::new(), + exec_time: None, + } + } + #[inline] pub fn default() -> Self { Testcase { diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 867eaadb31..fe46d05054 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -1,6 +1,7 @@ use crate::bolts::llmp::LlmpSender; use alloc::{string::ToString, vec::Vec}; use core::{marker::PhantomData, time::Duration}; +use serde::{Serialize, de::DeserializeOwned}; #[cfg(feature = "std")] use crate::bolts::llmp::LlmpReceiver; @@ -16,16 +17,13 @@ use crate::{ llmp::{self, LlmpClient, LlmpClientDescription, Tag}, shmem::ShMem, }, - corpus::Corpus, events::{BrokerEventResult, Event, EventManager}, executors::ExitKind, executors::{Executor, HasObservers}, - feedbacks::FeedbacksTuple, inputs::Input, observers::ObserversTuple, - state::State, + state::{IfInteresting}, stats::Stats, - utils::Rand, Error, }; @@ -250,21 +248,17 @@ where } // Handle arriving events in the client - fn handle_in_client( + fn handle_in_client( &mut self, - state: &mut State, + state: &mut S, sender_id: u32, event: Event, _executor: &mut E, ) -> Result<(), Error> where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, + S: IfInteresting { match event { Event::NewTestcase { @@ -284,7 +278,7 @@ where // TODO include ExitKind in NewTestcase let fitness = state.is_interesting(&input, &observers, ExitKind::Ok)?; if fitness > 0 { - if !state.add_if_interesting(input, fitness)?.is_none() { + if !state.add_if_interesting(&input, fitness)?.is_none() { #[cfg(feature = "std")] println!("Added received Testcase"); } @@ -317,19 +311,15 @@ where } } - fn process( + fn process( &mut self, - state: &mut State, + state: &mut S, executor: &mut E, ) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, + S: IfInteresting { // TODO: Get around local event copy by moving handle_in_client let mut events = vec![]; @@ -358,19 +348,11 @@ where Ok(count) } - fn fire( + fn fire( &mut self, - _state: &mut State, + _state: &mut S, event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - I: Input, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + ) -> Result<(), Error> { let serialized = postcard::to_allocvec(&event)?; self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; Ok(()) @@ -380,17 +362,13 @@ where /// Serialize the current state and corpus during an executiont to bytes. /// On top, add the current llmp event manager instance to be restored /// This method is needed when the fuzzer run crashes and has to restart. -pub fn serialize_state_mgr( - state: &State, +pub fn serialize_state_mgr( + state: &S, mgr: &LlmpEventManager, ) -> Result, Error> where - C: Corpus, - FT: FeedbacksTuple, I: Input, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: Serialize, SH: ShMem, ST: Stats, { @@ -398,20 +376,16 @@ where } /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` -pub fn deserialize_state_mgr( +pub fn deserialize_state_mgr( state_corpus_serialized: &[u8], -) -> Result<(State, LlmpEventManager), Error> +) -> Result<(S, LlmpEventManager), Error> where - C: Corpus, - FT: FeedbacksTuple, I: Input, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: DeserializeOwned, SH: ShMem, ST: Stats, { - let tuple: (State, _) = postcard::from_bytes(&state_corpus_serialized)?; + let tuple: (S, _) = postcard::from_bytes(&state_corpus_serialized)?; Ok(( tuple.0, LlmpEventManager::existing_client_from_description(&tuple.1)?, @@ -447,16 +421,12 @@ where } /// Reset the single page (we reuse it over and over from pos 0), then send the current state to the next runner. - fn on_restart( + fn on_restart( &mut self, - state: &mut State, + state: &mut S, ) -> Result<(), Error> where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: Serialize, { // First, reset the page to 0 so the next iteration can read read from the beginning of this page unsafe { self.sender.reset() }; @@ -465,35 +435,24 @@ where .send_buf(_LLMP_TAG_RESTART, &state_corpus_serialized) } - fn process( + fn process( &mut self, - state: &mut State, + state: &mut S, executor: &mut E, ) -> Result where - C: Corpus, E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, OT: ObserversTuple, + S: IfInteresting { self.llmp_mgr.process(state, executor) } - fn fire( + fn fire( &mut self, - state: &mut State, + state: &mut S, event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + ) -> Result<(), Error> { // Check if we are going to crash in the event, in which case we store our current state for the next runner self.llmp_mgr.fire(state, event) } @@ -530,24 +489,20 @@ where /// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. /// The restarter will start a new process each time the child crashes or timeouts. #[cfg(feature = "std")] -pub fn setup_restarting_mgr( +pub fn setup_restarting_mgr( //mgr: &mut LlmpEventManager, stats: ST, broker_port: u16, ) -> Result< ( - Option>, + Option, LlmpRestartingEventManager, ), Error, > where I: Input, - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, + S: DeserializeOwned, SH: ShMem, ST: Stats, { @@ -607,7 +562,7 @@ where // Restoring from a previous run, deserialize state and corpus. Some((_sender, _tag, msg)) => { println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); - let (state, mgr): (State, LlmpEventManager) = + let (state, mgr): (S, LlmpEventManager) = deserialize_state_mgr(&msg)?; (Some(state), LlmpRestartingEventManager::new(mgr, sender)) diff --git a/libafl/src/events/logger.rs b/libafl/src/events/logger.rs index 5fe97054aa..e96f5d3ac0 100644 --- a/libafl/src/events/logger.rs +++ b/libafl/src/events/logger.rs @@ -3,15 +3,10 @@ use alloc::{string::ToString, vec::Vec}; #[cfg(feature = "std")] #[cfg(unix)] use crate::{ - corpus::Corpus, events::{BrokerEventResult, Event, EventManager}, - executors::{Executor, HasObservers}, - feedbacks::FeedbacksTuple, + executors::{Executor}, inputs::Input, - observers::ObserversTuple, - state::State, stats::Stats, - utils::Rand, Error, }; @@ -33,19 +28,13 @@ where I: Input, ST: Stats, //CE: CustomEvent, { - fn process( + fn process( &mut self, - state: &mut State, + state: &mut S, _executor: &mut E, ) -> Result where - C: Corpus, - E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, + E: Executor { let count = self.events.len(); while self.events.len() > 0 { @@ -55,17 +44,11 @@ where Ok(count) } - fn fire( + fn fire( &mut self, - _state: &mut State, + _state: &mut S, event: Event, ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, { match Self::handle_in_broker(&mut self.stats, &event)? { BrokerEventResult::Forward => self.events.push(event), @@ -132,18 +115,11 @@ where } // Handle arriving events in the client - fn handle_in_client( + fn handle_in_client( &mut self, - _state: &mut State, + _state: &mut S, event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + ) -> Result<(), Error> { match event { _ => Err(Error::Unknown(format!( "Received illegal message that message should not have arrived: {:?}.", diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index b44c8923a5..1aa4f00440 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -9,13 +9,9 @@ use core::{fmt, marker::PhantomData, time::Duration}; use serde::{Deserialize, Serialize}; use crate::{ - corpus::Corpus, - executors::{Executor, HasObservers}, - feedbacks::FeedbacksTuple, + executors::{Executor}, inputs::Input, observers::ObserversTuple, - state::State, - utils::Rand, Error, }; @@ -162,19 +158,13 @@ where /// Lookup for incoming events and process them. /// Return the number of processes events or an error - fn process( + fn process( &mut self, - state: &mut State, + state: &mut S, executor: &mut E, ) -> Result where - C: Corpus, - E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple; + E: Executor; /// Serialize all observers for this type and manager fn serialize_observers(&mut self, observers: &OT) -> Result, Error> @@ -194,17 +184,10 @@ where /// For restarting event managers, implement a way to forward state to their next peers. #[inline] - fn on_restart( + fn on_restart( &mut self, - _state: &mut State, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + _state: &mut S, + ) -> Result<(), Error> { Ok(()) } @@ -213,17 +196,11 @@ where fn await_restart_safe(&mut self) {} /// Send off an event to the broker - fn fire( + fn fire( &mut self, - _state: &mut State, + state: &mut S, event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple; + ) -> Result<(), Error>; } /// An eventmgr for tests, and as placeholder if you really don't need an event manager. @@ -235,35 +212,22 @@ impl EventManager for NopEventManager where I: Input, { - fn process( + fn process( &mut self, - _state: &mut State, + _state: &mut S, _executor: &mut E, ) -> Result where - C: Corpus, - E: Executor + HasObservers, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, + E: Executor, { Ok(0) } - fn fire( + fn fire( &mut self, - _state: &mut State, + _state: &mut S, _event: Event, - ) -> Result<(), Error> - where - C: Corpus, - FT: FeedbacksTuple, - R: Rand, - OC: Corpus, - OFT: FeedbacksTuple, - { + ) -> Result<(), Error> { Ok(()) } } diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 9f3b523719..2fbe02ad2e 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -54,52 +54,42 @@ where OT: ObserversTuple, { #[inline] - fn pre_exec( + fn pre_exec( &mut self, - _state: &mut State, - _event_mgr: &mut EM, - _input: &I, + state: &mut S, + event_mgr: &mut EM, + input: &I, ) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, - C: Corpus, EM: EventManager, { #[cfg(unix)] #[cfg(feature = "std")] unsafe { - set_oncrash_ptrs::( - _state, - _event_mgr, + set_oncrash_ptrs( + state, + event_mgr, self.observers(), - _input, + input, ); } Ok(()) } #[inline] - fn post_exec( + fn post_exec( &mut self, - _state: &State, + _state: &S, _event_mgr: &mut EM, _input: &I, ) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - C: Corpus, EM: EventManager, - OC: Corpus, - OFT: FeedbacksTuple, { #[cfg(unix)] #[cfg(feature = "std")] unsafe { - reset_oncrash_ptrs::(); + reset_oncrash_ptrs(); } Ok(()) } @@ -148,8 +138,6 @@ where /// depnding on different corpus or state. /// * `name` - the name of this executor (to address it along the way) /// * `harness_fn` - the harness, executiong the function - /// * `on_crash_fn` - When an in-mem harness crashes, it may safe some state to continue fuzzing later. - /// Do that that in this function. The program will crash afterwards. /// * `observers` - the observers observing the target during execution pub fn new( name: &'static str, @@ -161,9 +149,9 @@ where where R: Rand, FT: FeedbacksTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, - C: Corpus, + C: Corpus, EM: EventManager, { #[cfg(feature = "std")] @@ -181,25 +169,6 @@ where } } -/* -unsafe fn tidy_up_on_exit(mgr: &EM) -where -EM: EventManager, -I: Input, -{ - - match manager.llmp { - IsClient { client } => { - let map = client.out_maps.last().unwrap(); - /// wait until we can drop the message safely. - map.await_save_to_unmap_blocking(); - /// Make sure all pages are unmapped. - drop(manager); - } - _ => (), - } -}*/ - #[cfg(feature = "std")] #[cfg(unix)] pub mod unix_signals { @@ -242,15 +211,15 @@ pub mod unix_signals { /// This is needed for certain non-rust side effects, as well as unix signal handling. static mut CURRENT_INPUT_PTR: *const c_void = ptr::null(); - pub unsafe extern "C" fn libaflrs_executor_inmem_handle_crash( + unsafe fn inmem_handle_crash( _sig: c_int, info: siginfo_t, _void: c_void, ) where EM: EventManager, - C: Corpus, + C: Corpus, OT: ObserversTuple, - OC: Corpus, + OC: Corpus, OFT: FeedbacksTuple, FT: FeedbacksTuple, I: Input, @@ -324,14 +293,14 @@ pub mod unix_signals { std::process::exit(1); } - pub unsafe extern "C" fn libaflrs_executor_inmem_handle_timeout( + unsafe fn inmem_handle_timeout( _sig: c_int, _info: siginfo_t, _void: c_void, ) where EM: EventManager, - C: Corpus, - OC: Corpus, + C: Corpus, + OC: Corpus, OFT: FeedbacksTuple, OT: ObserversTuple, FT: FeedbacksTuple, @@ -384,21 +353,12 @@ pub mod unix_signals { } #[inline] - pub unsafe fn set_oncrash_ptrs( - state: &mut State, + pub unsafe fn set_oncrash_ptrs( + state: &mut S, event_mgr: &mut EM, observers: &OT, input: &I, - ) where - EM: EventManager, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, - OT: ObserversTuple, - FT: FeedbacksTuple, - I: Input, - R: Rand, - { + ) { CURRENT_INPUT_PTR = input as *const _ as *const c_void; STATE_PTR = state as *mut _ as *mut c_void; EVENT_MGR_PTR = event_mgr as *mut _ as *mut c_void; @@ -406,19 +366,18 @@ pub mod unix_signals { } #[inline] - pub unsafe fn reset_oncrash_ptrs() { + pub unsafe fn reset_oncrash_ptrs() { CURRENT_INPUT_PTR = ptr::null(); STATE_PTR = ptr::null_mut(); EVENT_MGR_PTR = ptr::null_mut(); - OBSERVERS_PTR = ptr::null_mut(); + OBSERVERS_PTR = ptr::null(); } - // TODO clearly state that manager should be static (maybe put the 'static lifetime?) pub unsafe fn setup_crash_handlers() where EM: EventManager, - C: Corpus, - OC: Corpus, + C: Corpus, + OC: Corpus, OFT: FeedbacksTuple, OT: ObserversTuple, FT: FeedbacksTuple, @@ -441,7 +400,7 @@ pub mod unix_signals { libc::sigemptyset(&mut sa.sa_mask as *mut libc::sigset_t); sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK; sa.sa_sigaction = - libaflrs_executor_inmem_handle_crash:: as usize; + inmem_handle_crash:: as usize; for (sig, msg) in &[ (SIGSEGV, "segfault"), (SIGBUS, "sigbus"), @@ -456,7 +415,7 @@ pub mod unix_signals { } sa.sa_sigaction = - libaflrs_executor_inmem_handle_timeout:: as usize; + inmem_handle_timeout:: as usize; if sigaction(SIGUSR2, &mut sa as *mut sigaction, ptr::null_mut()) < 0 { panic!("Could not set up sigusr2 handler for timeouts"); } diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 77b6802a3a..7a4a794e0d 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -10,13 +10,9 @@ use core::marker::PhantomData; use crate::{ bolts::tuples::{MatchNameAndType, MatchType, Named, TupleList}, - corpus::Corpus, events::EventManager, - feedbacks::FeedbacksTuple, inputs::{HasTargetBytes, Input}, observers::ObserversTuple, - state::State, - utils::Rand, Error, }; @@ -84,18 +80,13 @@ where { #[inline] /// Called right before exexution starts - fn pre_exec( + fn pre_exec( &mut self, - _state: &mut State, + _state: &mut S, _event_mgr: &mut EM, _input: &I, ) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, EM: EventManager, { Ok(()) @@ -103,18 +94,13 @@ where #[inline] /// Called right after execution finished. - fn post_exec( + fn post_exec( &mut self, - _state: &State, + _state: &S, _event_mgr: &mut EM, _input: &I, ) -> Result<(), Error> where - R: Rand, - FT: FeedbacksTuple, - C: Corpus, - OC: Corpus, - OFT: FeedbacksTuple, EM: EventManager, { Ok(()) diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index e6abc6a7a8..e4d924749d 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -6,7 +6,7 @@ use crate::{ executors::{Executor}, inputs::Input, stages::StagesTuple, - state::{HasRand, HasCorpus}, + state::{HasRand, HasCorpus, HasExecutions}, utils::{Rand, current_milliseconds, current_time}, Error }; @@ -70,6 +70,7 @@ where { scheduler: CS, stages: ST, + phantom: PhantomData } impl HasStages for StdFuzzer @@ -139,7 +140,7 @@ where where EM: EventManager, E: Executor, - S: HasCorpus + HasRand, + S: HasCorpus + HasRand + HasExecutions, C: Corpus, R: Rand { @@ -152,7 +153,7 @@ where manager.fire( state, Event::UpdateStats { - executions: state.executions(), + executions: *state.executions(), time: current_time(), phantom: PhantomData, }, @@ -173,6 +174,7 @@ where Self { scheduler: scheduler, stages: stages, + phantom: PhantomData } } } diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index e1e11c07c1..0101e09da1 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -116,6 +116,7 @@ impl From for Error { } } +/* // TODO: no_std test #[cfg(feature = "std")] #[cfg(test)] @@ -198,3 +199,4 @@ mod tests { assert_eq!(state.corpus().count(), corpus_deserialized.count()); } } +*/ \ No newline at end of file diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 29cf4ef807..91da465ead 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -175,7 +175,7 @@ where R: Rand, { scheduled: SM, - phantom: PhantomData<(I, R, S)>, + phantom: PhantomData<(C, I, R, S)>, } impl Mutator for HavocBytesMutator @@ -258,9 +258,8 @@ where } } -impl Default for HavocBytesMutator> +impl Default for HavocBytesMutator> where - SM: ScheduledMutator + HasMaxSize, I: Input + HasBytesVec, S: HasRand + HasCorpus + HasMetadata, C: Corpus, diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 597f7c3d80..71635a33bd 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -3,7 +3,7 @@ use crate::{ inputs::{HasBytesVec, Input}, - state::{HasRand, HasCorpus, HasMetadata}, + state::{HasRand, HasMetadata}, mutators::*, utils::Rand, Error, diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 73f78765df..2e292912ce 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -9,7 +9,7 @@ use crate::{ stages::Stage, state::{HasRand}, utils::Rand, - state::HasCorpus, + state::{HasCorpus, Evaluator}, Error, }; @@ -43,7 +43,7 @@ where where EM: EventManager, E: Executor, - S: HasCorpus, + S: HasCorpus + Evaluator, C: Corpus { let num = self.iterations(state); @@ -57,7 +57,7 @@ where self.mutator_mut() .mutate(state, &mut input_mut, i as i32)?; - let fitness = state.process_input(input_mut, executor, manager)?; + let fitness = state.evaluate_input(input_mut, executor, manager)?; self.mutator_mut().post_exec(state, fitness, i as i32)?; } @@ -121,7 +121,7 @@ where where EM: EventManager, E: Executor, - S: HasCorpus, + S: HasCorpus + Evaluator, C: Corpus { self.perform_mutational(executor, state, manager, corpus_idx) diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 9337ad98a0..bcfe3d11e7 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -1,6 +1,6 @@ //! The fuzzer, and state are the core pieces of every good fuzzer -use core::{fmt::Debug, marker::PhantomData}; +use core::{fmt::Debug, marker::PhantomData, time::Duration}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::{ @@ -36,6 +36,18 @@ where fn corpus_mut(&mut self) -> &mut C; } +/// Trait for elements offering a corpus of solutions +pub trait HasSolutions +where + C: Corpus, + I: Input, +{ + /// The solutions corpus + fn solutions(&self) -> &C; + /// The solutions corpus (mut) + fn solutions_mut(&mut self) -> &mut C; +} + /// Trait for elements offering a rand pub trait HasRand where @@ -64,46 +76,409 @@ pub trait HasMetadata { } } +/// Trait for elements offering a feedbacks tuple +pub trait HasFeedbacks +where + FT: FeedbacksTuple, + I: Input +{ + /// The feedbacks tuple + fn feedbacks(&self) -> &FT; + + /// The feedbacks tuple (mut) + fn feedbacks_mut(&mut self) -> &mut FT; + + /// Resets all metadata holds by feedbacks + #[inline] + fn discard_feedbacks_metadata(&mut self, input: &I) -> Result<(), Error> { + // TODO: This could probably be automatic in the feedback somehow? + self.feedbacks_mut().discard_metadata_all(&input) + } + + /// Creates a new testcase, appending the metadata from each feedback + #[inline] + fn testcase_with_feedbacks_metadata(&mut self, input: I, fitness: u32) -> Result, Error> { + let mut testcase = Testcase::with_fitness(input, fitness); + self.feedbacks_mut().append_metadata_all(&mut testcase)?; + Ok(testcase) + } +} + +/// Trait for elements offering an objective feedbacks tuple +pub trait HasObjectives +where + FT: FeedbacksTuple, + I: Input +{ + /// The objective feedbacks tuple + fn objectives(&self) -> &FT; + + /// The objective feedbacks tuple (mut) + fn objectives_mut(&mut self) -> &mut FT; +} + +/// Trait for the execution counter +pub trait HasExecutions +{ + /// The executions counter + fn executions(&self) -> &usize; + + /// The executions counter (mut) + fn executions_mut(&mut self) -> &mut usize; +} + +/// Trait for the starting time +pub trait HasStartTime +{ + /// The starting time + fn start_time(&self) -> &Duration; + + /// The starting time (mut) + fn start_time_mut(&mut self) -> &mut Duration; +} + +/// Add to the state if interesting +pub trait IfInteresting +where + I: Input +{ + /// Evaluate if a set of observation channels has an interesting state + fn is_interesting( + &mut self, + input: &I, + observers: &OT, + exit_kind: ExitKind, + ) -> Result + where + OT: ObserversTuple; + + /// Adds this input to the corpus, if it's intersting, and return the index + fn add_if_interesting(&mut self, input: &I, fitness: u32) -> Result, Error>; +} + +/// Evaluate an input modyfing the state of the fuzzer and returning a fitness +pub trait Evaluator +where + I: Input, +{ + /// Runs the input and triggers observers and feedback + fn evaluate_input( + &mut self, + input: I, + executor: &mut E, + event_mgr: &mut EM, + ) -> Result + where + E: Executor, + EM: EventManager; +} + /// The state a fuzz run. #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(bound = "FT: serde::de::DeserializeOwned")] -pub struct State +pub struct State where C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + SC: Corpus, OFT: FeedbacksTuple, { /// RNG instance rand: R, /// How many times the executor ran the harness/target executions: usize, + /// At what time the fuzzing started + start_time: Duration, /// The corpus corpus: C, - // TODO use Duration - /// At what time the fuzzing started - start_time: u64, - /// Metadata stored for this state by one of the components - metadata: SerdeAnyMap, /// Feedbacks used to evaluate an input feedbacks: FT, - // Objective corpus - objective_corpus: OC, + // Solutions corpus + solutions: SC, /// Objective Feedbacks - objective_feedbacks: OFT, + objectives: OFT, + /// Metadata stored for this state by one of the components + metadata: SerdeAnyMap, phantom: PhantomData, } + +impl HasRand for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// The rand instance + #[inline] + fn rand(&self) -> &R { + &self.rand + } + + /// The rand instance (mut) + #[inline] + fn rand_mut(&mut self) -> &mut R { + &mut self.rand + } +} + + +impl HasCorpus for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Returns the corpus + #[inline] + fn corpus(&self) -> &C { + &self.corpus + } + + /// Returns the mutable corpus + #[inline] + fn corpus_mut(&mut self) -> &mut C { + &mut self.corpus + } +} + +impl HasSolutions for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Returns the solutions corpus + #[inline] + fn solutions(&self) -> &SC { + &self.solutions + } + + /// Returns the solutions corpus (mut) + #[inline] + fn solutions_mut(&mut self) -> &mut SC { + &mut self.solutions + } +} + +impl HasMetadata for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Get all the metadata into an HashMap + #[inline] + fn metadata(&self) -> &SerdeAnyMap { + &self.metadata + } + + /// Get all the metadata into an HashMap (mutable) + #[inline] + fn metadata_mut(&mut self) -> &mut SerdeAnyMap { + &mut self.metadata + } +} + +impl HasFeedbacks for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// The feedbacks tuple + #[inline] + fn feedbacks(&self) -> &FT { + &self.feedbacks + } + + /// The feedbacks tuple (mut) + #[inline] + fn feedbacks_mut(&mut self) -> &mut FT { + &mut self.feedbacks + } +} + +impl HasObjectives for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// The objective feedbacks tuple + #[inline] + fn objectives(&self) -> &OFT { + &self.objectives + } + + /// The objective feedbacks tuple (mut) + #[inline] + fn objectives_mut(&mut self) -> &mut OFT { + &mut self.objectives + } +} + +impl HasExecutions for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// The executions counter + #[inline] + fn executions(&self) -> &usize { + &self.executions + } + + /// The executions counter (mut) + #[inline] + fn executions_mut(&mut self) -> &mut usize { + &mut self.executions + } +} + +impl HasStartTime for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// The starting time + #[inline] + fn start_time(&self) -> &Duration { + &self.start_time + } + + /// The starting time (mut) + #[inline] + fn start_time_mut(&mut self) -> &mut Duration { + &mut self.start_time + } +} + +impl IfInteresting for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Evaluate if a set of observation channels has an interesting state + fn is_interesting( + &mut self, + input: &I, + observers: &OT, + exit_kind: ExitKind, + ) -> Result + where + OT: ObserversTuple, + { + Ok(self + .feedbacks_mut() + .is_interesting_all(input, observers, exit_kind)?) + } + + /// Adds this input to the corpus, if it's intersting, and return the index + #[inline] + fn add_if_interesting(&mut self, input: &I, fitness: u32) -> Result, Error> { + if fitness > 0 { + let testcase = self.testcase_with_feedbacks_metadata(input.clone(), fitness)?; + Ok(Some(self.corpus.add(testcase)?)) // TODO scheduler hook + } else { + self.discard_feedbacks_metadata(input)?; + Ok(None) + } + } +} + +impl Evaluator for State +where + C: Corpus, + I: Input, + R: Rand, + FT: FeedbacksTuple, + SC: Corpus, + OFT: FeedbacksTuple, +{ + /// Process one input, adding to the respective corpuses if needed and firing the right events + #[inline] + fn evaluate_input( + &mut self, + // TODO probably we can take a ref to input and pass a cloned one to add_if_interesting + input: I, + executor: &mut E, + manager: &mut EM, + ) -> Result + where + E: Executor + HasObservers, + OT: ObserversTuple, + C: Corpus, + EM: EventManager, + { + let (fitness, 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 + self.solutions_mut().add(Testcase::new(input.clone()))?; + } + + if let idx = Some(self.add_if_interesting(&input, fitness)?) { + let observers_buf = manager.serialize_observers(observers)?; + manager.fire( + self, + Event::NewTestcase { + input: input, + observers_buf, + corpus_size: self.corpus().count() + 1, + client_config: "TODO".into(), + time: crate::utils::current_time(), + executions: *self.executions(), + }, + )?; + } + + Ok(fitness) + } +} + + #[cfg(feature = "std")] -impl State +impl State where C: Corpus, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + SC: Corpus, OFT: FeedbacksTuple, { pub fn load_from_directory( @@ -133,12 +508,12 @@ where println!("Loading file {:?} ...", &path); let bytes = fs::read(&path)?; let input = BytesInput::new(bytes); - let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?; - if self.add_if_interesting(input, fitness)?.is_none() { + let (fitness, is_solution) = self.execute_input(&input, executor, manager)?; + if self.add_if_interesting(&input, fitness)?.is_none() { println!("File {:?} was not interesting, skipped.", &path); } - if obj_fitness > 0 { - println!("File {:?} is an objective, however will be not added as an initial testcase.", &path); + if is_solution { + println!("File {:?} is a solution, however will be not considered as it is an initial testcase.", &path); } } else if attr.is_dir() { self.load_from_directory(executor, manager, &path)?; @@ -176,159 +551,23 @@ where } } -impl HasRand for State + +impl State where C: Corpus, I: Input, R: Rand, FT: FeedbacksTuple, - OC: Corpus, + SC: Corpus, OFT: FeedbacksTuple, { - /// The rand instance - fn rand(&self) -> &R { - &self.rand - } - - /// The rand instance (mut) - fn rand_mut(&mut self) -> &mut R { - &mut self.rand - } -} - - -impl HasCorpus for State -where - C: Corpus, - I: Input, - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, -{ - /// Returns the corpus - fn corpus(&self) -> &C { - &self.corpus - } - - /// Returns the mutable corpus - fn corpus_mut(&mut self) -> &mut C { - &mut self.corpus - } -} - -/// Trait for elements offering metadata -impl HasMetadata for State -where - C: Corpus, - I: Input, - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, -{ - /// Get all the metadata into an HashMap - #[inline] - fn metadata(&self) -> &SerdeAnyMap { - &self.metadata - } - - /// Get all the metadata into an HashMap (mutable) - #[inline] - fn metadata_mut(&mut self) -> &mut SerdeAnyMap { - &mut self.metadata - } -} - -impl State -where - C: Corpus, - I: Input, - R: Rand, - FT: FeedbacksTuple, - OC: Corpus, - OFT: FeedbacksTuple, -{ - /// Get executions - #[inline] - pub fn executions(&self) -> usize { - self.executions - } - - /// Set executions - #[inline] - pub fn set_executions(&mut self, executions: usize) { - self.executions = executions - } - - #[inline] - pub fn start_time(&self) -> u64 { - self.start_time - } - #[inline] - pub fn set_start_time(&mut self, ms: u64) { - self.start_time = ms - } - - /// Returns vector of feebacks - #[inline] - pub fn feedbacks(&self) -> &FT { - &self.feedbacks - } - - /// Returns vector of feebacks (mutable) - #[inline] - pub fn feedbacks_mut(&mut self) -> &mut FT { - &mut self.feedbacks - } - - /// Returns vector of objective feebacks - #[inline] - pub fn objective_feedbacks(&self) -> &OFT { - &self.objective_feedbacks - } - - /// Returns vector of objective feebacks (mutable) - #[inline] - pub fn objective_feedbacks_mut(&mut self) -> &mut OFT { - &mut self.objective_feedbacks - } - - /// Returns the objective corpus - #[inline] - pub fn objective_corpus(&self) -> &OC { - &self.objective_corpus - } - - /// Returns the mutable objective corpus - #[inline] - pub fn objective_corpus_mut(&mut self) -> &mut OC { - &mut self.objective_corpus - } - - // TODO move some of these, like evaluate_input, to FuzzingEngine - #[inline] - pub fn is_interesting( - &mut self, - input: &I, - observers: &OT, - exit_kind: ExitKind, - ) -> Result - where - OT: ObserversTuple, - { - Ok(self - .feedbacks_mut() - .is_interesting_all(input, observers, exit_kind)?) - } - /// Runs the input and triggers observers and feedback - pub fn evaluate_input( + pub fn execute_input( &mut self, input: &I, executor: &mut E, event_mgr: &mut EM, - ) -> Result<(u32, u32), Error> + ) -> Result<(u32, bool), Error> where E: Executor + HasObservers, OT: ObserversTuple, @@ -341,121 +580,18 @@ where let exit_kind = executor.run_target(input)?; executor.post_exec(&self, event_mgr, input)?; - self.set_executions(self.executions() + 1); + *self.executions_mut() += 1; executor.post_exec_observers()?; let observers = executor.observers(); - let objective_fitness = - self.objective_feedbacks - .is_interesting_all(&input, observers, exit_kind.clone())?; + let fitness = self .feedbacks_mut() .is_interesting_all(&input, observers, exit_kind)?; - Ok((fitness, objective_fitness)) - } + + let is_solution = self.objectives_mut().is_interesting_all(&input, observers, exit_kind.clone())? > 0; - /// Resets all current feedbacks - #[inline] - pub fn discard_input(&mut self, input: &I) -> Result<(), Error> { - // TODO: This could probably be automatic in the feedback somehow? - self.feedbacks_mut().discard_metadata_all(&input) - } - - /// Creates a new testcase, appending the metadata from each feedback - #[inline] - pub fn input_to_testcase(&mut self, input: I, fitness: u32) -> Result, Error> { - let mut testcase = Testcase::new(input); - testcase.set_fitness(fitness); - self.feedbacks_mut().append_metadata_all(&mut testcase)?; - Ok(testcase) - } - - /// Create a testcase from this input, if it's intersting - #[inline] - pub fn testcase_if_interesting( - &mut self, - input: I, - fitness: u32, - ) -> Result>, Error> { - if fitness > 0 { - Ok(Some(self.input_to_testcase(input, fitness)?)) - } else { - self.discard_input(&input)?; - Ok(None) - } - } - - /// Adds this input to the corpus, if it's intersting - #[inline] - pub fn add_if_interesting(&mut self, input: I, fitness: u32) -> Result, Error> - where - C: Corpus, - { - if fitness > 0 { - let testcase = self.input_to_testcase(input, fitness)?; - Ok(Some(C::add(self, testcase)?)) - } else { - self.discard_input(&input)?; - Ok(None) - } - } - - /// Adds this input to the objective corpus, if it's an objective - #[inline] - pub fn add_if_objective(&mut self, input: I, fitness: u32) -> Result, Error> - where - C: Corpus, - { - if fitness > 0 { - let testcase = self.input_to_testcase(input, fitness)?; - Ok(Some(self.objective_corpus.add(testcase))) - } else { - self.discard_input(&input)?; - Ok(None) - } - } - - /// Process one input, adding to the respective corpuses if needed and firing the right events - #[inline] - pub fn process_input( - &mut self, - // TODO probably we can take a ref to input and pass a cloned one to add_if_interesting - input: I, - executor: &mut E, - manager: &mut EM, - ) -> Result - where - E: Executor + HasObservers, - OT: ObserversTuple, - C: Corpus, - EM: EventManager, - { - let (fitness, obj_fitness) = self.evaluate_input(&input, executor, manager)?; - let observers = executor.observers(); - - if obj_fitness > 0 { - self.add_if_objective(input.clone(), obj_fitness)?; - } - - if fitness > 0 { - let observers_buf = manager.serialize_observers(observers)?; - manager.fire( - self, - Event::NewTestcase { - input: input.clone(), - observers_buf, - corpus_size: self.corpus().count() + 1, - client_config: "TODO".into(), - time: crate::utils::current_time(), - executions: self.executions(), - }, - )?; - self.add_if_interesting(input, fitness)?; - } else { - self.discard_input(&input)?; - } - - Ok(fitness) + Ok((fitness, is_solution)) } pub fn generate_initial_inputs( @@ -475,8 +611,8 @@ where { let mut added = 0; for _ in 0..num { - let input = generator.generate(rand)?; - let fitness = self.process_input(input, executor, manager)?; + let input = generator.generate(self.rand_mut())?; + let fitness = self.evaluate_input(input, executor, manager)?; if fitness > 0 { added += 1; } @@ -493,15 +629,16 @@ where Ok(()) } - pub fn new(corpus: C, feedbacks: FT, objective_corpus: OC, objective_feedbacks: OFT) -> Self { + pub fn new(rand: R, corpus: C, feedbacks: FT, solutions: SC, objectives: OFT) -> Self { Self { - corpus, + rand, executions: 0, - start_time: current_milliseconds(), + start_time: Duration::from_millis(0), metadata: SerdeAnyMap::default(), - feedbacks: feedbacks, - objective_corpus: objective_corpus, - objective_feedbacks: objective_feedbacks, + corpus, + feedbacks, + solutions, + objectives, phantom: PhantomData, } }