Differential observers (#868)

* reduce diffexecutor constraints for new (so it may be used in a manager-less environment)

* add differential observers

* finish differential observeration

* requirement for observers (weak), default impl for time observer

* make the map swapper, revisit how differentialobserver is implemented

* semi-specialise multimap, add example

* improve example slightly

* fix clippy lints

* fix last clippy issue

* better docs + example flow

* improve example: correct map sizing + multimap vs split slice

* correct some comments

* fix tests + slight bit more docs

* fix bindings

* fixups for the CI

* typo fix

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dominik Maier <dmnk@google.com>
This commit is contained in:
Addison Crump 2022-11-20 23:56:23 +01:00 committed by GitHub
parent 556789dffa
commit 0515eebbd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1060 additions and 77 deletions

View File

@ -0,0 +1 @@
libpng-*

View File

@ -0,0 +1,40 @@
[package]
name = "baby_fuzzer_swap_differential"
version = "0.7.1"
authors = ["Addison Crump <research@addisoncrump.info>"]
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"

View File

@ -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
'''

View File

@ -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`

View File

@ -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(())
}

View File

@ -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;
}

View File

@ -0,0 +1,13 @@
#ifndef LIBAFL_COMMON_H
#define LIBAFL_COMMON_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define ACCEPT true
#define REJECT false
bool both_require(const uint8_t *bytes, size_t len);
#endif // LIBAFL_COMMON_H

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,35 @@
use std::env;
use libafl_cc::{ClangWrapper, CompilerWrapper};
pub fn main() {
let args: Vec<String> = 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");
}
}

View File

@ -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");
}
}

View File

@ -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:?}");
}
}
}

View File

@ -600,7 +600,7 @@ mod tests {
time: _,
executions: _,
} => {
let o: tuple_list_type!(StdMapObserver::<u32>) =
let o: tuple_list_type!(StdMapObserver::<u32, false>) =
postcard::from_bytes(observers_buf.as_ref().unwrap()).unwrap();
assert_eq!("test", o.0.name());
}

View File

@ -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<A, B, OTA, OTB> {
pub struct DiffExecutor<A, B, OTA, OTB, DOT> {
primary: A,
secondary: B,
observers: UnsafeCell<ProxyObserversTuple<OTA, OTB>>,
observers: UnsafeCell<ProxyObserversTuple<OTA, OTB, DOT>>,
}
impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB> {
impl<A, B, OTA, OTB, DOT> DiffExecutor<A, B, OTA, OTB, DOT> {
/// Create a new `DiffExecutor`, wrapping the given `executor`s.
pub fn new<EM, Z>(primary: A, secondary: B) -> Self
pub fn new(primary: A, secondary: B, observers: DOT) -> Self
where
A: Executor<EM, Z>,
B: Executor<EM, Z, State = A::State>,
EM: UsesState<State = A::State>,
Z: UsesState<State = A::State>,
A: UsesState + HasObservers<Observers = OTA>,
B: UsesState<State = A::State> + HasObservers<Observers = OTB>,
DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
OTA: ObserversTuple<A::State>,
OTB: ObserversTuple<A::State>,
{
Self {
primary,
@ -38,6 +39,7 @@ impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB> {
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<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB> {
}
}
impl<A, B, EM, OTA, OTB, Z> Executor<EM, Z> for DiffExecutor<A, B, OTA, OTB>
impl<A, B, EM, DOT, Z> Executor<EM, Z> for DiffExecutor<A, B, A::Observers, B::Observers, DOT>
where
A: Executor<EM, Z>,
B: Executor<EM, Z, State = A::State>,
A: Executor<EM, Z> + HasObservers,
B: Executor<EM, Z, State = A::State> + HasObservers,
EM: UsesState<State = A::State>,
OTA: Debug,
OTB: Debug,
DOT: DifferentialObserversTuple<A::Observers, B::Observers, A::State>,
Z: UsesState<State = A::State>,
{
fn run_target(
@ -69,10 +70,34 @@ where
mgr: &mut EM,
input: &Self::Input,
) -> Result<ExitKind, Error> {
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<A, B> {
pub struct ProxyObserversTuple<A, B, DOT> {
primary: OwnedPtrMut<A>,
secondary: OwnedPtrMut<B>,
differential: DOT,
}
impl<A, B, S> ObserversTuple<S> for ProxyObserversTuple<A, B>
impl<A, B, DOT, S> ObserversTuple<S> for ProxyObserversTuple<A, B, DOT>
where
A: ObserversTuple<S>,
B: ObserversTuple<S>,
DOT: DifferentialObserversTuple<A, B, S>,
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<A, B> MatchName for ProxyObserversTuple<A, B>
impl<A, B, DOT> MatchName for ProxyObserversTuple<A, B, DOT>
where
A: MatchName,
B: MatchName,
DOT: MatchName,
{
fn match_name<T>(&self, name: &str) -> Option<&T> {
if let Some(t) = self.primary.as_ref().match_name::<T>(name) {
return Some(t);
Some(t)
} else if let Some(t) = self.secondary.as_ref().match_name::<T>(name) {
Some(t)
} else {
self.differential.match_name::<T>(name)
}
self.secondary.as_ref().match_name::<T>(name)
}
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
if let Some(t) = self.primary.as_mut().match_name_mut::<T>(name) {
return Some(t);
Some(t)
} else if let Some(t) = self.secondary.as_mut().match_name_mut::<T>(name) {
Some(t)
} else {
self.differential.match_name_mut::<T>(name)
}
self.secondary.as_mut().match_name_mut::<T>(name)
}
}
impl<A, B> ProxyObserversTuple<A, B> {
impl<A, B, DOT> ProxyObserversTuple<A, B, DOT> {
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<A, B, OTA, OTB> UsesObservers for DiffExecutor<A, B, OTA, OTB>
impl<A, B, OTA, OTB, DOT> UsesObservers for DiffExecutor<A, B, OTA, OTB, DOT>
where
A: HasObservers<Observers = OTA>,
B: HasObservers<Observers = OTB, State = A::State>,
OTA: ObserversTuple<A::State>,
OTB: ObserversTuple<A::State>,
DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
{
type Observers = ProxyObserversTuple<OTA, OTB>;
type Observers = ProxyObserversTuple<OTA, OTB, DOT>;
}
impl<A, B, OTA, OTB> UsesState for DiffExecutor<A, B, OTA, OTB>
impl<A, B, OTA, OTB, DOT> UsesState for DiffExecutor<A, B, OTA, OTB, DOT>
where
A: UsesState,
B: UsesState<State = A::State>,
@ -207,15 +231,16 @@ where
type State = A::State;
}
impl<A, B, OTA, OTB> HasObservers for DiffExecutor<A, B, OTA, OTB>
impl<A, B, OTA, OTB, DOT> HasObservers for DiffExecutor<A, B, OTA, OTB, DOT>
where
A: HasObservers<Observers = OTA>,
B: HasObservers<Observers = OTB, State = A::State>,
OTA: ObserversTuple<A::State>,
OTB: ObserversTuple<A::State>,
DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
{
#[inline]
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB> {
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB, DOT> {
unsafe {
self.observers
.get()
@ -227,7 +252,7 @@ where
}
#[inline]
fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB> {
fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB, DOT> {
unsafe {
self.observers
.get()

View File

@ -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<S> for StdMapObserver<'a, T>
impl<'a, S, T> Observer<S> 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<S> 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<S>(name: S, map: &'a mut [T]) -> Self
fn maybe_differential<S>(name: S, map: &'a mut [T]) -> Self
where
S: Into<String>,
{
@ -446,7 +461,7 @@ where
/// Creates a new [`MapObserver`] with an owned map
#[must_use]
pub fn new_owned<S>(name: S, map: Vec<T>) -> Self
fn maybe_differential_owned<S>(name: S, map: Vec<T>) -> Self
where
S: Into<String>,
{
@ -462,7 +477,7 @@ where
/// # Safety
/// Will dereference the owned slice with up to len elements.
#[must_use]
pub fn new_from_ownedref<S>(name: S, map: OwnedSliceMut<'a, T>) -> Self
fn maybe_differential_from_ownedref<S>(name: S, map: OwnedSliceMut<'a, T>) -> Self
where
S: Into<String>,
{
@ -477,7 +492,7 @@ where
///
/// # Safety
/// Will dereference the `map_ptr` with up to len elements.
pub unsafe fn new_from_ptr<S>(name: S, map_ptr: *mut T, len: usize) -> Self
unsafe fn maybe_differential_from_ptr<S>(name: S, map_ptr: *mut T, len: usize) -> Self
where
S: Into<String>,
{
@ -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<S>(name: S, map: &'a mut [T]) -> Self
where
S: Into<String>,
{
Self::maybe_differential(name, map)
}
/// Creates a new [`MapObserver`] with an owned map
#[must_use]
pub fn new_owned<S>(name: S, map: Vec<T>) -> Self
where
S: Into<String>,
{
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<S>(name: S, map: OwnedSliceMut<'a, T>) -> Self
where
S: Into<String>,
{
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<S>(name: S, map_ptr: *mut T, len: usize) -> Self
where
S: Into<String>,
{
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<S>(name: S, map: &'a mut [T]) -> Self
where
S: Into<String>,
{
Self::maybe_differential(name, map)
}
/// Creates a new [`MapObserver`] with an owned map in differential mode
#[must_use]
pub fn differential_owned<S>(name: S, map: Vec<T>) -> Self
where
S: Into<String>,
{
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<S>(name: S, map: OwnedSliceMut<'a, T>) -> Self
where
S: Into<String>,
{
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<S>(name: S, map_ptr: *mut T, len: usize) -> Self
where
S: Into<String>,
{
Self::maybe_differential_from_ptr(name, map_ptr, len)
}
}
impl<'a, OTA, OTB, S, T> DifferentialObserver<OTA, OTB, S> for StdMapObserver<'a, T, true>
where
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<M> AsMutSlice for HitcountsMapObserver<M>
where
M: MapObserver + AsMutSlice,
@ -1243,6 +1377,33 @@ where
}
}
impl<M, OTA, OTB, S> DifferentialObserver<OTA, OTB, S> for HitcountsMapObserver<M>
where
M: DifferentialObserver<OTA, OTB, S>
+ MapObserver<Entry = u8>
+ Serialize
+ AsMutSlice<Entry = u8>,
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<M, OTA, OTB, S> DifferentialObserver<OTA, OTB, S> for HitcountsIterableMapObserver<M>
where
M: MapObserver<Entry = u8> + Observer<S> + DifferentialObserver<OTA, OTB, S>,
for<'it> M: AsIterMut<'it, Item = u8>,
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<S> for MultiMapObserver<'a, T>
impl<'a, S, T> Observer<S> 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<S> 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<OTA, OTB, S> for MultiMapObserver<'a, T, true>
where
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
Self: MapObserver,
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<T> AsMutSlice for OwnedMapObserver<T>
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]

View File

@ -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<OTA, OTB, S>: Observer<S>
where
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<OTA, OTB, S>: ObserversTuple<S>
where
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<OTA, OTB, S> DifferentialObserversTuple<OTA, OTB, S> for ()
where
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<Head, Tail, OTA, OTB, S> DifferentialObserversTuple<OTA, OTB, S> for (Head, Tail)
where
Head: DifferentialObserver<OTA, OTB, S>,
Tail: DifferentialObserversTuple<OTA, OTB, S>,
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<OTA, OTB, S> DifferentialObserver<OTA, OTB, S> for TimeObserver
where
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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<u32>) =
let obv2: tuple_list_type!(TimeObserver, StdMapObserver<u32, false>) =
postcard::from_bytes(&vec).unwrap();
assert_eq!(obv.0.name(), obv2.0.name());
}

View File

@ -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<const D1: bool, const D2: bool>(
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<S> for DifferentialAFLMapSwapObserver<'a, 'b> where S: UsesInput {}
impl<'a, 'b, OTA, OTB, S> DifferentialObserver<OTA, OTB, S>
for DifferentialAFLMapSwapObserver<'a, 'b>
where
OTA: ObserversTuple<S>,
OTB: ObserversTuple<S>,
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(())
}
}
}