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:
parent
556789dffa
commit
0515eebbd2
1
fuzzers/baby_fuzzer_swap_differential/.gitignore
vendored
Normal file
1
fuzzers/baby_fuzzer_swap_differential/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
libpng-*
|
40
fuzzers/baby_fuzzer_swap_differential/Cargo.toml
Normal file
40
fuzzers/baby_fuzzer_swap_differential/Cargo.toml
Normal 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"
|
45
fuzzers/baby_fuzzer_swap_differential/Makefile.toml
Normal file
45
fuzzers/baby_fuzzer_swap_differential/Makefile.toml
Normal 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
|
||||||
|
'''
|
11
fuzzers/baby_fuzzer_swap_differential/README.md
Normal file
11
fuzzers/baby_fuzzer_swap_differential/README.md
Normal 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`
|
45
fuzzers/baby_fuzzer_swap_differential/build.rs
Normal file
45
fuzzers/baby_fuzzer_swap_differential/build.rs
Normal 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(())
|
||||||
|
}
|
12
fuzzers/baby_fuzzer_swap_differential/common.c
Normal file
12
fuzzers/baby_fuzzer_swap_differential/common.c
Normal 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;
|
||||||
|
}
|
13
fuzzers/baby_fuzzer_swap_differential/common.h
Normal file
13
fuzzers/baby_fuzzer_swap_differential/common.h
Normal 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
|
10
fuzzers/baby_fuzzer_swap_differential/first.c
Normal file
10
fuzzers/baby_fuzzer_swap_differential/first.c
Normal 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;
|
||||||
|
}
|
8
fuzzers/baby_fuzzer_swap_differential/first.h
Normal file
8
fuzzers/baby_fuzzer_swap_differential/first.h
Normal 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
|
10
fuzzers/baby_fuzzer_swap_differential/second.c
Normal file
10
fuzzers/baby_fuzzer_swap_differential/second.c
Normal 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;
|
||||||
|
}
|
8
fuzzers/baby_fuzzer_swap_differential/second.h
Normal file
8
fuzzers/baby_fuzzer_swap_differential/second.h
Normal 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
|
35
fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs
Normal file
35
fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
246
fuzzers/baby_fuzzer_swap_differential/src/main.rs
Normal file
246
fuzzers/baby_fuzzer_swap_differential/src/main.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
@ -308,7 +308,7 @@ where
|
|||||||
for handle in &mut handles {
|
for handle in &mut handles {
|
||||||
let ecode = handle.wait()?;
|
let ecode = handle.wait()?;
|
||||||
if !ecode.success() {
|
if !ecode.success() {
|
||||||
println!("Client with handle {:?} exited with {:?}", handle, ecode);
|
println!("Client with handle {handle:?} exited with {ecode:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -600,7 +600,7 @@ mod tests {
|
|||||||
time: _,
|
time: _,
|
||||||
executions: _,
|
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();
|
postcard::from_bytes(observers_buf.as_ref().unwrap()).unwrap();
|
||||||
assert_eq!("test", o.0.name());
|
assert_eq!("test", o.0.name());
|
||||||
}
|
}
|
||||||
|
@ -10,27 +10,28 @@ use crate::{
|
|||||||
bolts::{ownedref::OwnedPtrMut, tuples::MatchName},
|
bolts::{ownedref::OwnedPtrMut, tuples::MatchName},
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
observers::{ObserversTuple, UsesObservers},
|
observers::{DifferentialObserversTuple, ObserversTuple, UsesObservers},
|
||||||
state::UsesState,
|
state::UsesState,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
|
/// A [`DiffExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DiffExecutor<A, B, OTA, OTB> {
|
pub struct DiffExecutor<A, B, OTA, OTB, DOT> {
|
||||||
primary: A,
|
primary: A,
|
||||||
secondary: B,
|
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.
|
/// 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
|
where
|
||||||
A: Executor<EM, Z>,
|
A: UsesState + HasObservers<Observers = OTA>,
|
||||||
B: Executor<EM, Z, State = A::State>,
|
B: UsesState<State = A::State> + HasObservers<Observers = OTB>,
|
||||||
EM: UsesState<State = A::State>,
|
DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
|
||||||
Z: UsesState<State = A::State>,
|
OTA: ObserversTuple<A::State>,
|
||||||
|
OTB: ObserversTuple<A::State>,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
primary,
|
primary,
|
||||||
@ -38,6 +39,7 @@ impl<A, B, OTA, OTB> DiffExecutor<A, B, OTA, OTB> {
|
|||||||
observers: UnsafeCell::new(ProxyObserversTuple {
|
observers: UnsafeCell::new(ProxyObserversTuple {
|
||||||
primary: OwnedPtrMut::Ptr(core::ptr::null_mut()),
|
primary: OwnedPtrMut::Ptr(core::ptr::null_mut()),
|
||||||
secondary: 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
|
where
|
||||||
A: Executor<EM, Z>,
|
A: Executor<EM, Z> + HasObservers,
|
||||||
B: Executor<EM, Z, State = A::State>,
|
B: Executor<EM, Z, State = A::State> + HasObservers,
|
||||||
EM: UsesState<State = A::State>,
|
EM: UsesState<State = A::State>,
|
||||||
OTA: Debug,
|
DOT: DifferentialObserversTuple<A::Observers, B::Observers, A::State>,
|
||||||
OTB: Debug,
|
|
||||||
Z: UsesState<State = A::State>,
|
Z: UsesState<State = A::State>,
|
||||||
{
|
{
|
||||||
fn run_target(
|
fn run_target(
|
||||||
@ -69,10 +70,34 @@ where
|
|||||||
mgr: &mut EM,
|
mgr: &mut EM,
|
||||||
input: &Self::Input,
|
input: &Self::Input,
|
||||||
) -> Result<ExitKind, Error> {
|
) -> 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)?;
|
let ret1 = self.primary.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.primary.post_run_reset();
|
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)?;
|
let ret2 = self.secondary.run_target(fuzzer, state, mgr, input)?;
|
||||||
self.secondary.post_run_reset();
|
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 {
|
if ret1 == ret2 {
|
||||||
Ok(ret1)
|
Ok(ret1)
|
||||||
} else {
|
} else {
|
||||||
@ -88,22 +113,23 @@ where
|
|||||||
/// Proxy the observers of the inner executors
|
/// Proxy the observers of the inner executors
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(
|
#[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>,
|
primary: OwnedPtrMut<A>,
|
||||||
secondary: OwnedPtrMut<B>,
|
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
|
where
|
||||||
A: ObserversTuple<S>,
|
A: ObserversTuple<S>,
|
||||||
B: ObserversTuple<S>,
|
B: ObserversTuple<S>,
|
||||||
|
DOT: DifferentialObserversTuple<A, B, S>,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
{
|
{
|
||||||
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
|
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
|
||||||
self.primary.as_mut().pre_exec_all(state, input)?;
|
self.differential.pre_exec_all(state, input)
|
||||||
self.secondary.as_mut().pre_exec_all(state, input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec_all(
|
fn post_exec_all(
|
||||||
@ -112,17 +138,11 @@ where
|
|||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.primary
|
self.differential.post_exec_all(state, input, exit_kind)
|
||||||
.as_mut()
|
|
||||||
.post_exec_all(state, input, exit_kind)?;
|
|
||||||
self.secondary
|
|
||||||
.as_mut()
|
|
||||||
.post_exec_all(state, input, exit_kind)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
|
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.differential.pre_exec_child_all(state, input)
|
||||||
self.secondary.as_mut().pre_exec_child_all(state, input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_exec_child_all(
|
fn post_exec_child_all(
|
||||||
@ -131,11 +151,7 @@ where
|
|||||||
input: &S::Input,
|
input: &S::Input,
|
||||||
exit_kind: &ExitKind,
|
exit_kind: &ExitKind,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.primary
|
self.differential
|
||||||
.as_mut()
|
|
||||||
.post_exec_child_all(state, input, exit_kind)?;
|
|
||||||
self.secondary
|
|
||||||
.as_mut()
|
|
||||||
.post_exec_child_all(state, input, exit_kind)
|
.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
|
where
|
||||||
A: MatchName,
|
A: MatchName,
|
||||||
B: MatchName,
|
B: MatchName,
|
||||||
|
DOT: MatchName,
|
||||||
{
|
{
|
||||||
fn match_name<T>(&self, name: &str) -> Option<&T> {
|
fn match_name<T>(&self, name: &str) -> Option<&T> {
|
||||||
if let Some(t) = self.primary.as_ref().match_name::<T>(name) {
|
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> {
|
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) {
|
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) {
|
fn set(&mut self, primary: &A, secondary: &B) {
|
||||||
self.primary = OwnedPtrMut::Ptr(primary as *const A as *mut A);
|
self.primary = OwnedPtrMut::Ptr(primary as *const A as *mut A);
|
||||||
self.secondary = OwnedPtrMut::Ptr(secondary as *const B as *mut B);
|
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
|
where
|
||||||
A: HasObservers<Observers = OTA>,
|
A: HasObservers<Observers = OTA>,
|
||||||
B: HasObservers<Observers = OTB, State = A::State>,
|
B: HasObservers<Observers = OTB, State = A::State>,
|
||||||
OTA: ObserversTuple<A::State>,
|
OTA: ObserversTuple<A::State>,
|
||||||
OTB: 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
|
where
|
||||||
A: UsesState,
|
A: UsesState,
|
||||||
B: UsesState<State = A::State>,
|
B: UsesState<State = A::State>,
|
||||||
@ -207,15 +231,16 @@ where
|
|||||||
type State = A::State;
|
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
|
where
|
||||||
A: HasObservers<Observers = OTA>,
|
A: HasObservers<Observers = OTA>,
|
||||||
B: HasObservers<Observers = OTB, State = A::State>,
|
B: HasObservers<Observers = OTB, State = A::State>,
|
||||||
OTA: ObserversTuple<A::State>,
|
OTA: ObserversTuple<A::State>,
|
||||||
OTB: ObserversTuple<A::State>,
|
OTB: ObserversTuple<A::State>,
|
||||||
|
DOT: DifferentialObserversTuple<OTA, OTB, A::State>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB> {
|
fn observers(&self) -> &ProxyObserversTuple<OTA, OTB, DOT> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.observers
|
self.observers
|
||||||
.get()
|
.get()
|
||||||
@ -227,7 +252,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB> {
|
fn observers_mut(&mut self) -> &mut ProxyObserversTuple<OTA, OTB, DOT> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.observers
|
self.observers
|
||||||
.get()
|
.get()
|
||||||
|
@ -25,7 +25,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
observers::Observer,
|
observers::{DifferentialObserver, Observer, ObserversTuple},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ where
|
|||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
pub struct StdMapObserver<'a, T>
|
pub struct StdMapObserver<'a, T, const DIFFERENTIAL: bool>
|
||||||
where
|
where
|
||||||
T: Default + Copy + 'static + Serialize,
|
T: Default + Copy + 'static + Serialize,
|
||||||
{
|
{
|
||||||
@ -199,7 +199,7 @@ where
|
|||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, T> Observer<S> for StdMapObserver<'a, T>
|
impl<'a, S, T> Observer<S> for StdMapObserver<'a, T, false>
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
T: Bounded
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned,
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned,
|
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
|
where
|
||||||
T: Bounded
|
T: Bounded
|
||||||
+ PartialEq
|
+ 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
|
where
|
||||||
T: Bounded
|
T: Bounded
|
||||||
+ PartialEq
|
+ 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
|
where
|
||||||
T: Bounded
|
T: Bounded
|
||||||
+ PartialEq
|
+ 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
|
where
|
||||||
T: Bounded
|
T: Bounded
|
||||||
+ PartialEq
|
+ 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
|
where
|
||||||
T: Bounded
|
T: Bounded
|
||||||
+ PartialEq
|
+ 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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
{
|
{
|
||||||
@ -415,7 +430,7 @@ where
|
|||||||
self.map.as_slice()
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned,
|
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
/// Creates a new [`MapObserver`]
|
/// Creates a new [`MapObserver`]
|
||||||
#[must_use]
|
#[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
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
@ -446,7 +461,7 @@ where
|
|||||||
|
|
||||||
/// Creates a new [`MapObserver`] with an owned map
|
/// Creates a new [`MapObserver`] with an owned map
|
||||||
#[must_use]
|
#[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
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
@ -462,7 +477,7 @@ where
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// Will dereference the owned slice with up to len elements.
|
/// Will dereference the owned slice with up to len elements.
|
||||||
#[must_use]
|
#[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
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
@ -477,7 +492,7 @@ where
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Will dereference the `map_ptr` with up to len elements.
|
/// 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
|
where
|
||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
@ -487,6 +502,124 @@ where
|
|||||||
initial: T::default(),
|
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
|
/// Use a const size to speedup `Feedback::is_interesting` when the user can
|
||||||
@ -1171,6 +1304,7 @@ where
|
|||||||
self.base.as_slice()
|
self.base.as_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> AsMutSlice for HitcountsMapObserver<M>
|
impl<M> AsMutSlice for HitcountsMapObserver<M>
|
||||||
where
|
where
|
||||||
M: MapObserver + AsMutSlice,
|
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
|
/// Map observer with hitcounts postprocessing
|
||||||
/// Less optimized version for non-slice iterators.
|
/// Less optimized version for non-slice iterators.
|
||||||
/// Slice-backed observers should use a [`HitcountsMapObserver`].
|
/// 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
|
/// The Multi Map Observer merge different maps into one observer
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
pub struct MultiMapObserver<'a, T>
|
pub struct MultiMapObserver<'a, T, const DIFFERENTIAL: bool>
|
||||||
where
|
where
|
||||||
T: Default + Copy + 'static + Serialize + Debug,
|
T: Default + Copy + 'static + Serialize + Debug,
|
||||||
{
|
{
|
||||||
@ -1455,7 +1641,7 @@ where
|
|||||||
iter_idx: usize,
|
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
|
where
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
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
|
where
|
||||||
T: Bounded
|
T: Bounded
|
||||||
+ PartialEq
|
+ PartialEq
|
||||||
@ -1589,13 +1784,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> MultiMapObserver<'a, T>
|
impl<'a, T, const DIFFERENTIAL: bool> MultiMapObserver<'a, T, DIFFERENTIAL>
|
||||||
where
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
{
|
{
|
||||||
/// Creates a new [`MultiMapObserver`]
|
/// Creates a new [`MultiMapObserver`], maybe in differential mode
|
||||||
#[must_use]
|
#[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 idx = 0;
|
||||||
let mut v = 0;
|
let mut v = 0;
|
||||||
let mut builder = vec![];
|
let mut builder = vec![];
|
||||||
@ -1619,6 +1814,28 @@ where
|
|||||||
iter_idx: 0,
|
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
|
/// Creates a new [`MultiMapObserver`] with an owned map
|
||||||
#[must_use]
|
#[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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
'a: 'it,
|
'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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
'a: 'it,
|
'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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
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
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
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
|
/// Exact copy of `StdMapObserver` that owns its map
|
||||||
/// Used for python bindings
|
/// Used for python bindings
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
@ -1894,6 +2123,7 @@ where
|
|||||||
self.map.as_slice()
|
self.map.as_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AsMutSlice for OwnedMapObserver<T>
|
impl<T> AsMutSlice for OwnedMapObserver<T>
|
||||||
where
|
where
|
||||||
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
T: Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
@ -1987,7 +2217,7 @@ pub mod pybind {
|
|||||||
/// Python class for StdMapObserver
|
/// Python class for StdMapObserver
|
||||||
pub struct $struct_name1 {
|
pub struct $struct_name1 {
|
||||||
/// Rust wrapped StdMapObserver object
|
/// Rust wrapped StdMapObserver object
|
||||||
pub inner: StdMapObserver<'static, $datatype>,
|
pub inner: StdMapObserver<'static, $datatype, false>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
|
@ -286,6 +286,130 @@ pub trait ObserverWithHashField {
|
|||||||
fn clear_hash(&mut self);
|
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.
|
/// A simple observer, just overlooking the runtime of the target.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct TimeObserver {
|
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.
|
/// A simple observer with a list of things.
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
@ -1204,7 +1336,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
let vec = postcard::to_allocvec(&obv).unwrap();
|
let vec = postcard::to_allocvec(&obv).unwrap();
|
||||||
println!("{vec:?}");
|
println!("{vec:?}");
|
||||||
let obv2: tuple_list_type!(TimeObserver, StdMapObserver<u32>) =
|
let obv2: tuple_list_type!(TimeObserver, StdMapObserver<u32, false>) =
|
||||||
postcard::from_bytes(&vec).unwrap();
|
postcard::from_bytes(&vec).unwrap();
|
||||||
assert_eq!(obv.0.name(), obv2.0.name());
|
assert_eq!(obv.0.name(), obv2.0.name());
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user