diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index 86b9111855..e8a8c26b50 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -772,7 +772,7 @@ where } } - /// Runs the input and triggers observers and feedback + /// Runs the input and triggers observers pub fn execute_input( &mut self, state: &mut ::State, diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 7fa46e4b9c..bf13fe318c 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -33,7 +33,7 @@ pub use multi::*; #[cfg(feature = "nautilus")] pub mod nautilus; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{borrow::Cow, boxed::Box, vec::Vec}; use libafl_bolts::{tuples::IntoVec, HasLen, Named}; #[cfg(feature = "nautilus")] @@ -407,3 +407,35 @@ impl IntoVec>> for Vec>> { self } } + +/// [`Mutator`] that does nothing, used for testing. +/// +/// Example: +/// +/// ```rust,ignore +/// let mut stages = tuple_list!(StdMutationalStage::new(NopMutator(MutationResult::Mutated))); +/// ``` +#[derive(Debug, Clone)] +pub struct NopMutator { + result: MutationResult, +} + +impl NopMutator { + /// The passed argument is returned every time the mutator is called. + #[must_use] + pub fn new(result: MutationResult) -> Self { + Self { result } + } +} + +impl Mutator for NopMutator { + fn mutate(&mut self, _state: &mut S, _input: &mut I) -> Result { + Ok(self.result) + } +} + +impl Named for NopMutator { + fn name(&self) -> &Cow<'static, str> { + &Cow::Borrowed("NopMutator") + } +} diff --git a/libafl/src/observers/stdio.rs b/libafl/src/observers/stdio.rs index e73c378d41..4ee30876c9 100644 --- a/libafl/src/observers/stdio.rs +++ b/libafl/src/observers/stdio.rs @@ -12,6 +12,159 @@ use crate::{inputs::UsesInput, observers::Observer, state::State, Error}; /// An observer that captures stdout of a target. /// Only works for supported executors. +/// +/// # Example usage +#[cfg_attr(all(feature = "std", target_os = "linux", not(miri)), doc = " ```")] // miri doesn't like the Command crate, linux as a shorthand for the availability of base64 +#[cfg_attr( + not(all(feature = "std", target_os = "linux", not(miri))), + doc = " ```ignore" +)] +/// use std::borrow::Cow; +/// +/// use libafl::{ +/// corpus::{Corpus, InMemoryCorpus, Testcase}, +/// events::{EventFirer, NopEventManager}, +/// executors::{CommandExecutor, ExitKind}, +/// feedbacks::Feedback, +/// inputs::{BytesInput, UsesInput}, +/// mutators::{MutationResult, NopMutator}, +/// observers::{ObserversTuple, StdErrObserver, StdOutObserver}, +/// schedulers::QueueScheduler, +/// stages::StdMutationalStage, +/// state::{HasCorpus, State, StdState}, +/// Error, Fuzzer, StdFuzzer, +/// }; +/// +/// use libafl_bolts::{ +/// current_nanos, +/// rands::StdRand, +/// tuples::{tuple_list, Handle, Handled, MatchNameRef}, +/// Named, +/// }; +/// +/// static mut STDOUT: Option> = None; +/// static mut STDERR: Option> = None; +/// +/// #[derive(Clone)] +/// struct ExportStdXObserver { +/// stdout_observer: Handle, +/// stderr_observer: Handle, +/// } +/// +/// impl Feedback for ExportStdXObserver +/// where +/// S: State +/// { +/// fn is_interesting( +/// &mut self, +/// _state: &mut S, +/// _manager: &mut EM, +/// _input: &::Input, +/// observers: &OT, +/// _exit_kind: &ExitKind, +/// ) -> Result +/// where +/// EM: EventFirer, +/// OT: ObserversTuple, +/// { +/// unsafe { +/// STDOUT = observers.get(&self.stdout_observer).unwrap().stdout.clone(); +/// STDERR = observers.get(&self.stderr_observer).unwrap().stderr.clone(); +/// } +/// Ok(true) +/// } +/// +/// #[cfg(feature = "track_hit_feedbacks")] +/// fn last_result(&self) -> Result { +/// Ok(true) +/// } +/// } +/// +/// impl Named for ExportStdXObserver { +/// fn name(&self) -> &Cow<'static, str> { +/// &Cow::Borrowed("ExportStdXObserver") +/// } +/// } +/// +/// fn main() { +/// let input_text = "Hello, World!"; +/// let encoded_input_text = "SGVsbG8sIFdvcmxkIQo="; +/// +/// let stdout_observer = StdOutObserver::new("stdout-observer"); +/// let stderr_observer = StdErrObserver::new("stderr-observer"); +/// +/// let mut feedback = ExportStdXObserver { +/// stdout_observer: stdout_observer.handle(), +/// stderr_observer: stderr_observer.handle(), +/// }; +/// +/// let mut objective = (); +/// +/// let mut executor = CommandExecutor::builder() +/// .program("base64") +/// .arg("--decode") +/// .stdout_observer(stdout_observer.handle()) +/// .stderr_observer(stderr_observer.handle()) +/// .build(tuple_list!(stdout_observer, stderr_observer)) +/// .unwrap(); +/// +/// let mut state = StdState::new( +/// StdRand::with_seed(current_nanos()), +/// InMemoryCorpus::new(), +/// InMemoryCorpus::new(), +/// &mut feedback, +/// &mut objective, +/// ) +/// .unwrap(); +/// +/// let scheduler = QueueScheduler::new(); +/// let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); +/// let mut manager = NopEventManager::new(); +/// +/// let mut stages = tuple_list!(StdMutationalStage::new(NopMutator::new( +/// MutationResult::Mutated +/// ))); +/// +/// state +/// .corpus_mut() +/// .add(Testcase::new(BytesInput::from( +/// encoded_input_text.as_bytes().to_vec(), +/// ))) +/// .unwrap(); +/// +/// let corpus_id = fuzzer +/// .fuzz_one(&mut stages, &mut executor, &mut state, &mut manager) +/// .unwrap(); +/// +/// unsafe { +/// assert!(input_text +/// .as_bytes() +/// .iter() +/// .zip(STDOUT.as_ref().unwrap().iter().filter(|e| **e != 10)) // ignore newline chars +/// .all(|(&a, &b)| a == b)); +/// assert!(STDERR.as_ref().unwrap().is_empty()); +/// } +/// +/// state +/// .corpus() +/// .get(corpus_id) +/// .unwrap() +/// .replace(Testcase::new(BytesInput::from( +/// encoded_input_text.bytes().skip(1).collect::>(), // skip one char to make it invalid code +/// ))); +/// +/// fuzzer +/// .fuzz_one(&mut stages, &mut executor, &mut state, &mut manager) +/// .unwrap(); +/// +/// unsafe { +/// let compare_vec: Vec = Vec::new(); +/// assert_eq!(compare_vec, *STDERR.as_ref().unwrap()); +/// // stdout will still contain data, we're just checking that there is an error message +/// } +/// } +/// ``` + #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct StdOutObserver { /// The name of the observer. @@ -55,10 +208,17 @@ where self.stdout = None; Ok(()) } + + fn pre_exec(&mut self, _state: &mut S, _input: &::Input) -> Result<(), Error> { + self.stdout = None; + Ok(()) + } } /// An observer that captures stderr of a target. /// Only works for supported executors. +/// +/// Check docs for [`StdOutObserver`] for example. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct StdErrObserver { /// The name of the observer. @@ -102,4 +262,9 @@ where self.stderr = None; Ok(()) } + + fn pre_exec(&mut self, _state: &mut S, _input: &::Input) -> Result<(), Error> { + self.stderr = None; + Ok(()) + } }