Adding StdXObserver Docs (#2311)
* 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>
This commit is contained in:
parent
c3930b39fc
commit
5a2652b984
@ -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>(
|
pub fn execute_input<E, EM>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut <Self as UsesState>::State,
|
state: &mut <Self as UsesState>::State,
|
||||||
|
@ -33,7 +33,7 @@ pub use multi::*;
|
|||||||
#[cfg(feature = "nautilus")]
|
#[cfg(feature = "nautilus")]
|
||||||
pub mod 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};
|
use libafl_bolts::{tuples::IntoVec, HasLen, Named};
|
||||||
#[cfg(feature = "nautilus")]
|
#[cfg(feature = "nautilus")]
|
||||||
@ -407,3 +407,35 @@ impl<I, S> IntoVec<Box<dyn Mutator<I, S>>> for Vec<Box<dyn Mutator<I, S>>> {
|
|||||||
self
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,6 +12,159 @@ use crate::{inputs::UsesInput, observers::Observer, state::State, Error};
|
|||||||
|
|
||||||
/// An observer that captures stdout of a target.
|
/// An observer that captures stdout of a target.
|
||||||
/// Only works for supported executors.
|
/// 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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct StdOutObserver {
|
pub struct StdOutObserver {
|
||||||
/// The name of the observer.
|
/// The name of the observer.
|
||||||
@ -55,10 +208,17 @@ where
|
|||||||
self.stdout = None;
|
self.stdout = None;
|
||||||
Ok(())
|
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.
|
/// An observer that captures stderr of a target.
|
||||||
/// Only works for supported executors.
|
/// Only works for supported executors.
|
||||||
|
///
|
||||||
|
/// Check docs for [`StdOutObserver`] for example.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct StdErrObserver {
|
pub struct StdErrObserver {
|
||||||
/// The name of the observer.
|
/// The name of the observer.
|
||||||
@ -102,4 +262,9 @@ where
|
|||||||
self.stderr = None;
|
self.stderr = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &<S as UsesInput>::Input) -> Result<(), Error> {
|
||||||
|
self.stderr = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user