diff --git a/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs index 85485ab855..c5fd3dc4d7 100644 --- a/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/command_executor/src/main.rs @@ -24,7 +24,7 @@ use libafl::{ inputs::{HasTargetBytes, Input}, monitors::SimpleMonitor, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - observers::{get_asan_runtime_flags, ASANBacktraceObserver, StdMapObserver}, + observers::{get_asan_runtime_flags, AsanBacktraceObserver, StdMapObserver}, schedulers::QueueScheduler, stages::mutational::StdMutationalStage, state::StdState, @@ -40,7 +40,7 @@ pub fn main() { // Create an observation channel using the signals map let observer = StdMapObserver::new("signals", signals.as_mut_slice()); // Create a stacktrace observer - let bt_observer = ASANBacktraceObserver::new("ASANBacktraceObserver"); + let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver"); // Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks let mut feedback = MaxMapFeedback::new(&observer); @@ -48,7 +48,7 @@ pub fn main() { // A feedback to choose if an input is a solution or not let mut objective = feedback_and!( CrashFeedback::new(), - NewHashFeedback::::new(&bt_observer) + NewHashFeedback::::new(&bt_observer) ); // let mut objective = CrashFeedback::new(); @@ -76,7 +76,7 @@ pub fn main() { // such as the notification of the addition of a new item to the corpus let mut mgr = SimpleEventManager::new(mon); - // A queue policy to get testcasess from the corpus + // A queue policy to get testcases from the corpus let scheduler = QueueScheduler::new(); // A fuzzer with feedbacks and a corpus scheduler @@ -108,12 +108,7 @@ pub fn main() { } } - let mut executor = MyExecutor { shmem_id }.into_executor( - tuple_list!(observer, bt_observer), - None, - None, - Some("ASANBacktraceObserver".to_string()), - ); + let mut executor = MyExecutor { shmem_id }.into_executor(tuple_list!(observer, bt_observer)); // Generator of printable bytearrays of max size 32 let mut generator = RandPrintablesGenerator::new(32); diff --git a/fuzzers/backtrace_baby_fuzzers/forkserver_executor/src/main.rs b/fuzzers/backtrace_baby_fuzzers/forkserver_executor/src/main.rs index 335e479667..bc5392c559 100644 --- a/fuzzers/backtrace_baby_fuzzers/forkserver_executor/src/main.rs +++ b/fuzzers/backtrace_baby_fuzzers/forkserver_executor/src/main.rs @@ -22,7 +22,7 @@ use libafl::{ inputs::BytesInput, monitors::SimpleMonitor, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - observers::{ASANBacktraceObserver, ConstMapObserver, HitcountsMapObserver}, + observers::{AsanBacktraceObserver, ConstMapObserver, HitcountsMapObserver}, schedulers::QueueScheduler, stages::mutational::StdMutationalStage, state::StdState, @@ -50,7 +50,7 @@ pub fn main() { shmem_map, )); - let bt_observer = ASANBacktraceObserver::new("ASANBacktraceObserver"); + let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver"); // Feedback to rate the interestingness of an input // This one is composed by two Feedbacks in OR diff --git a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs index d0e7aff0da..8455d45243 100644 --- a/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs +++ b/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs @@ -224,7 +224,7 @@ fn fuzz( // Create a concolic trace ConcolicTracingStage::new( TracingStage::new( - MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer), None, None, None) + MyCommandConfigurator::default().into_executor(tuple_list!(concolic_observer)) ), concolic_observer_name, ), diff --git a/libafl/src/bolts/cpu.rs b/libafl/src/bolts/cpu.rs index dc678a2fa0..2a9c46dc55 100644 --- a/libafl/src/bolts/cpu.rs +++ b/libafl/src/bolts/cpu.rs @@ -36,7 +36,7 @@ pub fn read_time_counter() -> u64 { #[cfg(target_arch = "aarch64")] #[must_use] pub fn read_time_counter() -> u64 { - let mut v: u64 = 0; + let mut v: u64; unsafe { // TODO pushing a change in core::arch::aarch64 ? asm!("mrs {v}, cntvct_el0", v = out(reg) v); diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 72e9d9b9e2..9e269ccaad 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -27,7 +27,7 @@ use crate::{ AsSlice, }, inputs::HasTargetBytes, - observers::{ASANBacktraceObserver, ObserversTuple, StdErrObserver, StdOutObserver}, + observers::ObserversTuple, std::borrow::ToOwned, }; #[cfg(feature = "std")] @@ -45,10 +45,10 @@ enum InputLocation { }, /// Deliver input via `StdIn` StdIn, - /// Deliver the iniput via the specified [`InputFile`] + /// Deliver the input via the specified [`InputFile`] /// You can use specify [`InputFile::create(INPUTFILE_STD)`] to use a default filename. File { - /// The fiel to write input to. The target should read input from this location. + /// The file to write input to. The target should read input from this location. out_file: InputFile, }, } @@ -79,7 +79,6 @@ pub struct StdCommandConfigurator { debug_child: bool, has_stdout_observer: bool, has_stderr_observer: bool, - has_asan_observer: bool, /// true: input gets delivered via stdink input_location: InputLocation, /// The Command to execute @@ -104,7 +103,7 @@ impl CommandConfigurator for StdCommandConfigurator { if self.has_stdout_observer { cmd.stdout(Stdio::piped()); } - if self.has_stderr_observer || self.has_asan_observer { + if self.has_stderr_observer { cmd.stderr(Stdio::piped()); } @@ -150,23 +149,16 @@ impl CommandConfigurator for StdCommandConfigurator { /// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process. /// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it. -/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`]. +/// Instead, you can also use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`]. pub struct CommandExecutor where T: Debug, OT: Debug, { - /// The wrapped comand configurer + /// The wrapped command configurer configurer: T, + /// The obsevers used by this executor observers: OT, - /// cache if the AsanBacktraceObserver is present - has_asan_observer: Option, - /// If set, we found a [`StdErrObserver`] in the observer list. - /// Pipe the child's `stderr` instead of closing it. - has_stdout_observer: Option, - /// If set, we found a [`StdOutObserver`] in the observer list - /// Pipe the child's `stdout` instead of closing it. - has_stderr_observer: Option, phantom: PhantomData<(EM, I, S, Z)>, } @@ -215,18 +207,15 @@ where impl CommandExecutor where - OT: MatchName + Debug, + OT: MatchName + Debug + ObserversTuple, { /// Creates a new `CommandExecutor`. /// Instead of parsing the Command for `@@`, it will pub fn from_cmd_with_file

( cmd: &Command, debug_child: bool, - observers: OT, + mut observers: OT, path: P, - has_stdout_observer: Option, - has_stderr_observer: Option, - has_asan_observer: Option, ) -> Result where P: AsRef, @@ -238,43 +227,37 @@ where } command.stdin(Stdio::null()); - if has_stdout_observer.is_some() { + let has_stdout_observer = observers.observes_stdout(); + if has_stdout_observer { command.stdout(Stdio::piped()); } - if has_stderr_observer.is_some() || has_asan_observer.is_some() { + let has_stderr_observer = observers.observes_stderr(); + if has_stderr_observer { command.stderr(Stdio::piped()); } Ok(Self { observers, - configurer: StdCommandConfigurator { input_location: InputLocation::File { out_file: InputFile::create(path)?, }, command, debug_child, - has_stdout_observer: has_stdout_observer.is_some(), - has_stderr_observer: has_stderr_observer.is_some(), - has_asan_observer: has_asan_observer.is_some(), + has_stdout_observer, + has_stderr_observer, }, - has_stdout_observer, - has_stderr_observer, - has_asan_observer, phantom: PhantomData, }) } - /// Parses an AFL-like comandline, replacing `@@` with the input file. + /// Parses an AFL-like commandline, replacing `@@` with the input file. /// If no `@@` was found, will use stdin for input. /// The arg 0 is the program. pub fn parse_afl_cmdline( args: IT, observers: OT, debug_child: bool, - has_stdout_observer: Option, - has_stderr_observer: Option, - has_asan_observer: Option, ) -> Result where IT: IntoIterator, @@ -283,16 +266,6 @@ where let mut atat_at = None; let mut builder = CommandExecutorBuilder::new(); builder.debug_child(debug_child); - if let Some(name) = has_stdout_observer { - builder.stdout_observer(name); - } - if let Some(name) = has_stderr_observer { - builder.stderr_observer(name); - } - if let Some(name) = has_asan_observer { - builder.asan_observer(name); - } - let afl_delim = OsStr::new("@@"); for (pos, arg) in args.into_iter().enumerate() { @@ -326,7 +299,7 @@ impl Executor for CommandExecutor, T: Debug, { fn run_target( @@ -361,37 +334,23 @@ where } }; - if self.has_asan_observer.is_some() || self.has_stderr_observer.is_some() { + if self.observers.observes_stderr() { let mut stderr = String::new(); child.stderr.as_mut().ok_or_else(|| { Error::illegal_state( "Observer tries to read stderr, but stderr was not `Stdio::pipe` in CommandExecutor", ) })?.read_to_string(&mut stderr)?; - if let Some(name) = self.has_asan_observer.as_ref() { - self.observers - .match_name_mut::(name) - .unwrap() - .parse_asan_output(&stderr); - } - if let Some(name) = self.has_stderr_observer.as_ref() { - self.observers - .match_name_mut::(name) - .unwrap() - .stderr = Some(stderr); - } + self.observers.observe_stderr(&stderr); } - if let Some(name) = self.has_stdout_observer.as_ref() { + if self.observers.observes_stdout() { let mut stdout = String::new(); child.stdout.as_mut().ok_or_else(|| { Error::illegal_state( "Observer tries to read stdout, but stdout was not `Stdio::pipe` in CommandExecutor", ) })?.read_to_string(&mut stdout)?; - self.observers - .match_name_mut::(name) - .unwrap() - .stdout = Some(stdout); + self.observers.observe_stdout(&stdout); } res @@ -419,9 +378,6 @@ pub struct CommandExecutorBuilder { input_location: InputLocation, cwd: Option, envs: Vec<(OsString, OsString)>, - has_stdout_observer: Option, - has_stderr_observer: Option, - has_asan_observer: Option, } impl Default for CommandExecutorBuilder { @@ -441,30 +397,9 @@ impl CommandExecutorBuilder { cwd: None, envs: vec![], debug_child: false, - has_stdout_observer: None, - has_stderr_observer: None, - has_asan_observer: None, } } - /// Set the stdout observer name - pub fn stdout_observer(&mut self, name: String) -> &mut Self { - self.has_stdout_observer = Some(name); - self - } - - /// Set the stderr observer name - pub fn stderr_observer(&mut self, name: String) -> &mut Self { - self.has_stderr_observer = Some(name); - self - } - - /// Set the asan observer name - pub fn asan_observer(&mut self, name: String) -> &mut Self { - self.has_asan_observer = Some(name); - self - } - /// Set the binary to execute /// This option is required. pub fn program(&mut self, program: O) -> &mut Self @@ -573,13 +508,13 @@ impl CommandExecutorBuilder { self } - /// Builds the `ComandExecutor` + /// Builds the `CommandExecutor`. pub fn build( &self, - observers: OT, + mut observers: OT, ) -> Result, Error> where - OT: Debug + MatchName, + OT: Debug + MatchName + ObserversTuple, { let program = if let Some(program) = &self.program { program @@ -610,28 +545,22 @@ impl CommandExecutorBuilder { command.stdout(Stdio::null()); command.stderr(Stdio::null()); } - if self.has_stderr_observer.is_some() || self.has_asan_observer.is_some() { - // we need stderr for ASANBackt - command.stderr(Stdio::piped()); - } - if self.has_stdout_observer.is_some() { + if observers.observes_stdout() { command.stdout(Stdio::piped()); } + if observers.observes_stderr() { + // we need stderr for `AsanBacktaceObserver`, and others + command.stderr(Stdio::piped()); + } let configurator = StdCommandConfigurator { debug_child: self.debug_child, - has_stdout_observer: self.has_stdout_observer.is_some(), - has_stderr_observer: self.has_stderr_observer.is_some(), - has_asan_observer: self.has_asan_observer.is_some(), + has_stdout_observer: observers.observes_stdout(), + has_stderr_observer: observers.observes_stderr(), input_location: self.input_location.clone(), command, }; - Ok(configurator.into_executor::( - observers, - self.has_stdout_observer.clone(), - self.has_stderr_observer.clone(), - self.has_asan_observer.clone(), - )) + Ok(configurator.into_executor::(observers)) } } @@ -663,7 +592,7 @@ impl CommandExecutorBuilder { /// } /// /// fn make_executor() -> impl Executor { -/// MyExecutor.into_executor((), None, None, None) +/// MyExecutor.into_executor(()) /// } /// ``` @@ -675,21 +604,13 @@ pub trait CommandConfigurator: Sized + Debug { I: Input + HasTargetBytes; /// Create an `Executor` from this `CommandConfigurator`. - fn into_executor( - self, - observers: OT, - has_stdout_observer: Option, - has_stderr_observer: Option, - has_asan_observer: Option, - ) -> CommandExecutor + /// It will observe the outputs with the respective given observer name. + fn into_executor(self, observers: OT) -> CommandExecutor where OT: Debug + MatchName, { CommandExecutor { observers, - has_asan_observer, - has_stdout_observer, - has_stderr_observer, configurer: self, phantom: PhantomData, } @@ -740,15 +661,9 @@ mod tests { println!("{status}"); })); - let mut executor = CommandExecutor::parse_afl_cmdline( - &["file".to_string(), "@@".to_string()], - (), - true, - None, - None, - None, - ) - .unwrap(); + let mut executor = + CommandExecutor::parse_afl_cmdline(&["file".to_string(), "@@".to_string()], (), true) + .unwrap(); executor .run_target( &mut (), diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index 903601ec2f..dba1c7cef9 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -146,6 +146,29 @@ where .as_mut() .post_exec_child_all(state, input, exit_kind) } + + /// Returns true if a `stdout` observer was added to the list + #[inline] + fn observes_stdout(&mut self) -> bool { + self.primary.as_mut().observes_stdout() || self.secondary.as_mut().observes_stdout() + } + /// Returns true if a `stderr` observer was added to the list + #[inline] + fn observes_stderr(&mut self) -> bool { + self.primary.as_mut().observes_stderr() || self.secondary.as_mut().observes_stderr() + } + + /// Runs `observe_stdout` for all stdout observers in the list + fn observe_stdout(&mut self, stdout: &str) { + self.primary.as_mut().observe_stderr(stdout); + self.secondary.as_mut().observe_stderr(stdout); + } + + /// Runs `observe_stderr` for all stderr observers in the list + fn observe_stderr(&mut self, stderr: &str) { + self.primary.as_mut().observe_stderr(stderr); + self.secondary.as_mut().observe_stderr(stderr); + } } impl MatchName for ProxyObserversTuple diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 5dcd16b1e7..8a6c8ea800 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -33,7 +33,7 @@ use crate::{ executors::{Executor, ExitKind, HasObservers}, inputs::{HasTargetBytes, Input}, mutators::Tokens, - observers::{get_asan_runtime_flags_with_log_path, ASANBacktraceObserver, ObserversTuple}, + observers::{get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, ObserversTuple}, Error, }; @@ -966,13 +966,13 @@ where if self.has_asan_observer.is_none() { self.has_asan_observer = Some( self.observers() - .match_name::("ASANBacktraceObserver") + .match_name::("AsanBacktraceObserver") .is_some(), ); } if self.has_asan_observer.unwrap() { self.observers_mut() - .match_name_mut::("ASANBacktraceObserver") + .match_name_mut::("AsanBacktraceObserver") .unwrap() .parse_asan_output_from_asan_log_file(pid)?; } diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index b73d8fad03..b5e6fad90d 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -85,6 +85,28 @@ pub trait Observer: Named + Debug { ) -> Result<(), Error> { Ok(()) } + + /// If this observer observes `stdout` + #[inline] + fn observes_stdout(&mut self) -> bool { + false + } + /// If this observer observes `stderr` + #[inline] + fn observes_stderr(&mut self) -> bool { + false + } + + /// React to new `stdout` + /// To use this, always return `true` from `observes_stdout` + #[inline] + #[allow(unused_variables)] + fn observe_stdout(&mut self, stdout: &str) {} + /// React to new `stderr` + /// To use this, always return `true` from `observes_stderr` + #[inline] + #[allow(unused_variables)] + fn observe_stderr(&mut self, stderr: &str) {} } /// A haskell-style tuple of observers @@ -110,6 +132,16 @@ pub trait ObserversTuple: MatchName + Debug { input: &I, exit_kind: &ExitKind, ) -> Result<(), Error>; + + /// Returns true if a `stdout` observer was added to the list + fn observes_stdout(&mut self) -> bool; + /// Returns true if a `stderr` observer was added to the list + fn observes_stderr(&mut self) -> bool; + + /// Runs `observe_stdout` for all stdout observers in the list + fn observe_stdout(&mut self, stdout: &str); + /// Runs `observe_stderr` for all stderr observers in the list + fn observe_stderr(&mut self, stderr: &str); } impl ObserversTuple for () { @@ -138,6 +170,26 @@ impl ObserversTuple for () { ) -> Result<(), Error> { Ok(()) } + + /// Returns true if a `stdout` observer was added to the list + #[inline] + fn observes_stdout(&mut self) -> bool { + false + } + /// Returns true if a `stderr` observer was added to the list + #[inline] + fn observes_stderr(&mut self) -> bool { + false + } + + /// Runs `observe_stdout` for all stdout observers in the list + #[inline] + #[allow(unused_variables)] + fn observe_stdout(&mut self, stdout: &str) {} + /// Runs `observe_stderr` for all stderr observers in the list + #[inline] + #[allow(unused_variables)] + fn observe_stderr(&mut self, stderr: &str) {} } impl ObserversTuple for (Head, Tail) @@ -174,9 +226,31 @@ where self.0.post_exec_child(state, input, exit_kind)?; self.1.post_exec_child_all(state, input, exit_kind) } + + /// Returns true if a `stdout` observer was added to the list + #[inline] + fn observes_stdout(&mut self) -> bool { + self.0.observes_stdout() || self.1.observes_stdout() + } + /// Returns true if a `stderr` observer was added to the list + #[inline] + fn observes_stderr(&mut self) -> bool { + self.0.observes_stderr() || self.1.observes_stderr() + } + + /// Runs `observe_stdout` for all stdout observers in the list + #[inline] + fn observe_stdout(&mut self, stdout: &str) { + self.0.observe_stdout(stdout); + } + /// Runs `observe_stderr` for all stderr observers in the list + #[inline] + fn observe_stderr(&mut self, stderr: &str) { + self.1.observe_stderr(stderr); + } } -/// A trait for obervers with a hash field +/// A trait for [`Observer`]`s` with a hash field pub trait ObserverWithHashField { /// get the value of the hash field fn hash(&self) -> &Option; @@ -876,6 +950,23 @@ pub mod pybind { } Ok(()) } + + // TODO: expose stdout/stderr to python + #[inline] + fn observes_stdout(&mut self) -> bool { + false + } + + #[inline] + fn observes_stderr(&mut self) -> bool { + false + } + + #[inline] + fn observe_stderr(&mut self, _: &str) {} + + #[inline] + fn observe_stdout(&mut self, _: &str) {} } impl MatchName for PythonObserversTuple { diff --git a/libafl/src/observers/stacktrace.rs b/libafl/src/observers/stacktrace.rs index 78c7d31a5c..b1365ab6af 100644 --- a/libafl/src/observers/stacktrace.rs +++ b/libafl/src/observers/stacktrace.rs @@ -165,12 +165,12 @@ pub fn get_asan_runtime_flags() -> String { /// An observer looking at the backtrace of target command using ASAN output #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ASANBacktraceObserver { +pub struct AsanBacktraceObserver { observer_name: String, hash: Option, } -impl ASANBacktraceObserver { +impl AsanBacktraceObserver { /// Creates a new [`BacktraceObserver`] with the given name. #[must_use] pub fn new(observer_name: &str) -> Self { @@ -216,7 +216,7 @@ impl ASANBacktraceObserver { } } -impl ObserverWithHashField for ASANBacktraceObserver { +impl ObserverWithHashField for AsanBacktraceObserver { /// Gets the hash value of this observer. #[must_use] fn hash(&self) -> &Option { @@ -234,13 +234,13 @@ impl ObserverWithHashField for ASANBacktraceObserver { } } -impl Default for ASANBacktraceObserver { +impl Default for AsanBacktraceObserver { fn default() -> Self { - Self::new("ASANBacktraceObserver") + Self::new("AsanBacktraceObserver") } } -impl Observer for ASANBacktraceObserver +impl Observer for AsanBacktraceObserver where I: Debug, { @@ -256,9 +256,20 @@ where ) -> Result<(), Error> { Ok(()) } + + /// Do nothing on new `stderr` + #[inline] + fn observes_stderr(&mut self) -> bool { + true + } + + /// Do nothing on new `stderr` + fn observe_stderr(&mut self, stderr: &str) { + self.parse_asan_output(stderr); + } } -impl Named for ASANBacktraceObserver { +impl Named for AsanBacktraceObserver { fn name(&self) -> &str { &self.observer_name } diff --git a/libafl/src/observers/stdio.rs b/libafl/src/observers/stdio.rs index fdcadc8a99..074a73d346 100644 --- a/libafl/src/observers/stdio.rs +++ b/libafl/src/observers/stdio.rs @@ -1,5 +1,5 @@ //! The [`StdOutObserver`] and [`StdErrObserver`] observers look at the stdout of a program -//! The executor must explicitely support these observers. +//! The executor must explicitly support these observers. //! For example, they are supported on the [`crate::executors::CommandExecutor`]. use alloc::string::String; @@ -27,7 +27,16 @@ impl StdOutObserver { } } -impl Observer for StdOutObserver {} +impl Observer for StdOutObserver { + #[inline] + fn observes_stdout(&mut self) -> bool { + true + } + /// React to new `stdout` + fn observe_stdout(&mut self, stdout: &str) { + self.stdout = Some(stdout.into()); + } +} impl Named for StdOutObserver { fn name(&self) -> &str { @@ -54,7 +63,17 @@ impl StdErrObserver { } } -impl Observer for StdErrObserver {} +impl Observer for StdErrObserver { + #[inline] + fn observes_stderr(&mut self) -> bool { + true + } + + /// Do nothing on new `stderr` + fn observe_stderr(&mut self, stderr: &str) { + self.stderr = Some(stderr.into()); + } +} impl Named for StdErrObserver { fn name(&self) -> &str {