diff --git a/fuzzers/baby_fuzzer_swap_differential/.gitignore b/fuzzers/baby_fuzzer_swap_differential/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_swap_differential/Cargo.toml b/fuzzers/baby_fuzzer_swap_differential/Cargo.toml new file mode 100644 index 0000000000..4bd5b53db9 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "baby_fuzzer_swap_differential" +version = "0.7.1" +authors = ["Addison Crump "] +edition = "2021" +default-run = "fuzzer_sd" + +[features] +tui = [] +multimap = [] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[build-dependencies] +anyhow = "1" +bindgen = "0.61" +cc = "1.0" + +[dependencies] +libafl = { path = "../../libafl" } +libafl_targets = { path = "../../libafl_targets", features = ["sancov_pcguard_hitcounts", "libfuzzer", "sancov_cmplog", "pointer_maps"] } +mimalloc = { version = "*", default-features = false } + +libafl_cc = { path = "../../libafl_cc/" } + +[[bin]] +name = "fuzzer_sd" +path = "src/main.rs" + +[[bin]] +name = "libafl_cc" +path = "src/bin/libafl_cc.rs" diff --git a/fuzzers/baby_fuzzer_swap_differential/Makefile.toml b/fuzzers/baby_fuzzer_swap_differential/Makefile.toml new file mode 100644 index 0000000000..36cf1a2e9f --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/Makefile.toml @@ -0,0 +1,45 @@ +# Variables +[env] +FUZZER_NAME='fuzzer_sd' +CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } +LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc' +FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}' +PROJECT_DIR = { script = ["pwd"] } + +# Compilers +[tasks.cc] +command = "cargo" +args = ["build" , "--release", "--bin", "libafl_cc"] + +# Harness +[tasks.fuzzer] +command = "cargo" +args = ["build" , "--release", "--bin", "${FUZZER_NAME}"] +dependencies = [ "cc" ] + +# Run the fuzzer +[tasks.run] +command = "${CARGO_TARGET_DIR}/release/${FUZZER_NAME}" +dependencies = [ "fuzzer" ] + +# Test +[tasks.test] +linux_alias = "test_unix" +mac_alias = "test_unix" +windows_alias = "unsupported" + +[tasks.test_unix] +script_runner = "@shell" +script=''' +timeout 10s ${CARGO_TARGET_DIR}/release/${FUZZER_NAME} +''' +dependencies = [ "fuzzer" ] + +# Clean up +[tasks.clean] +# Disable default `clean` definition +clear = true +script_runner="@shell" +script=''' +cargo clean +''' \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_swap_differential/README.md b/fuzzers/baby_fuzzer_swap_differential/README.md new file mode 100644 index 0000000000..b16ab4611e --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/README.md @@ -0,0 +1,11 @@ +# Baby fuzzer (swap differential) + +This is a minimalistic example about how to create a libafl-based differential fuzzer which swaps out the AFL map during +execution so that both maps may be measured. + +It runs on a single core until an input is discovered which both inputs accept. + +The tested programs are provided in `first.c` and `second.c`. + +You may execute this fuzzer with `cargo make run`. If you prefer to do so manually, you may also simply use +`cargo build --release --bin libafl_cc` followed by `cargo run --release --bin fuzzer_sd` \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_swap_differential/build.rs b/fuzzers/baby_fuzzer_swap_differential/build.rs new file mode 100644 index 0000000000..8a216dc438 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/build.rs @@ -0,0 +1,45 @@ +use std::{env, path::PathBuf, str::FromStr}; + +fn main() -> anyhow::Result<()> { + if env::var("CARGO_BIN_NAME").map_or(true, |v| v != "libafl_cc") { + println!("cargo:rerun-if-changed=./first.h"); + println!("cargo:rerun-if-changed=./first.c"); + println!("cargo:rerun-if-changed=./second.h"); + println!("cargo:rerun-if-changed=./second.c"); + println!("cargo:rerun-if-changed=./common.c"); + + // Configure and generate bindings. + let bindings = bindgen::builder() + .header("first.h") + .header("second.h") + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .generate()?; + + // Write the generated bindings to an output file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); + + let compiler = env::var("CARGO_TARGET_DIR") + .map_or(PathBuf::from_str("target").unwrap(), |v| { + PathBuf::from_str(&v).unwrap() + }) + .join("release/libafl_cc"); + println!("cargo:rerun-if-changed={}", compiler.to_str().unwrap()); + if !compiler.try_exists().unwrap_or(false) { + println!("cargo:warning=Can't find libafl_cc; assuming that we're building it."); + } else { + cc::Build::new() + .compiler(compiler) + .file("first.c") + .file("second.c") + .file("common.c") + .compile("diff-target"); + + println!("cargo:rustc-link-lib=diff-target"); + } + } + + Ok(()) +} diff --git a/fuzzers/baby_fuzzer_swap_differential/common.c b/fuzzers/baby_fuzzer_swap_differential/common.c new file mode 100644 index 0000000000..ca4abefadf --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/common.c @@ -0,0 +1,12 @@ +#include "common.h" + +bool both_require(const uint8_t *bytes, size_t len) { + if (len >= 1 && bytes[0] == 'a') { + if (len >= 2 && bytes[1] == 'b') { + if (len >= 3 && bytes[2] == 'c') { + return ACCEPT; + } + } + } + return REJECT; +} \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_swap_differential/common.h b/fuzzers/baby_fuzzer_swap_differential/common.h new file mode 100644 index 0000000000..d9c23e0752 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/common.h @@ -0,0 +1,13 @@ +#ifndef LIBAFL_COMMON_H +#define LIBAFL_COMMON_H + +#include +#include +#include + +#define ACCEPT true +#define REJECT false + +bool both_require(const uint8_t *bytes, size_t len); + +#endif // LIBAFL_COMMON_H diff --git a/fuzzers/baby_fuzzer_swap_differential/first.c b/fuzzers/baby_fuzzer_swap_differential/first.c new file mode 100644 index 0000000000..f2e1270953 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/first.c @@ -0,0 +1,10 @@ +#include "first.h" + +bool inspect_first(const uint8_t *bytes, size_t len) { + if (both_require(bytes, len)) { + if (len >= 4 && bytes[3] == 'd') { + return ACCEPT; + } + } + return REJECT; +} diff --git a/fuzzers/baby_fuzzer_swap_differential/first.h b/fuzzers/baby_fuzzer_swap_differential/first.h new file mode 100644 index 0000000000..7b6c5a013c --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/first.h @@ -0,0 +1,8 @@ +#ifndef LIBAFL_FIRST_H +#define LIBAFL_FIRST_H + +#include "common.h" + +bool inspect_first(const uint8_t *bytes, size_t len); + +#endif // LIBAFL_FIRST_H diff --git a/fuzzers/baby_fuzzer_swap_differential/second.c b/fuzzers/baby_fuzzer_swap_differential/second.c new file mode 100644 index 0000000000..b05be5a2a3 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/second.c @@ -0,0 +1,10 @@ +#include "second.h" + +bool inspect_second(const uint8_t *bytes, size_t len) { + if (both_require(bytes, len)) { + if (len >= 5 && bytes[4] == 'e') { + return ACCEPT; + } + } + return REJECT; +} \ No newline at end of file diff --git a/fuzzers/baby_fuzzer_swap_differential/second.h b/fuzzers/baby_fuzzer_swap_differential/second.h new file mode 100644 index 0000000000..03f6eaf233 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/second.h @@ -0,0 +1,8 @@ +#ifndef LIBAFL_SECOND_H +#define LIBAFL_SECOND_H + +#include "common.h" + +bool inspect_second(const uint8_t *bytes, size_t len); + +#endif // LIBAFL_SECOND_H diff --git a/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs b/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs new file mode 100644 index 0000000000..a4b4c980b2 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs @@ -0,0 +1,35 @@ +use std::env; + +use libafl_cc::{ClangWrapper, CompilerWrapper}; + +pub fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let mut dir = env::current_exe().unwrap(); + let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ wrapper was called. Expected {:?} to end with c or cxx", dir), + }; + + dir.pop(); + + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) + .parse_args(&args) + .expect("Failed to parse the command line") + .add_arg("-fsanitize-coverage=trace-pc-guard") + .run() + .expect("Failed to run the wrapped compiler") + { + std::process::exit(code); + } + } else { + panic!("LibAFL CC: No Arguments given"); + } +} diff --git a/fuzzers/baby_fuzzer_swap_differential/src/main.rs b/fuzzers/baby_fuzzer_swap_differential/src/main.rs new file mode 100644 index 0000000000..46ca57a743 --- /dev/null +++ b/fuzzers/baby_fuzzer_swap_differential/src/main.rs @@ -0,0 +1,246 @@ +#[cfg(windows)] +use std::ptr::write_volatile; +use std::{ + alloc::{alloc_zeroed, Layout}, + path::PathBuf, +}; + +#[cfg(feature = "tui")] +use libafl::monitors::tui::TuiMonitor; +#[cfg(not(feature = "tui"))] +use libafl::monitors::SimpleMonitor; +use libafl::{ + bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice}, + corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, + events::SimpleEventManager, + executors::{inprocess::InProcessExecutor, DiffExecutor, ExitKind}, + feedbacks::{CrashFeedback, MaxMapFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + generators::RandPrintablesGenerator, + inputs::{BytesInput, HasTargetBytes}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + observers::StdMapObserver, + schedulers::QueueScheduler, + stages::mutational::StdMutationalStage, + state::{HasSolutions, StdState}, +}; +use libafl_targets::{DifferentialAFLMapSwapObserver, MAX_EDGES_NUM}; +use mimalloc::MiMalloc; + +#[global_allocator] +static GLOBAL: MiMalloc = MiMalloc; + +// bindings to the functions defined in the target +mod bindings { + #![allow(non_camel_case_types)] + #![allow(non_upper_case_globals)] + #![allow(unused)] + + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +use bindings::{inspect_first, inspect_second}; + +#[cfg(feature = "multimap")] +mod multimap { + pub use libafl::observers::{HitcountsIterableMapObserver, MultiMapObserver}; + + pub static mut FIRST_EDGES: &'static mut [u8] = &mut []; + pub static mut SECOND_EDGES: &'static mut [u8] = &mut []; + pub static mut COMBINED_EDGES: [&'static mut [u8]; 2] = [&mut [], &mut []]; +} +#[cfg(feature = "multimap")] +use multimap::*; + +#[cfg(not(feature = "multimap"))] +mod slicemap { + pub use libafl::observers::HitcountsMapObserver; + + pub static mut EDGES: &'static mut [u8] = &mut []; +} +#[cfg(not(feature = "multimap"))] +use slicemap::*; + +#[allow(clippy::similar_names)] +pub fn main() { + // The closure that we want to fuzz + let mut first_harness = |input: &BytesInput| { + let target = input.target_bytes(); + let buf = target.as_slice(); + if unsafe { inspect_first(buf.as_ptr(), buf.len()) } { + ExitKind::Crash + } else { + ExitKind::Ok + } + }; + let mut second_harness = |input: &BytesInput| { + let target = input.target_bytes(); + let buf = target.as_slice(); + if unsafe { inspect_second(buf.as_ptr(), buf.len()) } { + ExitKind::Crash + } else { + ExitKind::Ok + } + }; + + #[cfg(feature = "multimap")] + let (first_map_observer, second_map_observer, map_swapper, map_observer) = { + // initialize the maps + unsafe { + let layout = Layout::from_size_align(MAX_EDGES_NUM, 64).unwrap(); + FIRST_EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM); + SECOND_EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM); + COMBINED_EDGES = [&mut FIRST_EDGES, &mut SECOND_EDGES]; + } + + // create the base maps used to observe the different executors from two independent maps + let mut first_map_observer = StdMapObserver::new("first-edges", unsafe { FIRST_EDGES }); + let mut second_map_observer = StdMapObserver::new("second-edges", unsafe { SECOND_EDGES }); + + // create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!) + let map_swapper = + DifferentialAFLMapSwapObserver::new(&mut first_map_observer, &mut second_map_observer); + + // create a combined map observer, e.g. for calibration + // we use MultiMapObserver::differential to indicate that we want to use the observer in + // differential mode + let map_observer = HitcountsIterableMapObserver::new(MultiMapObserver::differential( + "combined-edges", + unsafe { &mut COMBINED_EDGES }, + )); + ( + first_map_observer, + second_map_observer, + map_swapper, + map_observer, + ) + }; + #[cfg(not(feature = "multimap"))] + let (first_map_observer, second_map_observer, map_swapper, map_observer) = { + // initialize the map + unsafe { + let layout = Layout::from_size_align(MAX_EDGES_NUM * 2, 64).unwrap(); + EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM * 2); + } + + // create the base maps used to observe the different executors by splitting a slice + let mut first_map_observer = + StdMapObserver::new("first-edges", unsafe { &mut EDGES[..MAX_EDGES_NUM] }); + let mut second_map_observer = + StdMapObserver::new("second-edges", unsafe { &mut EDGES[MAX_EDGES_NUM..] }); + + // create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!) + let map_swapper = + DifferentialAFLMapSwapObserver::new(&mut first_map_observer, &mut second_map_observer); + + // create a combined map observer, e.g. for calibration + // we use StdMapObserver::differential to indicate that we want to use the observer in + // differential mode + let map_observer = + HitcountsMapObserver::new(StdMapObserver::differential("combined-edges", unsafe { + EDGES + })); + ( + first_map_observer, + second_map_observer, + map_swapper, + map_observer, + ) + }; + + // Feedback to rate the interestingness of an input + let mut feedback = MaxMapFeedback::new(&map_observer); + + // A feedback to choose if an input is a solution or not + // Crash here means "both crashed", which is our objective + let mut objective = CrashFeedback::new(); + + // create a State from scratch + let mut state = StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(), + // States of the feedbacks. + // The feedbacks can report the data that should persist in the State. + &mut feedback, + // Same for objective feedbacks + &mut objective, + ) + .unwrap(); + + // The Monitor trait define how the fuzzer stats are displayed to the user + #[cfg(not(feature = "tui"))] + let mon = SimpleMonitor::new(|s| println!("{}", s)); + #[cfg(feature = "tui")] + let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false); + + // The event manager handle the various events generated during the fuzzing loop + // 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 testcases from the corpus + let scheduler = QueueScheduler::new(); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + // Create the executor for an in-process function with just one observer + let first_executor = InProcessExecutor::new( + &mut first_harness, + tuple_list!(first_map_observer), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the first executor"); + let second_executor = InProcessExecutor::new( + &mut second_harness, + tuple_list!(second_map_observer), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the second executor"); + + // create the differential executor, providing both the map swapper (which will ensure the + // instrumentation picks the correct map to write to) and the map observer (which provides the + // combined feedback) + let mut differential_executor = DiffExecutor::new( + first_executor, + second_executor, + tuple_list!(map_swapper, map_observer), + ); + + // Generator of printable bytearrays of max size 32 + let mut generator = RandPrintablesGenerator::new(32); + + // Generate 8 initial inputs + state + .generate_initial_inputs( + &mut fuzzer, + &mut differential_executor, + &mut generator, + &mut mgr, + 8, + ) + .expect("Failed to generate the initial corpus"); + + // Setup a mutational stage with a basic bytes mutator + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + while state.solutions().is_empty() { + fuzzer + .fuzz_one( + &mut stages, + &mut differential_executor, + &mut state, + &mut mgr, + ) + .expect("Error in the fuzzing loop"); + } +} diff --git a/libafl/src/bolts/launcher.rs b/libafl/src/bolts/launcher.rs index 94c972824c..4f5e98ded8 100644 --- a/libafl/src/bolts/launcher.rs +++ b/libafl/src/bolts/launcher.rs @@ -308,7 +308,7 @@ where for handle in &mut handles { let ecode = handle.wait()?; if !ecode.success() { - println!("Client with handle {:?} exited with {:?}", handle, ecode); + println!("Client with handle {handle:?} exited with {ecode:?}"); } } } diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 0371db0c0d..6872f0e145 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -600,7 +600,7 @@ mod tests { time: _, executions: _, } => { - let o: tuple_list_type!(StdMapObserver::) = + let o: tuple_list_type!(StdMapObserver::) = postcard::from_bytes(observers_buf.as_ref().unwrap()).unwrap(); assert_eq!("test", o.0.name()); } diff --git a/libafl/src/executors/differential.rs b/libafl/src/executors/differential.rs index f8d2c70143..fc110bb021 100644 --- a/libafl/src/executors/differential.rs +++ b/libafl/src/executors/differential.rs @@ -10,27 +10,28 @@ use crate::{ bolts::{ownedref::OwnedPtrMut, tuples::MatchName}, executors::{Executor, ExitKind, HasObservers}, inputs::UsesInput, - observers::{ObserversTuple, UsesObservers}, + observers::{DifferentialObserversTuple, ObserversTuple, UsesObservers}, state::UsesState, Error, }; /// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one #[derive(Debug)] -pub struct DiffExecutor { +pub struct DiffExecutor { primary: A, secondary: B, - observers: UnsafeCell>, + observers: UnsafeCell>, } -impl DiffExecutor { +impl DiffExecutor { /// Create a new `DiffExecutor`, wrapping the given `executor`s. - pub fn new(primary: A, secondary: B) -> Self + pub fn new(primary: A, secondary: B, observers: DOT) -> Self where - A: Executor, - B: Executor, - EM: UsesState, - Z: UsesState, + A: UsesState + HasObservers, + B: UsesState + HasObservers, + DOT: DifferentialObserversTuple, + OTA: ObserversTuple, + OTB: ObserversTuple, { Self { primary, @@ -38,6 +39,7 @@ impl DiffExecutor { observers: UnsafeCell::new(ProxyObserversTuple { primary: OwnedPtrMut::Ptr(core::ptr::null_mut()), secondary: OwnedPtrMut::Ptr(core::ptr::null_mut()), + differential: observers, }), } } @@ -53,13 +55,12 @@ impl DiffExecutor { } } -impl Executor for DiffExecutor +impl Executor for DiffExecutor where - A: Executor, - B: Executor, + A: Executor + HasObservers, + B: Executor + HasObservers, EM: UsesState, - OTA: Debug, - OTB: Debug, + DOT: DifferentialObserversTuple, Z: UsesState, { fn run_target( @@ -69,10 +70,34 @@ where mgr: &mut EM, input: &Self::Input, ) -> Result { + self.observers(); // update in advance + let observers = self.observers.get_mut(); + observers + .differential + .pre_observe_first_all(observers.primary.as_mut())?; + observers.primary.as_mut().pre_exec_all(state, input)?; let ret1 = self.primary.run_target(fuzzer, state, mgr, input)?; self.primary.post_run_reset(); + observers + .primary + .as_mut() + .post_exec_all(state, input, &ret1)?; + observers + .differential + .post_observe_first_all(observers.primary.as_mut())?; + observers + .differential + .pre_observe_second_all(observers.secondary.as_mut())?; + observers.secondary.as_mut().pre_exec_all(state, input)?; let ret2 = self.secondary.run_target(fuzzer, state, mgr, input)?; self.secondary.post_run_reset(); + observers + .secondary + .as_mut() + .post_exec_all(state, input, &ret2)?; + observers + .differential + .post_observe_second_all(observers.secondary.as_mut())?; if ret1 == ret2 { Ok(ret1) } else { @@ -88,22 +113,23 @@ where /// Proxy the observers of the inner executors #[derive(Serialize, Deserialize, Debug)] #[serde( - bound = "A: serde::Serialize + serde::de::DeserializeOwned, B: serde::Serialize + serde::de::DeserializeOwned" + bound = "A: serde::Serialize + serde::de::DeserializeOwned, B: serde::Serialize + serde::de::DeserializeOwned, DOT: serde::Serialize + serde::de::DeserializeOwned" )] -pub struct ProxyObserversTuple { +pub struct ProxyObserversTuple { primary: OwnedPtrMut, secondary: OwnedPtrMut, + differential: DOT, } -impl ObserversTuple for ProxyObserversTuple +impl ObserversTuple for ProxyObserversTuple where A: ObserversTuple, B: ObserversTuple, + DOT: DifferentialObserversTuple, S: UsesInput, { fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> { - self.primary.as_mut().pre_exec_all(state, input)?; - self.secondary.as_mut().pre_exec_all(state, input) + self.differential.pre_exec_all(state, input) } fn post_exec_all( @@ -112,17 +138,11 @@ where input: &S::Input, exit_kind: &ExitKind, ) -> Result<(), Error> { - self.primary - .as_mut() - .post_exec_all(state, input, exit_kind)?; - self.secondary - .as_mut() - .post_exec_all(state, input, exit_kind) + self.differential.post_exec_all(state, input, exit_kind) } fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> { - self.primary.as_mut().pre_exec_child_all(state, input)?; - self.secondary.as_mut().pre_exec_child_all(state, input) + self.differential.pre_exec_child_all(state, input) } fn post_exec_child_all( @@ -131,11 +151,7 @@ where input: &S::Input, exit_kind: &ExitKind, ) -> Result<(), Error> { - self.primary - .as_mut() - .post_exec_child_all(state, input, exit_kind)?; - self.secondary - .as_mut() + self.differential .post_exec_child_all(state, input, exit_kind) } @@ -163,43 +179,51 @@ where } } -impl MatchName for ProxyObserversTuple +impl MatchName for ProxyObserversTuple where A: MatchName, B: MatchName, + DOT: MatchName, { fn match_name(&self, name: &str) -> Option<&T> { if let Some(t) = self.primary.as_ref().match_name::(name) { - return Some(t); + Some(t) + } else if let Some(t) = self.secondary.as_ref().match_name::(name) { + Some(t) + } else { + self.differential.match_name::(name) } - self.secondary.as_ref().match_name::(name) } fn match_name_mut(&mut self, name: &str) -> Option<&mut T> { if let Some(t) = self.primary.as_mut().match_name_mut::(name) { - return Some(t); + Some(t) + } else if let Some(t) = self.secondary.as_mut().match_name_mut::(name) { + Some(t) + } else { + self.differential.match_name_mut::(name) } - self.secondary.as_mut().match_name_mut::(name) } } -impl ProxyObserversTuple { +impl ProxyObserversTuple { fn set(&mut self, primary: &A, secondary: &B) { self.primary = OwnedPtrMut::Ptr(primary as *const A as *mut A); self.secondary = OwnedPtrMut::Ptr(secondary as *const B as *mut B); } } -impl UsesObservers for DiffExecutor +impl UsesObservers for DiffExecutor where A: HasObservers, B: HasObservers, OTA: ObserversTuple, OTB: ObserversTuple, + DOT: DifferentialObserversTuple, { - type Observers = ProxyObserversTuple; + type Observers = ProxyObserversTuple; } -impl UsesState for DiffExecutor +impl UsesState for DiffExecutor where A: UsesState, B: UsesState, @@ -207,15 +231,16 @@ where type State = A::State; } -impl HasObservers for DiffExecutor +impl HasObservers for DiffExecutor where A: HasObservers, B: HasObservers, OTA: ObserversTuple, OTB: ObserversTuple, + DOT: DifferentialObserversTuple, { #[inline] - fn observers(&self) -> &ProxyObserversTuple { + fn observers(&self) -> &ProxyObserversTuple { unsafe { self.observers .get() @@ -227,7 +252,7 @@ where } #[inline] - fn observers_mut(&mut self) -> &mut ProxyObserversTuple { + fn observers_mut(&mut self) -> &mut ProxyObserversTuple { unsafe { self.observers .get() diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 6d0569bd5d..937e3bc3c9 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -25,7 +25,7 @@ use crate::{ }, executors::ExitKind, inputs::UsesInput, - observers::Observer, + observers::{DifferentialObserver, Observer, ObserversTuple}, Error, }; @@ -190,7 +190,7 @@ where #[derive(Clone, Serialize, Deserialize, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] #[allow(clippy::unsafe_derive_deserialize)] -pub struct StdMapObserver<'a, T> +pub struct StdMapObserver<'a, T, const DIFFERENTIAL: bool> where T: Default + Copy + 'static + Serialize, { @@ -199,7 +199,7 @@ where name: String, } -impl<'a, S, T> Observer for StdMapObserver<'a, T> +impl<'a, S, T> Observer for StdMapObserver<'a, T, false> where S: UsesInput, T: Bounded @@ -217,7 +217,21 @@ where } } -impl<'a, T> Named for StdMapObserver<'a, T> +impl<'a, S, T> Observer for StdMapObserver<'a, T, true> +where + S: UsesInput, + T: Bounded + + PartialEq + + Default + + Copy + + 'static + + Serialize + + serde::de::DeserializeOwned + + Debug, +{ +} + +impl<'a, T, const DIFFERENTIAL: bool> Named for StdMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, { @@ -227,7 +241,7 @@ where } } -impl<'a, T> HasLen for StdMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> HasLen for StdMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, { @@ -237,7 +251,7 @@ where } } -impl<'a, 'it, T> AsIter<'it> for StdMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> AsIter<'it> for StdMapObserver<'a, T, DIFFERENTIAL> where T: Bounded + PartialEq @@ -257,7 +271,7 @@ where } } -impl<'a, 'it, T> AsIterMut<'it> for StdMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> AsIterMut<'it> for StdMapObserver<'a, T, DIFFERENTIAL> where T: Bounded + PartialEq @@ -277,7 +291,7 @@ where } } -impl<'a, 'it, T> IntoIterator for &'it StdMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> IntoIterator for &'it StdMapObserver<'a, T, DIFFERENTIAL> where T: Bounded + PartialEq @@ -297,7 +311,8 @@ where } } -impl<'a, 'it, T> IntoIterator for &'it mut StdMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> IntoIterator + for &'it mut StdMapObserver<'a, T, DIFFERENTIAL> where T: Bounded + PartialEq @@ -317,7 +332,7 @@ where } } -impl<'a, T> MapObserver for StdMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> MapObserver for StdMapObserver<'a, T, DIFFERENTIAL> where T: Bounded + PartialEq @@ -404,7 +419,7 @@ where } } -impl<'a, T> AsSlice for StdMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> AsSlice for StdMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { @@ -415,7 +430,7 @@ where self.map.as_slice() } } -impl<'a, T> AsMutSlice for StdMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> AsMutSlice for StdMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { @@ -427,13 +442,13 @@ where } } -impl<'a, T> StdMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> StdMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, { /// Creates a new [`MapObserver`] #[must_use] - pub fn new(name: S, map: &'a mut [T]) -> Self + fn maybe_differential(name: S, map: &'a mut [T]) -> Self where S: Into, { @@ -446,7 +461,7 @@ where /// Creates a new [`MapObserver`] with an owned map #[must_use] - pub fn new_owned(name: S, map: Vec) -> Self + fn maybe_differential_owned(name: S, map: Vec) -> Self where S: Into, { @@ -462,7 +477,7 @@ where /// # Safety /// Will dereference the owned slice with up to len elements. #[must_use] - pub fn new_from_ownedref(name: S, map: OwnedSliceMut<'a, T>) -> Self + fn maybe_differential_from_ownedref(name: S, map: OwnedSliceMut<'a, T>) -> Self where S: Into, { @@ -477,7 +492,7 @@ where /// /// # Safety /// Will dereference the `map_ptr` with up to len elements. - pub unsafe fn new_from_ptr(name: S, map_ptr: *mut T, len: usize) -> Self + unsafe fn maybe_differential_from_ptr(name: S, map_ptr: *mut T, len: usize) -> Self where S: Into, { @@ -487,6 +502,124 @@ where initial: T::default(), } } + + /// Gets the backing for this map + pub fn map(&self) -> &OwnedSliceMut<'a, T> { + &self.map + } + + /// Gets the backing for this map mutably + pub fn map_mut(&mut self) -> &mut OwnedSliceMut<'a, T> { + &mut self.map + } +} + +impl<'a, T> StdMapObserver<'a, T, false> +where + T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`MapObserver`] + #[must_use] + pub fn new(name: S, map: &'a mut [T]) -> Self + where + S: Into, + { + Self::maybe_differential(name, map) + } + + /// Creates a new [`MapObserver`] with an owned map + #[must_use] + pub fn new_owned(name: S, map: Vec) -> Self + where + S: Into, + { + Self::maybe_differential_owned(name, map) + } + + /// Creates a new [`MapObserver`] from an [`OwnedSliceMut`] map. + /// + /// # Safety + /// Will dereference the owned slice with up to len elements. + #[must_use] + pub fn new_from_ownedref(name: S, map: OwnedSliceMut<'a, T>) -> Self + where + S: Into, + { + Self::maybe_differential_from_ownedref(name, map) + } + + /// Creates a new [`MapObserver`] from a raw pointer + /// + /// # Safety + /// Will dereference the `map_ptr` with up to len elements. + pub unsafe fn new_from_ptr(name: S, map_ptr: *mut T, len: usize) -> Self + where + S: Into, + { + Self::maybe_differential_from_ptr(name, map_ptr, len) + } +} + +impl<'a, T> StdMapObserver<'a, T, true> +where + T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`MapObserver`] in differential mode + #[must_use] + pub fn differential(name: S, map: &'a mut [T]) -> Self + where + S: Into, + { + Self::maybe_differential(name, map) + } + + /// Creates a new [`MapObserver`] with an owned map in differential mode + #[must_use] + pub fn differential_owned(name: S, map: Vec) -> Self + where + S: Into, + { + Self::maybe_differential_owned(name, map) + } + + /// Creates a new [`MapObserver`] from an [`OwnedSliceMut`] map in differential mode. + /// + /// # Safety + /// Will dereference the owned slice with up to len elements. + #[must_use] + pub fn differential_from_ownedref(name: S, map: OwnedSliceMut<'a, T>) -> Self + where + S: Into, + { + Self::maybe_differential_from_ownedref(name, map) + } + + /// Creates a new [`MapObserver`] from a raw pointer in differential mode + /// + /// # Safety + /// Will dereference the `map_ptr` with up to len elements. + pub unsafe fn differential_from_ptr(name: S, map_ptr: *mut T, len: usize) -> Self + where + S: Into, + { + Self::maybe_differential_from_ptr(name, map_ptr, len) + } +} + +impl<'a, OTA, OTB, S, T> DifferentialObserver for StdMapObserver<'a, T, true> +where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, + T: Bounded + + PartialEq + + Default + + Copy + + 'static + + Serialize + + serde::de::DeserializeOwned + + Debug, +{ } /// Use a const size to speedup `Feedback::is_interesting` when the user can @@ -1171,6 +1304,7 @@ where self.base.as_slice() } } + impl AsMutSlice for HitcountsMapObserver where M: MapObserver + AsMutSlice, @@ -1243,6 +1377,33 @@ where } } +impl DifferentialObserver for HitcountsMapObserver +where + M: DifferentialObserver + + MapObserver + + Serialize + + AsMutSlice, + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ + fn pre_observe_first(&mut self, observers: &mut OTA) -> Result<(), Error> { + self.base.pre_observe_first(observers) + } + + fn post_observe_first(&mut self, observers: &mut OTA) -> Result<(), Error> { + self.base.post_observe_first(observers) + } + + fn pre_observe_second(&mut self, observers: &mut OTB) -> Result<(), Error> { + self.base.pre_observe_second(observers) + } + + fn post_observe_second(&mut self, observers: &mut OTB) -> Result<(), Error> { + self.base.post_observe_second(observers) + } +} + /// Map observer with hitcounts postprocessing /// Less optimized version for non-slice iterators. /// Slice-backed observers should use a [`HitcountsMapObserver`]. @@ -1439,11 +1600,36 @@ where } } +impl DifferentialObserver for HitcountsIterableMapObserver +where + M: MapObserver + Observer + DifferentialObserver, + for<'it> M: AsIterMut<'it, Item = u8>, + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ + fn pre_observe_first(&mut self, observers: &mut OTA) -> Result<(), Error> { + self.base.pre_observe_first(observers) + } + + fn post_observe_first(&mut self, observers: &mut OTA) -> Result<(), Error> { + self.base.post_observe_first(observers) + } + + fn pre_observe_second(&mut self, observers: &mut OTB) -> Result<(), Error> { + self.base.pre_observe_second(observers) + } + + fn post_observe_second(&mut self, observers: &mut OTB) -> Result<(), Error> { + self.base.post_observe_second(observers) + } +} + /// The Multi Map Observer merge different maps into one observer #[derive(Serialize, Deserialize, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] #[allow(clippy::unsafe_derive_deserialize)] -pub struct MultiMapObserver<'a, T> +pub struct MultiMapObserver<'a, T, const DIFFERENTIAL: bool> where T: Default + Copy + 'static + Serialize + Debug, { @@ -1455,7 +1641,7 @@ where iter_idx: usize, } -impl<'a, S, T> Observer for MultiMapObserver<'a, T> +impl<'a, S, T> Observer for MultiMapObserver<'a, T, false> where S: UsesInput, T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, @@ -1467,7 +1653,16 @@ where } } -impl<'a, T> Named for MultiMapObserver<'a, T> +impl<'a, S, T> Observer for MultiMapObserver<'a, T, true> +where + S: UsesInput, + T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + Self: MapObserver, +{ + // in differential mode, we are *not* responsible for resetting the map! +} + +impl<'a, T, const DIFFERENTIAL: bool> Named for MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { @@ -1477,7 +1672,7 @@ where } } -impl<'a, T> HasLen for MultiMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> HasLen for MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { @@ -1487,7 +1682,7 @@ where } } -impl<'a, T> MapObserver for MultiMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> MapObserver for MultiMapObserver<'a, T, DIFFERENTIAL> where T: Bounded + PartialEq @@ -1589,13 +1784,13 @@ where } } -impl<'a, T> MultiMapObserver<'a, T> +impl<'a, T, const DIFFERENTIAL: bool> MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { - /// Creates a new [`MultiMapObserver`] + /// Creates a new [`MultiMapObserver`], maybe in differential mode #[must_use] - pub fn new(name: &'static str, maps: &'a mut [&'a mut [T]]) -> Self { + fn new_maybe_differential(name: &'static str, maps: &'a mut [&'a mut [T]]) -> Self { let mut idx = 0; let mut v = 0; let mut builder = vec![]; @@ -1619,6 +1814,28 @@ where iter_idx: 0, } } +} + +impl<'a, T> MultiMapObserver<'a, T, true> +where + T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + /// Creates a new [`MultiMapObserver`] in differential mode + #[must_use] + pub fn differential(name: &'static str, maps: &'a mut [&'a mut [T]]) -> Self { + Self::new_maybe_differential(name, maps) + } +} + +impl<'a, T> MultiMapObserver<'a, T, false> +where + T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + /// Creates a new [`MultiMapObserver`] + #[must_use] + pub fn new(name: &'static str, maps: &'a mut [&'a mut [T]]) -> Self { + Self::new_maybe_differential(name, maps) + } /// Creates a new [`MultiMapObserver`] with an owned map #[must_use] @@ -1648,7 +1865,7 @@ where } } -impl<'a, 'it, T> AsIter<'it> for MultiMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> AsIter<'it> for MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, 'a: 'it, @@ -1661,7 +1878,7 @@ where } } -impl<'a, 'it, T> AsIterMut<'it> for MultiMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> AsIterMut<'it> for MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, 'a: 'it, @@ -1674,7 +1891,8 @@ where } } -impl<'a, 'it, T> IntoIterator for &'it MultiMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> IntoIterator + for &'it MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { @@ -1686,7 +1904,8 @@ where } } -impl<'a, 'it, T> IntoIterator for &'it mut MultiMapObserver<'a, T> +impl<'a, 'it, T, const DIFFERENTIAL: bool> IntoIterator + for &'it mut MultiMapObserver<'a, T, DIFFERENTIAL> where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { @@ -1698,6 +1917,16 @@ where } } +impl<'a, T, OTA, OTB, S> DifferentialObserver for MultiMapObserver<'a, T, true> +where + T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + Self: MapObserver, + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ +} + /// Exact copy of `StdMapObserver` that owns its map /// Used for python bindings #[derive(Serialize, Deserialize, Debug, Clone)] @@ -1894,6 +2123,7 @@ where self.map.as_slice() } } + impl AsMutSlice for OwnedMapObserver where T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, @@ -1987,7 +2217,7 @@ pub mod pybind { /// Python class for StdMapObserver pub struct $struct_name1 { /// Rust wrapped StdMapObserver object - pub inner: StdMapObserver<'static, $datatype>, + pub inner: StdMapObserver<'static, $datatype, false>, } #[pymethods] diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 5920fbe1ce..8e571cc19d 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -286,6 +286,130 @@ pub trait ObserverWithHashField { fn clear_hash(&mut self); } +/// A trait for [`Observer`]`s` which observe over differential execution. +/// +/// Differential observers have the following flow during a single execution: +/// - `Observer::pre_exec` for the differential observer is invoked. +/// - `DifferentialObserver::pre_observe_first` for the differential observer is invoked. +/// - `Observer::pre_exec` for each of the observers for the first executor is invoked. +/// - The first executor is invoked. +/// - `Observer::post_exec` for each of the observers for the first executor is invoked. +/// - `DifferentialObserver::post_observe_first` for the differential observer is invoked. +/// - `DifferentialObserver::pre_observe_second` for the differential observer is invoked. +/// - `Observer::pre_exec` for each of the observers for the second executor is invoked. +/// - The second executor is invoked. +/// - `Observer::post_exec` for each of the observers for the second executor is invoked. +/// - `DifferentialObserver::post_observe_second` for the differential observer is invoked. +/// - `Observer::post_exec` for the differential observer is invoked. +/// +/// You should perform any preparation for the diff execution in `Observer::pre_exec` and respective +/// cleanup in `Observer::post_exec`. For individual executions, use +/// `DifferentialObserver::{pre,post}_observe_{first,second}` as necessary for first and second, +/// respectively. +#[allow(unused_variables)] +pub trait DifferentialObserver: Observer +where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ + /// Perform an operation with the first set of observers before they are `pre_exec`'d. + fn pre_observe_first(&mut self, observers: &mut OTA) -> Result<(), Error> { + Ok(()) + } + + /// Perform an operation with the first set of observers after they are `post_exec`'d. + fn post_observe_first(&mut self, observers: &mut OTA) -> Result<(), Error> { + Ok(()) + } + + /// Perform an operation with the second set of observers before they are `pre_exec`'d. + fn pre_observe_second(&mut self, observers: &mut OTB) -> Result<(), Error> { + Ok(()) + } + + /// Perform an operation with the second set of observers after they are `post_exec`'d. + fn post_observe_second(&mut self, observers: &mut OTB) -> Result<(), Error> { + Ok(()) + } +} + +/// Differential observers tuple, for when you're using multiple differential observers. +pub trait DifferentialObserversTuple: ObserversTuple +where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ + /// Perform an operation with the first set of observers before they are `pre_exec`'d on all the + /// differential observers in this tuple. + fn pre_observe_first_all(&mut self, observers: &mut OTA) -> Result<(), Error>; + + /// Perform an operation with the first set of observers after they are `post_exec`'d on all the + /// differential observers in this tuple. + fn post_observe_first_all(&mut self, observers: &mut OTA) -> Result<(), Error>; + + /// Perform an operation with the second set of observers before they are `pre_exec`'d on all + /// the differential observers in this tuple. + fn pre_observe_second_all(&mut self, observers: &mut OTB) -> Result<(), Error>; + + /// Perform an operation with the second set of observers after they are `post_exec`'d on all + /// the differential observers in this tuple. + fn post_observe_second_all(&mut self, observers: &mut OTB) -> Result<(), Error>; +} + +impl DifferentialObserversTuple for () +where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ + fn pre_observe_first_all(&mut self, _: &mut OTA) -> Result<(), Error> { + Ok(()) + } + + fn post_observe_first_all(&mut self, _: &mut OTA) -> Result<(), Error> { + Ok(()) + } + + fn pre_observe_second_all(&mut self, _: &mut OTB) -> Result<(), Error> { + Ok(()) + } + + fn post_observe_second_all(&mut self, _: &mut OTB) -> Result<(), Error> { + Ok(()) + } +} + +impl DifferentialObserversTuple for (Head, Tail) +where + Head: DifferentialObserver, + Tail: DifferentialObserversTuple, + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ + fn pre_observe_first_all(&mut self, observers: &mut OTA) -> Result<(), Error> { + self.0.pre_observe_first(observers)?; + self.1.pre_observe_first_all(observers) + } + + fn post_observe_first_all(&mut self, observers: &mut OTA) -> Result<(), Error> { + self.0.post_observe_first(observers)?; + self.1.post_observe_first_all(observers) + } + + fn pre_observe_second_all(&mut self, observers: &mut OTB) -> Result<(), Error> { + self.0.pre_observe_second(observers)?; + self.1.pre_observe_second_all(observers) + } + + fn post_observe_second_all(&mut self, observers: &mut OTB) -> Result<(), Error> { + self.0.post_observe_second(observers)?; + self.1.post_observe_second_all(observers) + } +} + /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TimeObserver { @@ -339,6 +463,14 @@ impl Named for TimeObserver { } } +impl DifferentialObserver for TimeObserver +where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, +{ +} + /// A simple observer with a list of things. #[derive(Serialize, Deserialize, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] @@ -1204,7 +1336,7 @@ mod tests { ); let vec = postcard::to_allocvec(&obv).unwrap(); println!("{vec:?}"); - let obv2: tuple_list_type!(TimeObserver, StdMapObserver) = + let obv2: tuple_list_type!(TimeObserver, StdMapObserver) = postcard::from_bytes(&vec).unwrap(); assert_eq!(obv.0.name(), obv2.0.name()); } diff --git a/libafl_targets/src/coverage.rs b/libafl_targets/src/coverage.rs index 83efb638c5..25d7123625 100644 --- a/libafl_targets/src/coverage.rs +++ b/libafl_targets/src/coverage.rs @@ -89,3 +89,115 @@ pub fn edges_max_num() -> usize { } } } + +#[cfg(feature = "pointer_maps")] +pub use swap::*; + +#[cfg(feature = "pointer_maps")] +mod swap { + use alloc::string::{String, ToString}; + use core::fmt::Debug; + + use libafl::{ + bolts::{ownedref::OwnedSliceMut, tuples::Named, AsMutSlice}, + inputs::UsesInput, + observers::{DifferentialObserver, Observer, ObserversTuple, StdMapObserver}, + Error, + }; + use serde::{Deserialize, Serialize}; + + use super::{EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE}; + + /// Observer to be used with `DiffExecutor`s when executing a differential target that shares + /// the AFL map in order to swap out the maps (and thus allow for map observing the two targets + /// separately). + #[allow(clippy::unsafe_derive_deserialize)] + #[derive(Debug, Serialize, Deserialize)] + pub struct DifferentialAFLMapSwapObserver<'a, 'b> { + first_map: OwnedSliceMut<'a, u8>, + second_map: OwnedSliceMut<'b, u8>, + first_name: String, + second_name: String, + name: String, + } + + impl<'a, 'b> DifferentialAFLMapSwapObserver<'a, 'b> { + /// Create a new `DifferentialAFLMapSwapObserver`. + pub fn new( + first: &mut StdMapObserver<'a, u8, D1>, + second: &mut StdMapObserver<'b, u8, D2>, + ) -> Self { + Self { + first_name: first.name().to_string(), + second_name: second.name().to_string(), + name: format!("differential_{}_{}", first.name(), second.name()), + first_map: unsafe { + let slice = first.map_mut().as_mut_slice(); + OwnedSliceMut::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()) + }, + second_map: unsafe { + let slice = second.map_mut().as_mut_slice(); + OwnedSliceMut::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()) + }, + } + } + + /// Get the first map + #[must_use] + pub fn first_map(&self) -> &OwnedSliceMut<'a, u8> { + &self.first_map + } + + /// Get the second map + #[must_use] + pub fn second_map(&self) -> &OwnedSliceMut<'b, u8> { + &self.second_map + } + + /// Get the first name + #[must_use] + pub fn first_name(&self) -> &str { + &self.first_name + } + + /// Get the second name + #[must_use] + pub fn second_name(&self) -> &str { + &self.second_name + } + } + + impl<'a, 'b> Named for DifferentialAFLMapSwapObserver<'a, 'b> { + fn name(&self) -> &str { + &self.name + } + } + + impl<'a, 'b, S> Observer for DifferentialAFLMapSwapObserver<'a, 'b> where S: UsesInput {} + + impl<'a, 'b, OTA, OTB, S> DifferentialObserver + for DifferentialAFLMapSwapObserver<'a, 'b> + where + OTA: ObserversTuple, + OTB: ObserversTuple, + S: UsesInput, + { + fn pre_observe_first(&mut self, _: &mut OTA) -> Result<(), Error> { + let slice = self.first_map.as_mut_slice(); + unsafe { + EDGES_MAP_PTR = slice.as_mut_ptr(); + EDGES_MAP_PTR_SIZE = slice.len(); + } + Ok(()) + } + + fn pre_observe_second(&mut self, _: &mut OTB) -> Result<(), Error> { + let slice = self.second_map.as_mut_slice(); + unsafe { + EDGES_MAP_PTR = slice.as_mut_ptr(); + EDGES_MAP_PTR_SIZE = slice.len(); + } + Ok(()) + } + } +}