Skip to content

Commit

Permalink
Adding StdXObserver Docs (#2311)
Browse files Browse the repository at this point in the history
* Adding StdXObserver Docs

* fixing docs

* code cleanup

* moving example

* improving exclusion rules

* adding impls for features

* adding test exclusions

* excluding miri from OS including tests

* fixing CI

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
  • Loading branch information
riesentoaster and tokatoka committed Jun 14, 2024
1 parent c3930b3 commit 5a2652b
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 2 deletions.
2 changes: 1 addition & 1 deletion libafl/src/fuzzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,7 @@ where
}
}

/// Runs the input and triggers observers and feedback
/// Runs the input and triggers observers
pub fn execute_input<E, EM>(
&mut self,
state: &mut <Self as UsesState>::State,
Expand Down
34 changes: 33 additions & 1 deletion libafl/src/mutators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -407,3 +407,35 @@ impl<I, S> IntoVec<Box<dyn Mutator<I, S>>> for Vec<Box<dyn Mutator<I, S>>> {
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<I, S> Mutator<I, S> for NopMutator {
fn mutate(&mut self, _state: &mut S, _input: &mut I) -> Result<MutationResult, Error> {
Ok(self.result)
}
}

impl Named for NopMutator {
fn name(&self) -> &Cow<'static, str> {
&Cow::Borrowed("NopMutator")
}
}
165 changes: 165 additions & 0 deletions libafl/src/observers/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>> = None;
/// static mut STDERR: Option<Vec<u8>> = None;
///
/// #[derive(Clone)]
/// struct ExportStdXObserver {
/// stdout_observer: Handle<StdOutObserver>,
/// stderr_observer: Handle<StdErrObserver>,
/// }
///
/// impl<S> Feedback<S> for ExportStdXObserver
/// where
/// S: State
/// {
/// fn is_interesting<EM, OT>(
/// &mut self,
/// _state: &mut S,
/// _manager: &mut EM,
/// _input: &<S>::Input,
/// observers: &OT,
/// _exit_kind: &ExitKind,
/// ) -> Result<bool, Error>
/// where
/// EM: EventFirer<State = S>,
/// OT: ObserversTuple<S>,
/// {
/// 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<bool, Error> {
/// 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::<Vec<u8>>(), // 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<u8> = 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.
Expand Down Expand Up @@ -55,10 +208,17 @@ where
self.stdout = None;
Ok(())
}

fn pre_exec(&mut self, _state: &mut S, _input: &<S as UsesInput>::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.
Expand Down Expand Up @@ -102,4 +262,9 @@ where
self.stderr = None;
Ok(())
}

fn pre_exec(&mut self, _state: &mut S, _input: &<S as UsesInput>::Input) -> Result<(), Error> {
self.stderr = None;
Ok(())
}
}

0 comments on commit 5a2652b

Please sign in to comment.