Forksrv adaptive map size and AFL++ CmpLog support (#896)
* AFL++ cmplog map * map size opt in forkserver * MapObserver::downsize_map and adaptive map size in forkserver * fix fokserver_simple cmd opts * clippy * fuzzbench forkserver with cmplog * delete makefile in fuzzbench forkserver * fuzzbench_forkserver is persistent * ForkserverExecutorBuilder::build_dynamic_map * fix * clippy * fix * fix macos * fix compilation * fix bugs * fixes Co-authored-by: Dominik Maier <domenukk@gmail.com> Co-authored-by: Dominik Maier <dmnk@google.com>
This commit is contained in:
parent
b33839708e
commit
7b0039606b
5
.github/workflows/build_and_test.yml
vendored
5
.github/workflows/build_and_test.yml
vendored
@ -153,8 +153,6 @@ jobs:
|
|||||||
- name: set mold linker as default linker
|
- name: set mold linker as default linker
|
||||||
if: runner.os == 'Linux' # mold only support linux until now
|
if: runner.os == 'Linux' # mold only support linux until now
|
||||||
uses: rui314/setup-mold@v1
|
uses: rui314/setup-mold@v1
|
||||||
- name: enable mult-thread for `make`
|
|
||||||
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
|
|
||||||
- name: Add nightly rustfmt and clippy
|
- name: Add nightly rustfmt and clippy
|
||||||
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||||
- name: Add no_std toolchain
|
- name: Add no_std toolchain
|
||||||
@ -170,6 +168,9 @@ jobs:
|
|||||||
macos: llvm libpng nasm coreutils z3 bash
|
macos: llvm libpng nasm coreutils z3 bash
|
||||||
- name: pip install
|
- name: pip install
|
||||||
run: python3 -m pip install msgpack jinja2
|
run: python3 -m pip install msgpack jinja2
|
||||||
|
# Note that nproc needs to have coreutils installed on macOS, so the order of CI commands matters.
|
||||||
|
- name: enable mult-thread for `make`
|
||||||
|
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
|
||||||
- name: install cargo-make
|
- name: install cargo-make
|
||||||
uses: baptiste0928/cargo-install@v1.3.0
|
uses: baptiste0928/cargo-install@v1.3.0
|
||||||
with:
|
with:
|
||||||
|
@ -15,10 +15,10 @@ fn main() {
|
|||||||
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
let cwd = env::current_dir().unwrap().to_string_lossy().to_string();
|
||||||
|
|
||||||
let afl = format!("{}/AFLplusplus", &cwd);
|
let afl = format!("{}/AFLplusplus", &cwd);
|
||||||
let afl_gcc = format!("{}/AFLplusplus/afl-cc", &cwd);
|
let afl_cc = format!("{}/AFLplusplus/afl-cc", &cwd);
|
||||||
|
|
||||||
let afl_path = Path::new(&afl);
|
let afl_path = Path::new(&afl);
|
||||||
let afl_gcc_path = Path::new(&afl_gcc);
|
let afl_cc_path = Path::new(&afl_cc);
|
||||||
|
|
||||||
if !afl_path.is_dir() {
|
if !afl_path.is_dir() {
|
||||||
println!("cargo:warning=AFL++ not found, downloading...");
|
println!("cargo:warning=AFL++ not found, downloading...");
|
||||||
@ -29,19 +29,29 @@ fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !afl_gcc_path.is_file() {
|
if !afl_cc_path.is_file() {
|
||||||
Command::new("make")
|
let mut afl_cc_make = Command::new("make");
|
||||||
.arg("all")
|
afl_cc_make.arg("all").current_dir(afl_path);
|
||||||
.current_dir(&afl_path)
|
if let Ok(llvm_config) = env::var("LLVM_CONFIG") {
|
||||||
.status()
|
if !llvm_config.is_empty() {
|
||||||
.unwrap();
|
afl_cc_make.env("LLVM_CONFIG", llvm_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
afl_cc_make.status().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::new(afl_gcc_path)
|
let mut compile_command = Command::new(afl_cc_path);
|
||||||
.args(&["src/program.c", "-o"])
|
compile_command
|
||||||
.arg(&format!("{}/target/release/program", &cwd))
|
.args(["src/program.c", "-o"])
|
||||||
.status()
|
.arg(&format!("{}/target/release/program", &cwd));
|
||||||
.unwrap();
|
|
||||||
|
if let Ok(llvm_config) = env::var("LLVM_CONFIG") {
|
||||||
|
if !llvm_config.is_empty() {
|
||||||
|
compile_command.env("LLVM_CONFIG", llvm_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_command.status().unwrap();
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=src/");
|
println!("cargo:rerun-if-changed=src/");
|
||||||
|
@ -2,28 +2,27 @@ use core::time::Duration;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{self, Parser};
|
use clap::{self, Parser};
|
||||||
#[cfg(not(target_vendor = "apple"))]
|
|
||||||
use libafl::bolts::shmem::StdShMemProvider;
|
|
||||||
#[cfg(target_vendor = "apple")]
|
|
||||||
use libafl::bolts::shmem::UnixShMemProvider;
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{
|
bolts::{
|
||||||
current_nanos,
|
current_nanos,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, MatchName, Merge},
|
||||||
AsMutSlice,
|
AsMutSlice,
|
||||||
},
|
},
|
||||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
executors::{
|
||||||
|
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
||||||
|
HasObservers,
|
||||||
|
},
|
||||||
feedback_and_fast, feedback_or,
|
feedback_and_fast, feedback_or,
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
monitors::SimpleMonitor,
|
monitors::SimpleMonitor,
|
||||||
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
|
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
|
||||||
observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, MapObserver, StdMapObserver, TimeObserver},
|
||||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, StdState},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
@ -73,7 +72,6 @@ struct Opt {
|
|||||||
name = "arguments",
|
name = "arguments",
|
||||||
num_args(1..),
|
num_args(1..),
|
||||||
allow_hyphen_values = true,
|
allow_hyphen_values = true,
|
||||||
default_value = "[].to_vec()"
|
|
||||||
)]
|
)]
|
||||||
arguments: Vec<String>,
|
arguments: Vec<String>,
|
||||||
|
|
||||||
@ -89,19 +87,15 @@ struct Opt {
|
|||||||
|
|
||||||
#[allow(clippy::similar_names)]
|
#[allow(clippy::similar_names)]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
const MAP_SIZE: usize = 65536;
|
||||||
|
|
||||||
let opt = Opt::parse();
|
let opt = Opt::parse();
|
||||||
|
|
||||||
let corpus_dirs: Vec<PathBuf> = [opt.in_dir].to_vec();
|
let corpus_dirs: Vec<PathBuf> = [opt.in_dir].to_vec();
|
||||||
|
|
||||||
const MAP_SIZE: usize = 65536;
|
// The unix shmem provider supported by AFL++ for shared memory
|
||||||
|
|
||||||
// The default, OS-specific privider for shared memory
|
|
||||||
#[cfg(target_vendor = "apple")]
|
|
||||||
let mut shmem_provider = UnixShMemProvider::new().unwrap();
|
let mut shmem_provider = UnixShMemProvider::new().unwrap();
|
||||||
|
|
||||||
#[cfg(not(target_vendor = "apple"))]
|
|
||||||
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
|
||||||
|
|
||||||
// The coverage map shared between observer and executor
|
// The coverage map shared between observer and executor
|
||||||
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
||||||
// let the forkserver know the shmid
|
// let the forkserver know the shmid
|
||||||
@ -109,10 +103,7 @@ pub fn main() {
|
|||||||
let shmem_buf = shmem.as_mut_slice();
|
let shmem_buf = shmem.as_mut_slice();
|
||||||
|
|
||||||
// Create an observation channel using the signals map
|
// Create an observation channel using the signals map
|
||||||
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
|
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf));
|
||||||
"shared_mem",
|
|
||||||
shmem_buf,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
@ -172,15 +163,24 @@ pub fn main() {
|
|||||||
let args = opt.arguments;
|
let args = opt.arguments;
|
||||||
|
|
||||||
let mut tokens = Tokens::new();
|
let mut tokens = Tokens::new();
|
||||||
let forkserver = ForkserverExecutor::builder()
|
let mut forkserver = ForkserverExecutor::builder()
|
||||||
.program(opt.executable)
|
.program(opt.executable)
|
||||||
.debug_child(debug_child)
|
.debug_child(debug_child)
|
||||||
.shmem_provider(&mut shmem_provider)
|
.shmem_provider(&mut shmem_provider)
|
||||||
.autotokens(&mut tokens)
|
.autotokens(&mut tokens)
|
||||||
.parse_afl_cmdline(args)
|
.parse_afl_cmdline(args)
|
||||||
|
.coverage_map_size(MAP_SIZE)
|
||||||
.build(tuple_list!(time_observer, edges_observer))
|
.build(tuple_list!(time_observer, edges_observer))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
|
||||||
|
forkserver
|
||||||
|
.observers_mut()
|
||||||
|
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
|
||||||
|
.unwrap()
|
||||||
|
.downsize_map(dynamic_map_size);
|
||||||
|
}
|
||||||
|
|
||||||
let mut executor = TimeoutForkserverExecutor::with_signal(
|
let mut executor = TimeoutForkserverExecutor::with_signal(
|
||||||
forkserver,
|
forkserver,
|
||||||
Duration::from_millis(opt.timeout),
|
Duration::from_millis(opt.timeout),
|
||||||
|
2
fuzzers/fuzzbench_forkserver/.gitignore
vendored
Normal file
2
fuzzers/fuzzbench_forkserver/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
libpng-*
|
||||||
|
fuzzer
|
20
fuzzers/fuzzbench_forkserver/Cargo.toml
Normal file
20
fuzzers/fuzzbench_forkserver/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "fuzzbench_forkserver"
|
||||||
|
version = "0.8.2"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = 3
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = { version = "1.0", features = ["parallel"] }
|
||||||
|
which = { version = "4.0.2" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
clap = { version = "4.0", features = ["default"] }
|
||||||
|
nix = "0.25"
|
384
fuzzers/fuzzbench_forkserver/src/main.rs
Normal file
384
fuzzers/fuzzbench_forkserver/src/main.rs
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
use core::{cell::RefCell, time::Duration};
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
io::Write,
|
||||||
|
path::PathBuf,
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
|
use clap::{Arg, ArgAction, Command};
|
||||||
|
use libafl::{
|
||||||
|
bolts::{
|
||||||
|
current_nanos, current_time,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
|
tuples::{tuple_list, Merge},
|
||||||
|
AsMutSlice,
|
||||||
|
},
|
||||||
|
corpus::{Corpus, OnDiskCorpus},
|
||||||
|
events::SimpleEventManager,
|
||||||
|
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
|
||||||
|
feedback_and_fast, feedback_or,
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::BytesInput,
|
||||||
|
monitors::SimpleMonitor,
|
||||||
|
mutators::{
|
||||||
|
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
|
||||||
|
StdMOptMutator, StdScheduledMutator, Tokens,
|
||||||
|
},
|
||||||
|
observers::{AFLCmpMap, HitcountsMapObserver, StdCmpObserver, StdMapObserver, TimeObserver},
|
||||||
|
schedulers::{
|
||||||
|
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||||
|
},
|
||||||
|
stages::{
|
||||||
|
calibrate::CalibrationStage, power::StdPowerMutationalStage, StdMutationalStage,
|
||||||
|
TracingStage,
|
||||||
|
},
|
||||||
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use nix::sys::signal::Signal;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let res = match Command::new(env!("CARGO_PKG_NAME"))
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.author("AFLplusplus team")
|
||||||
|
.about("LibAFL-based fuzzer for Fuzzbench")
|
||||||
|
.arg(
|
||||||
|
Arg::new("out")
|
||||||
|
.short('o')
|
||||||
|
.long("output")
|
||||||
|
.help("The directory to place finds in ('corpus')"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("in")
|
||||||
|
.short('i')
|
||||||
|
.long("input")
|
||||||
|
.help("The directory to read initial inputs from ('seeds')"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("tokens")
|
||||||
|
.short('x')
|
||||||
|
.long("tokens")
|
||||||
|
.help("A file to read tokens from, to be used during fuzzing"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("logfile")
|
||||||
|
.short('l')
|
||||||
|
.long("logfile")
|
||||||
|
.help("Duplicates all output to this file")
|
||||||
|
.default_value("libafl.log"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("timeout")
|
||||||
|
.short('t')
|
||||||
|
.long("timeout")
|
||||||
|
.help("Timeout for each individual execution, in milliseconds")
|
||||||
|
.default_value("1200"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("exec")
|
||||||
|
.help("The instrumented binary we want to fuzz")
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("debug-child")
|
||||||
|
.short('d')
|
||||||
|
.long("debug-child")
|
||||||
|
.help("If not set, the child's stdout and stderror will be redirected to /dev/null")
|
||||||
|
.action(ArgAction::SetTrue),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("signal")
|
||||||
|
.short('s')
|
||||||
|
.long("signal")
|
||||||
|
.help("Signal used to stop child")
|
||||||
|
.default_value("SIGKILL"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("cmplog")
|
||||||
|
.short('c')
|
||||||
|
.long("cmplog")
|
||||||
|
.help("The instrumented binary with cmplog"),
|
||||||
|
)
|
||||||
|
.arg(Arg::new("arguments"))
|
||||||
|
.try_get_matches()
|
||||||
|
{
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}",
|
||||||
|
env::current_exe()
|
||||||
|
.unwrap_or_else(|_| "fuzzer".into())
|
||||||
|
.to_string_lossy(),
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Workdir: {:?}",
|
||||||
|
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
|
||||||
|
let mut out_dir = PathBuf::from(
|
||||||
|
res.get_one::<String>("out")
|
||||||
|
.expect("The --output parameter is missing")
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
if fs::create_dir(&out_dir).is_err() {
|
||||||
|
println!("Out dir at {:?} already exists.", &out_dir);
|
||||||
|
if !out_dir.is_dir() {
|
||||||
|
println!("Out dir at {:?} is not a valid directory!", &out_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut crashes = out_dir.clone();
|
||||||
|
crashes.push("crashes");
|
||||||
|
out_dir.push("queue");
|
||||||
|
|
||||||
|
let in_dir = PathBuf::from(
|
||||||
|
res.get_one::<String>("in")
|
||||||
|
.expect("The --input parameter is missing")
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
if !in_dir.is_dir() {
|
||||||
|
println!("In dir at {:?} is not a valid directory!", &in_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = res.get_one::<String>("tokens").map(PathBuf::from);
|
||||||
|
|
||||||
|
let logfile = PathBuf::from(res.get_one::<String>("logfile").unwrap().to_string());
|
||||||
|
|
||||||
|
let timeout = Duration::from_millis(
|
||||||
|
res.get_one::<String>("timeout")
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
.parse()
|
||||||
|
.expect("Could not parse timeout in milliseconds"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let executable = res
|
||||||
|
.get_one::<String>("exec")
|
||||||
|
.expect("The executable is missing")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let debug_child = res.get_flag("debug-child");
|
||||||
|
|
||||||
|
let signal = str::parse::<Signal>(
|
||||||
|
&res.get_one::<String>("signal")
|
||||||
|
.expect("The --signal parameter is missing")
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let cmplog_exec = res
|
||||||
|
.get_one::<String>("cmplog")
|
||||||
|
.map(std::string::ToString::to_string);
|
||||||
|
|
||||||
|
let arguments = res
|
||||||
|
.get_many::<String>("arguments")
|
||||||
|
.map(|v| v.map(std::string::ToString::to_string).collect::<Vec<_>>())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
fuzz(
|
||||||
|
out_dir,
|
||||||
|
crashes,
|
||||||
|
&in_dir,
|
||||||
|
tokens,
|
||||||
|
&logfile,
|
||||||
|
timeout,
|
||||||
|
executable,
|
||||||
|
debug_child,
|
||||||
|
signal,
|
||||||
|
&cmplog_exec,
|
||||||
|
&arguments,
|
||||||
|
)
|
||||||
|
.expect("An error occurred while fuzzing");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The actual fuzzer
|
||||||
|
fn fuzz(
|
||||||
|
corpus_dir: PathBuf,
|
||||||
|
objective_dir: PathBuf,
|
||||||
|
seed_dir: &PathBuf,
|
||||||
|
tokenfile: Option<PathBuf>,
|
||||||
|
logfile: &PathBuf,
|
||||||
|
timeout: Duration,
|
||||||
|
executable: String,
|
||||||
|
debug_child: bool,
|
||||||
|
signal: Signal,
|
||||||
|
cmplog_exec: &Option<String>,
|
||||||
|
arguments: &[String],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// a large initial map size that should be enough
|
||||||
|
// to house all potential coverage maps for our targets
|
||||||
|
// (we will eventually reduce the used size according to the actual map)
|
||||||
|
const MAP_SIZE: usize = 2_621_440;
|
||||||
|
|
||||||
|
let log = RefCell::new(OpenOptions::new().append(true).create(true).open(logfile)?);
|
||||||
|
|
||||||
|
// 'While the monitor are state, they are usually used in the broker - which is likely never restarted
|
||||||
|
let monitor = SimpleMonitor::new(|s| {
|
||||||
|
println!("{}", s);
|
||||||
|
writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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(monitor);
|
||||||
|
|
||||||
|
// The unix shmem provider for shared memory, to match AFL++'s shared memory at the target side
|
||||||
|
let mut shmem_provider = UnixShMemProvider::new().unwrap();
|
||||||
|
|
||||||
|
// The coverage map shared between observer and executor
|
||||||
|
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
||||||
|
// let the forkserver know the shmid
|
||||||
|
shmem.write_to_env("__AFL_SHM_ID").unwrap();
|
||||||
|
let shmem_buf = shmem.as_mut_slice();
|
||||||
|
|
||||||
|
// Create an observation channel using the hitcounts map of AFL++
|
||||||
|
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf));
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false);
|
||||||
|
|
||||||
|
let calibration = CalibrationStage::new(&map_feedback);
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
map_feedback,
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// A feedback to choose if an input is a solution or not
|
||||||
|
// We want to do the same crash deduplication that AFL does
|
||||||
|
let mut objective = feedback_and_fast!(
|
||||||
|
// Must be a crash
|
||||||
|
CrashFeedback::new(),
|
||||||
|
// Take it onlt if trigger new coverage over crashes
|
||||||
|
MaxMapFeedback::new(&edges_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
OnDiskCorpus::<BytesInput>::new(corpus_dir).unwrap(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(objective_dir).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();
|
||||||
|
|
||||||
|
println!("Let's fuzz :)");
|
||||||
|
|
||||||
|
// Setup a MOPT mutator
|
||||||
|
let mutator = StdMOptMutator::new(
|
||||||
|
&mut state,
|
||||||
|
havoc_mutations().merge(tokens_mutations()),
|
||||||
|
7,
|
||||||
|
5,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let power = StdPowerMutationalStage::new(mutator, &edges_observer);
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
|
||||||
|
PowerSchedule::EXPLORE,
|
||||||
|
));
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
let mut tokens = Tokens::new();
|
||||||
|
let forkserver = ForkserverExecutor::builder()
|
||||||
|
.program(executable)
|
||||||
|
.debug_child(debug_child)
|
||||||
|
.shmem_provider(&mut shmem_provider)
|
||||||
|
.autotokens(&mut tokens)
|
||||||
|
.parse_afl_cmdline(arguments)
|
||||||
|
.coverage_map_size(MAP_SIZE)
|
||||||
|
.is_persistent(true)
|
||||||
|
.build_dynamic_map(edges_observer, tuple_list!(time_observer))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut executor = TimeoutForkserverExecutor::with_signal(forkserver, timeout, signal)
|
||||||
|
.expect("Failed to create the executor.");
|
||||||
|
|
||||||
|
// Read tokens
|
||||||
|
if let Some(tokenfile) = tokenfile {
|
||||||
|
tokens.add_from_file(tokenfile)?;
|
||||||
|
}
|
||||||
|
if !tokens.is_empty() {
|
||||||
|
state.add_metadata(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &seed_dir);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
|
||||||
|
if let Some(exec) = &cmplog_exec {
|
||||||
|
// The cmplog map shared between observer and executor
|
||||||
|
let mut cmplog_shmem = shmem_provider
|
||||||
|
.new_shmem(core::mem::size_of::<AFLCmpMap>())
|
||||||
|
.unwrap();
|
||||||
|
// let the forkserver know the shmid
|
||||||
|
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
||||||
|
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLCmpMap>() };
|
||||||
|
|
||||||
|
let cmplog_observer = StdCmpObserver::new("cmplog", cmpmap, true);
|
||||||
|
|
||||||
|
let cmplog_forkserver = ForkserverExecutor::builder()
|
||||||
|
.program(exec)
|
||||||
|
.debug_child(debug_child)
|
||||||
|
.shmem_provider(&mut shmem_provider)
|
||||||
|
.parse_afl_cmdline(arguments)
|
||||||
|
.is_persistent(true)
|
||||||
|
.build(tuple_list!(cmplog_observer))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let cmplog_executor =
|
||||||
|
TimeoutForkserverExecutor::with_signal(cmplog_forkserver, timeout * 10, signal)
|
||||||
|
.expect("Failed to create the executor.");
|
||||||
|
|
||||||
|
let tracing = TracingStage::new(cmplog_executor);
|
||||||
|
|
||||||
|
// Setup a randomic Input2State stage
|
||||||
|
let i2s =
|
||||||
|
StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
||||||
|
|
||||||
|
// The order of the stages matter!
|
||||||
|
let mut stages = tuple_list!(calibration, tracing, i2s, power);
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
} else {
|
||||||
|
// The order of the stages matter!
|
||||||
|
let mut stages = tuple_list!(calibration, power);
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Never reached
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -61,6 +61,7 @@ erased-serde = { version = "0.3.21", default-features = false, features = ["allo
|
|||||||
postcard = { version = "1.0", features = ["alloc"] } # no_std compatible serde serialization fromat
|
postcard = { version = "1.0", features = ["alloc"] } # no_std compatible serde serialization fromat
|
||||||
bincode = {version = "1.3", optional = true }
|
bincode = {version = "1.3", optional = true }
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
|
c2rust-bitfields = { version = "0.3", features = ["no_std"] }
|
||||||
num_enum = { version = "0.5.7", default-features = false }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
typed-builder = "0.10.0" # Implement the builder pattern at compiletime
|
typed-builder = "0.10.0" # Implement the builder pattern at compiletime
|
||||||
ahash = { version = "0.7", default-features=false } # The hash function already used in hashbrown
|
ahash = { version = "0.7", default-features=false } # The hash function already used in hashbrown
|
||||||
|
@ -23,6 +23,26 @@ pub trait IntoOwned {
|
|||||||
fn into_owned(self) -> Self;
|
fn into_owned(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait to downsize slice references
|
||||||
|
pub trait DownsizeSlice {
|
||||||
|
/// Reduce the size of the slice
|
||||||
|
fn downsize(&mut self, len: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DownsizeSlice for &'a [T] {
|
||||||
|
fn downsize(&mut self, len: usize) {
|
||||||
|
*self = &self[..len];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DownsizeSlice for &'a mut [T] {
|
||||||
|
fn downsize(&mut self, len: usize) {
|
||||||
|
let mut value = core::mem::take(self);
|
||||||
|
value = unsafe { value.get_unchecked_mut(..len) };
|
||||||
|
let _ = core::mem::replace(self, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrap a reference and convert to a [`Box`] on serialize
|
/// Wrap a reference and convert to a [`Box`] on serialize
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum OwnedRef<'a, T>
|
pub enum OwnedRef<'a, T>
|
||||||
@ -239,6 +259,39 @@ impl<'a, T> OwnedSlice<'a, T> {
|
|||||||
inner: OwnedSliceInner::RefRaw(ptr, len),
|
inner: OwnedSliceInner::RefRaw(ptr, len),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Downsize the inner slice or vec returning the old size on success or `None` on failure
|
||||||
|
pub fn downsize(&mut self, new_len: usize) -> Option<usize> {
|
||||||
|
match &mut self.inner {
|
||||||
|
OwnedSliceInner::RefRaw(_rr, len) => {
|
||||||
|
let tmp = *len;
|
||||||
|
if new_len <= tmp {
|
||||||
|
*len = new_len;
|
||||||
|
Some(tmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OwnedSliceInner::Ref(r) => {
|
||||||
|
let tmp = r.len();
|
||||||
|
if new_len <= tmp {
|
||||||
|
r.downsize(new_len);
|
||||||
|
Some(tmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OwnedSliceInner::Owned(v) => {
|
||||||
|
let tmp = v.len();
|
||||||
|
if new_len <= tmp {
|
||||||
|
v.truncate(new_len);
|
||||||
|
Some(tmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'it, T> IntoIterator for &'it OwnedSlice<'a, T> {
|
impl<'a, 'it, T> IntoIterator for &'it OwnedSlice<'a, T> {
|
||||||
@ -429,6 +482,39 @@ impl<'a, T: 'a + Sized> OwnedSliceMut<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Downsize the inner slice or vec returning the old size on success or `None` on failure
|
||||||
|
pub fn downsize(&mut self, new_len: usize) -> Option<usize> {
|
||||||
|
match &mut self.inner {
|
||||||
|
OwnedSliceMutInner::RefRaw(_rr, len) => {
|
||||||
|
let tmp = *len;
|
||||||
|
if new_len <= tmp {
|
||||||
|
*len = new_len;
|
||||||
|
Some(tmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OwnedSliceMutInner::Ref(r) => {
|
||||||
|
let tmp = r.len();
|
||||||
|
if new_len <= tmp {
|
||||||
|
r.downsize(new_len);
|
||||||
|
Some(tmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OwnedSliceMutInner::Owned(v) => {
|
||||||
|
let tmp = v.len();
|
||||||
|
if new_len <= tmp {
|
||||||
|
v.truncate(new_len);
|
||||||
|
Some(tmp)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: Sized> AsSlice for OwnedSliceMut<'a, T> {
|
impl<'a, T: Sized> AsSlice for OwnedSliceMut<'a, T> {
|
||||||
|
@ -27,14 +27,16 @@ use crate::{
|
|||||||
bolts::{
|
bolts::{
|
||||||
fs::{InputFile, INPUTFILE_STD},
|
fs::{InputFile, INPUTFILE_STD},
|
||||||
os::{dup2, pipes::Pipe},
|
os::{dup2, pipes::Pipe},
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
|
tuples::Prepend,
|
||||||
AsMutSlice, AsSlice,
|
AsMutSlice, AsSlice,
|
||||||
},
|
},
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
inputs::{HasTargetBytes, Input, UsesInput},
|
inputs::{HasTargetBytes, Input, UsesInput},
|
||||||
mutators::Tokens,
|
mutators::Tokens,
|
||||||
observers::{
|
observers::{
|
||||||
get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, ObserversTuple, UsesObservers,
|
get_asan_runtime_flags_with_log_path, AsanBacktraceObserver, MapObserver, Observer,
|
||||||
|
ObserversTuple, UsesObservers,
|
||||||
},
|
},
|
||||||
state::UsesState,
|
state::UsesState,
|
||||||
Error,
|
Error,
|
||||||
@ -44,9 +46,23 @@ const FORKSRV_FD: i32 = 198;
|
|||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
const FS_OPT_ENABLED: i32 = 0x80000001_u32 as i32;
|
const FS_OPT_ENABLED: i32 = 0x80000001_u32 as i32;
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
const FS_OPT_MAPSIZE: i32 = 0x40000000_u32 as i32;
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
const FS_OPT_SHDMEM_FUZZ: i32 = 0x01000000_u32 as i32;
|
const FS_OPT_SHDMEM_FUZZ: i32 = 0x01000000_u32 as i32;
|
||||||
#[allow(clippy::cast_possible_wrap)]
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
const FS_OPT_AUTODICT: i32 = 0x10000000_u32 as i32;
|
const FS_OPT_AUTODICT: i32 = 0x10000000_u32 as i32;
|
||||||
|
|
||||||
|
// #[allow(clippy::cast_possible_wrap)]
|
||||||
|
// const FS_OPT_MAX_MAPSIZE: i32 = ((0x00fffffe_u32 >> 1) + 1) as i32; // 8388608
|
||||||
|
const fn fs_opt_get_mapsize(x: i32) -> i32 {
|
||||||
|
((x & 0x00fffffe) >> 1) + 1
|
||||||
|
}
|
||||||
|
/* const fn fs_opt_set_mapsize(x: usize) -> usize {
|
||||||
|
if x <= 1 {
|
||||||
|
if x > FS_OPT_MAX_MAPSIZE { 0 } else { (x - 1) << 1 }
|
||||||
|
} else { 0 }
|
||||||
|
} */
|
||||||
|
|
||||||
/// The length of header bytes which tells shmem size
|
/// The length of header bytes which tells shmem size
|
||||||
const SHMEM_FUZZ_HDR_SIZE: usize = 4;
|
const SHMEM_FUZZ_HDR_SIZE: usize = 4;
|
||||||
const MAX_FILE: usize = 1024 * 1024;
|
const MAX_FILE: usize = 1024 * 1024;
|
||||||
@ -511,6 +527,7 @@ where
|
|||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
/// Cache that indicates if we have a `ASan` observer registered.
|
/// Cache that indicates if we have a `ASan` observer registered.
|
||||||
has_asan_observer: Option<bool>,
|
has_asan_observer: Option<bool>,
|
||||||
|
map_size: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<OT, S, SP> Debug for ForkserverExecutor<OT, S, SP>
|
impl<OT, S, SP> Debug for ForkserverExecutor<OT, S, SP>
|
||||||
@ -531,10 +548,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ForkserverExecutor<(), (), StdShMemProvider> {
|
impl ForkserverExecutor<(), (), UnixShMemProvider> {
|
||||||
/// Builder for `ForkserverExecutor`
|
/// Builder for `ForkserverExecutor`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn builder() -> ForkserverExecutorBuilder<'static, StdShMemProvider> {
|
pub fn builder() -> ForkserverExecutorBuilder<'static, UnixShMemProvider> {
|
||||||
ForkserverExecutorBuilder::new()
|
ForkserverExecutorBuilder::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,7 +559,7 @@ impl ForkserverExecutor<(), (), StdShMemProvider> {
|
|||||||
impl<OT, S, SP> ForkserverExecutor<OT, S, SP>
|
impl<OT, S, SP> ForkserverExecutor<OT, S, SP>
|
||||||
where
|
where
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
S: UsesState,
|
S: UsesInput,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
{
|
{
|
||||||
/// The `target` binary that's going to run.
|
/// The `target` binary that's going to run.
|
||||||
@ -564,6 +581,11 @@ where
|
|||||||
pub fn input_file(&self) -> &InputFile {
|
pub fn input_file(&self) -> &InputFile {
|
||||||
&self.input_file
|
&self.input_file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The coverage map size if specified by the target
|
||||||
|
pub fn coverage_map_size(&self) -> Option<usize> {
|
||||||
|
self.map_size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The builder for `ForkserverExecutor`
|
/// The builder for `ForkserverExecutor`
|
||||||
@ -581,6 +603,8 @@ pub struct ForkserverExecutorBuilder<'a, SP> {
|
|||||||
autotokens: Option<&'a mut Tokens>,
|
autotokens: Option<&'a mut Tokens>,
|
||||||
input_filename: Option<OsString>,
|
input_filename: Option<OsString>,
|
||||||
shmem_provider: Option<&'a mut SP>,
|
shmem_provider: Option<&'a mut SP>,
|
||||||
|
map_size: Option<usize>,
|
||||||
|
real_map_size: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
||||||
@ -592,6 +616,80 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
S::Input: Input + HasTargetBytes,
|
S::Input: Input + HasTargetBytes,
|
||||||
SP: ShMemProvider,
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
let (forkserver, input_file, map) = self.build_helper()?;
|
||||||
|
|
||||||
|
let target = self.program.take().unwrap();
|
||||||
|
println!(
|
||||||
|
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}",
|
||||||
|
target,
|
||||||
|
self.arguments.clone(),
|
||||||
|
self.use_stdin
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(ForkserverExecutor {
|
||||||
|
target,
|
||||||
|
args: self.arguments.clone(),
|
||||||
|
input_file,
|
||||||
|
uses_shmem_testcase: self.uses_shmem_testcase,
|
||||||
|
forkserver,
|
||||||
|
observers,
|
||||||
|
map,
|
||||||
|
phantom: PhantomData,
|
||||||
|
has_asan_observer: None, // initialized on first use
|
||||||
|
map_size: self.map_size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds `ForkserverExecutor` downsizing the coverage map to fit exaclty the AFL++ map size.
|
||||||
|
#[allow(clippy::pedantic)]
|
||||||
|
pub fn build_dynamic_map<MO, OT, S>(
|
||||||
|
&mut self,
|
||||||
|
mut map_observer: MO,
|
||||||
|
other_observers: OT,
|
||||||
|
) -> Result<ForkserverExecutor<(MO, OT), S, SP>, Error>
|
||||||
|
where
|
||||||
|
MO: Observer<S> + MapObserver, // TODO maybe enforce Entry = u8 for the cov map
|
||||||
|
OT: ObserversTuple<S> + Prepend<MO, PreprendResult = OT>,
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input: Input + HasTargetBytes,
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
let (forkserver, input_file, map) = self.build_helper()?;
|
||||||
|
|
||||||
|
let target = self.program.take().unwrap();
|
||||||
|
println!(
|
||||||
|
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}, map_size: {:?}",
|
||||||
|
target,
|
||||||
|
self.arguments.clone(),
|
||||||
|
self.use_stdin,
|
||||||
|
self.map_size
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(dynamic_map_size) = self.map_size {
|
||||||
|
map_observer.downsize_map(dynamic_map_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
let observers: (MO, OT) = other_observers.prepend(map_observer);
|
||||||
|
|
||||||
|
Ok(ForkserverExecutor {
|
||||||
|
target,
|
||||||
|
args: self.arguments.clone(),
|
||||||
|
input_file,
|
||||||
|
uses_shmem_testcase: self.uses_shmem_testcase,
|
||||||
|
forkserver,
|
||||||
|
observers,
|
||||||
|
map,
|
||||||
|
phantom: PhantomData,
|
||||||
|
has_asan_observer: None, // initialized on first use
|
||||||
|
map_size: self.map_size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::pedantic)]
|
||||||
|
fn build_helper(&mut self) -> Result<(Forkserver, InputFile, Option<SP::ShMem>), Error>
|
||||||
|
where
|
||||||
|
SP: ShMemProvider,
|
||||||
{
|
{
|
||||||
let input_filename = match &self.input_filename {
|
let input_filename = match &self.input_filename {
|
||||||
Some(name) => name.clone(),
|
Some(name) => name.clone(),
|
||||||
@ -613,22 +711,18 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (target, mut forkserver) = match &self.program {
|
let mut forkserver = match &self.program {
|
||||||
Some(t) => {
|
Some(t) => Forkserver::new(
|
||||||
let forkserver = Forkserver::new(
|
t.clone(),
|
||||||
t.clone(),
|
self.arguments.clone(),
|
||||||
self.arguments.clone(),
|
self.envs.clone(),
|
||||||
self.envs.clone(),
|
input_file.as_raw_fd(),
|
||||||
input_file.as_raw_fd(),
|
self.use_stdin,
|
||||||
self.use_stdin,
|
0,
|
||||||
0,
|
self.is_persistent,
|
||||||
self.is_persistent,
|
self.is_deferred_frksrv,
|
||||||
self.is_deferred_frksrv,
|
self.debug_child,
|
||||||
self.debug_child,
|
)?,
|
||||||
)?;
|
|
||||||
|
|
||||||
(t.clone(), forkserver)
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
return Err(Error::illegal_argument(
|
return Err(Error::illegal_argument(
|
||||||
"ForkserverExecutorBuilder::build: target file not found".to_string(),
|
"ForkserverExecutorBuilder::build: target file not found".to_string(),
|
||||||
@ -648,7 +742,8 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
// <https://github.com/AFLplusplus/AFLplusplus/blob/147654f8715d237fe45c1657c87b2fe36c4db22a/instrumentation/afl-compiler-rt.o.c#L1026>
|
// <https://github.com/AFLplusplus/AFLplusplus/blob/147654f8715d237fe45c1657c87b2fe36c4db22a/instrumentation/afl-compiler-rt.o.c#L1026>
|
||||||
if status & FS_OPT_ENABLED == FS_OPT_ENABLED
|
if status & FS_OPT_ENABLED == FS_OPT_ENABLED
|
||||||
&& (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ
|
&& (status & FS_OPT_SHDMEM_FUZZ == FS_OPT_SHDMEM_FUZZ
|
||||||
|| status & FS_OPT_AUTODICT == FS_OPT_AUTODICT)
|
|| status & FS_OPT_AUTODICT == FS_OPT_AUTODICT
|
||||||
|
|| status & FS_OPT_MAPSIZE == FS_OPT_MAPSIZE)
|
||||||
{
|
{
|
||||||
let mut send_status = FS_OPT_ENABLED;
|
let mut send_status = FS_OPT_ENABLED;
|
||||||
|
|
||||||
@ -658,9 +753,25 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
self.uses_shmem_testcase = true;
|
self.uses_shmem_testcase = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status & FS_OPT_AUTODICT == FS_OPT_AUTODICT) && self.autotokens.is_some() {
|
if status & FS_OPT_MAPSIZE == FS_OPT_MAPSIZE {
|
||||||
println!("Using AUTODICT feature");
|
let mut map_size = fs_opt_get_mapsize(status);
|
||||||
send_status |= FS_OPT_AUTODICT;
|
// When 0, we assume that map_size was filled by the user or const
|
||||||
|
/* TODO autofill map size from the observer
|
||||||
|
|
||||||
|
if map_size > 0 {
|
||||||
|
self.map_size = Some(map_size as usize);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
self.real_map_size = map_size;
|
||||||
|
if map_size % 64 != 0 {
|
||||||
|
map_size = ((map_size + 63) >> 6) << 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(self.map_size.is_none() || map_size as usize <= self.map_size.unwrap());
|
||||||
|
|
||||||
|
println!("Target MAP SIZE = {:#x}", self.real_map_size);
|
||||||
|
self.map_size = Some(map_size as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
let send_len = forkserver.write_ctl(send_status)?;
|
let send_len = forkserver.write_ctl(send_status)?;
|
||||||
@ -698,24 +809,7 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
println!("Forkserver Options are not available.");
|
println!("Forkserver Options are not available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
Ok((forkserver, input_file, map))
|
||||||
"ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}",
|
|
||||||
target,
|
|
||||||
self.arguments.clone(),
|
|
||||||
self.use_stdin
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(ForkserverExecutor {
|
|
||||||
target,
|
|
||||||
args: self.arguments.clone(),
|
|
||||||
input_file,
|
|
||||||
uses_shmem_testcase: self.uses_shmem_testcase,
|
|
||||||
forkserver,
|
|
||||||
observers,
|
|
||||||
map,
|
|
||||||
phantom: PhantomData,
|
|
||||||
has_asan_observer: None, // initialized on first use
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use autodict?
|
/// Use autodict?
|
||||||
@ -757,26 +851,28 @@ impl<'a, SP> ForkserverExecutorBuilder<'a, SP> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
|
||||||
/// Creates a new `AFL`-style [`ForkserverExecutor`] with the given target, arguments and observers.
|
/// Creates a new `AFL`-style [`ForkserverExecutor`] with the given target, arguments and observers.
|
||||||
/// This is the builder for `ForkserverExecutor`
|
/// This is the builder for `ForkserverExecutor`
|
||||||
/// This Forkserver will attempt to provide inputs over shared mem when `shmem_provider` is given.
|
/// This Forkserver will attempt to provide inputs over shared mem when `shmem_provider` is given.
|
||||||
/// Else this forkserver will try to write the input to `.cur_input` file.
|
/// Else this forkserver will try to write the input to `.cur_input` file.
|
||||||
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
|
/// If `debug_child` is set, the child will print to `stdout`/`stderr`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
pub fn new() -> ForkserverExecutorBuilder<'a, UnixShMemProvider> {
|
||||||
ForkserverExecutorBuilder {
|
ForkserverExecutorBuilder {
|
||||||
program: None,
|
program: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
envs: vec![],
|
envs: vec![],
|
||||||
debug_child: false,
|
debug_child: false,
|
||||||
use_stdin: true,
|
use_stdin: false,
|
||||||
uses_shmem_testcase: false,
|
uses_shmem_testcase: false,
|
||||||
is_persistent: false,
|
is_persistent: false,
|
||||||
is_deferred_frksrv: false,
|
is_deferred_frksrv: false,
|
||||||
autotokens: None,
|
autotokens: None,
|
||||||
input_filename: None,
|
input_filename: None,
|
||||||
shmem_provider: None,
|
shmem_provider: None,
|
||||||
|
map_size: None,
|
||||||
|
real_map_size: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,6 +974,13 @@ impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Call this to set a defauult const coverage map size
|
||||||
|
pub fn coverage_map_size(mut self, size: usize) -> Self {
|
||||||
|
self.map_size = Some(size);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Shmem provider for forkserver's shared memory testcase feature.
|
/// Shmem provider for forkserver's shared memory testcase feature.
|
||||||
pub fn shmem_provider<SP: ShMemProvider>(
|
pub fn shmem_provider<SP: ShMemProvider>(
|
||||||
self,
|
self,
|
||||||
@ -895,11 +998,13 @@ impl<'a> ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
|||||||
autotokens: self.autotokens,
|
autotokens: self.autotokens,
|
||||||
input_filename: self.input_filename,
|
input_filename: self.input_filename,
|
||||||
shmem_provider: Some(shmem_provider),
|
shmem_provider: Some(shmem_provider),
|
||||||
|
map_size: self.map_size,
|
||||||
|
real_map_size: self.real_map_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for ForkserverExecutorBuilder<'a, StdShMemProvider> {
|
impl<'a> Default for ForkserverExecutorBuilder<'a, UnixShMemProvider> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -1111,7 +1216,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{
|
bolts::{
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::tuple_list,
|
tuples::tuple_list,
|
||||||
AsMutSlice,
|
AsMutSlice,
|
||||||
},
|
},
|
||||||
@ -1127,7 +1232,7 @@ mod tests {
|
|||||||
let bin = OsString::from("echo");
|
let bin = OsString::from("echo");
|
||||||
let args = vec![OsString::from("@@")];
|
let args = vec![OsString::from("@@")];
|
||||||
|
|
||||||
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
let mut shmem_provider = UnixShMemProvider::new().unwrap();
|
||||||
|
|
||||||
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
||||||
shmem.write_to_env("__AFL_SHM_ID").unwrap();
|
shmem.write_to_env("__AFL_SHM_ID").unwrap();
|
||||||
|
@ -6,10 +6,12 @@ use alloc::{
|
|||||||
};
|
};
|
||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use c2rust_bitfields::BitfieldStruct;
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{ownedref::OwnedRefMut, tuples::Named, AsMutSlice, AsSlice},
|
bolts::{ownedref::OwnedRefMut, tuples::Named, AsMutSlice, AsSlice},
|
||||||
|
executors::ExitKind,
|
||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
observers::Observer,
|
observers::Observer,
|
||||||
state::HasMetadata,
|
state::HasMetadata,
|
||||||
@ -201,18 +203,19 @@ where
|
|||||||
pub struct StdCmpObserver<'a, CM, S>
|
pub struct StdCmpObserver<'a, CM, S>
|
||||||
where
|
where
|
||||||
CM: CmpMap + Serialize,
|
CM: CmpMap + Serialize,
|
||||||
S: UsesInput,
|
S: UsesInput + HasMetadata,
|
||||||
{
|
{
|
||||||
cmp_map: OwnedRefMut<'a, CM>,
|
cmp_map: OwnedRefMut<'a, CM>,
|
||||||
size: Option<OwnedRefMut<'a, usize>>,
|
size: Option<OwnedRefMut<'a, usize>>,
|
||||||
name: String,
|
name: String,
|
||||||
|
add_meta: bool,
|
||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, CM, S> CmpObserver<CM, S> for StdCmpObserver<'a, CM, S>
|
impl<'a, CM, S> CmpObserver<CM, S> for StdCmpObserver<'a, CM, S>
|
||||||
where
|
where
|
||||||
CM: CmpMap + Serialize + DeserializeOwned,
|
CM: CmpMap + Serialize + DeserializeOwned,
|
||||||
S: UsesInput + Debug,
|
S: UsesInput + Debug + HasMetadata,
|
||||||
{
|
{
|
||||||
/// Get the number of usable cmps (all by default)
|
/// Get the number of usable cmps (all by default)
|
||||||
fn usable_count(&self) -> usize {
|
fn usable_count(&self) -> usize {
|
||||||
@ -234,18 +237,30 @@ where
|
|||||||
impl<'a, CM, S> Observer<S> for StdCmpObserver<'a, CM, S>
|
impl<'a, CM, S> Observer<S> for StdCmpObserver<'a, CM, S>
|
||||||
where
|
where
|
||||||
CM: CmpMap + Serialize + DeserializeOwned,
|
CM: CmpMap + Serialize + DeserializeOwned,
|
||||||
S: UsesInput + Debug,
|
S: UsesInput + Debug + HasMetadata,
|
||||||
{
|
{
|
||||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
self.cmp_map.as_mut().reset()?;
|
self.cmp_map.as_mut().reset()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if self.add_meta {
|
||||||
|
self.add_cmpvalues_meta(state);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, CM, S> Named for StdCmpObserver<'a, CM, S>
|
impl<'a, CM, S> Named for StdCmpObserver<'a, CM, S>
|
||||||
where
|
where
|
||||||
CM: CmpMap + Serialize + DeserializeOwned,
|
CM: CmpMap + Serialize + DeserializeOwned,
|
||||||
S: UsesInput,
|
S: UsesInput + HasMetadata,
|
||||||
{
|
{
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
@ -255,27 +270,244 @@ where
|
|||||||
impl<'a, CM, S> StdCmpObserver<'a, CM, S>
|
impl<'a, CM, S> StdCmpObserver<'a, CM, S>
|
||||||
where
|
where
|
||||||
CM: CmpMap + Serialize + DeserializeOwned,
|
CM: CmpMap + Serialize + DeserializeOwned,
|
||||||
S: UsesInput,
|
S: UsesInput + HasMetadata,
|
||||||
{
|
{
|
||||||
/// Creates a new [`StdCmpObserver`] with the given name and map.
|
/// Creates a new [`StdCmpObserver`] with the given name and map.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(name: &'static str, map: &'a mut CM) -> Self {
|
pub fn new(name: &'static str, map: &'a mut CM, add_meta: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
size: None,
|
size: None,
|
||||||
cmp_map: OwnedRefMut::Ref(map),
|
cmp_map: OwnedRefMut::Ref(map),
|
||||||
|
add_meta,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`StdCmpObserver`] with the given name, map and reference to variable size.
|
/// Creates a new [`StdCmpObserver`] with the given name, map and reference to variable size.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_size(name: &'static str, map: &'a mut CM, size: &'a mut usize) -> Self {
|
pub fn with_size(
|
||||||
|
name: &'static str,
|
||||||
|
map: &'a mut CM,
|
||||||
|
add_meta: bool,
|
||||||
|
size: &'a mut usize,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
size: Some(OwnedRefMut::Ref(size)),
|
size: Some(OwnedRefMut::Ref(size)),
|
||||||
cmp_map: OwnedRefMut::Ref(map),
|
cmp_map: OwnedRefMut::Ref(map),
|
||||||
|
add_meta,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* From AFL++ cmplog.h
|
||||||
|
|
||||||
|
#define CMP_MAP_W 65536
|
||||||
|
#define CMP_MAP_H 32
|
||||||
|
#define CMP_MAP_RTN_H (CMP_MAP_H / 4)
|
||||||
|
|
||||||
|
struct cmp_header {
|
||||||
|
|
||||||
|
unsigned hits : 24;
|
||||||
|
unsigned id : 24;
|
||||||
|
unsigned shape : 5;
|
||||||
|
unsigned type : 2;
|
||||||
|
unsigned attribute : 4;
|
||||||
|
unsigned overflow : 1;
|
||||||
|
unsigned reserved : 4;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct cmp_operands {
|
||||||
|
|
||||||
|
u64 v0;
|
||||||
|
u64 v1;
|
||||||
|
u64 v0_128;
|
||||||
|
u64 v1_128;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct cmpfn_operands {
|
||||||
|
|
||||||
|
u8 v0[31];
|
||||||
|
u8 v0_len;
|
||||||
|
u8 v1[31];
|
||||||
|
u8 v1_len;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
typedef struct cmp_operands cmp_map_list[CMP_MAP_H];
|
||||||
|
|
||||||
|
struct cmp_map {
|
||||||
|
|
||||||
|
struct cmp_header headers[CMP_MAP_W];
|
||||||
|
struct cmp_operands log[CMP_MAP_W][CMP_MAP_H];
|
||||||
|
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The AFL++ `CMP_MAP_W`
|
||||||
|
pub const AFL_CMP_MAP_W: usize = 65536;
|
||||||
|
/// The AFL++ `CMP_MAP_H`
|
||||||
|
pub const AFL_CMP_MAP_H: usize = 32;
|
||||||
|
/// The AFL++ `CMP_MAP_RTN_H`
|
||||||
|
pub const AFL_CMP_MAP_RTN_H: usize = AFL_CMP_MAP_H / 4;
|
||||||
|
|
||||||
|
/// The AFL++ `CMP_TYPE_INS`
|
||||||
|
pub const AFL_CMP_TYPE_INS: u32 = 1;
|
||||||
|
/// The AFL++ `CMP_TYPE_RTN`
|
||||||
|
pub const AFL_CMP_TYPE_RTN: u32 = 2;
|
||||||
|
|
||||||
|
/// The AFL++ `cmp_header` struct
|
||||||
|
#[derive(Debug, Copy, Clone, BitfieldStruct)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct AFLCmpHeader {
|
||||||
|
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")]
|
||||||
|
#[bitfield(name = "id", ty = "u32", bits = "24..=47")]
|
||||||
|
#[bitfield(name = "shape", ty = "u32", bits = "48..=52")]
|
||||||
|
#[bitfield(name = "_type", ty = "u32", bits = "53..=54")]
|
||||||
|
#[bitfield(name = "attribute", ty = "u32", bits = "55..=58")]
|
||||||
|
#[bitfield(name = "overflow", ty = "u32", bits = "59..=59")]
|
||||||
|
#[bitfield(name = "reserved", ty = "u32", bits = "60..=63")]
|
||||||
|
data: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The AFL++ `cmp_operands` struct
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct AFLCmpOperands {
|
||||||
|
v0: u64,
|
||||||
|
v1: u64,
|
||||||
|
v0_128: u64,
|
||||||
|
v1_128: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The AFL++ `cmpfn_operands` struct
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct AFLCmpFnOperands {
|
||||||
|
v0: [u8; 31],
|
||||||
|
v0_len: u8,
|
||||||
|
v1: [u8; 31],
|
||||||
|
v1_len: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A proxy union to avoid casting operands as in AFL++
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub union AFLCmpVals {
|
||||||
|
operands: [[AFLCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W],
|
||||||
|
fn_operands: [[AFLCmpFnOperands; AFL_CMP_MAP_RTN_H]; AFL_CMP_MAP_W],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for AFLCmpVals {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("AFLCmpVals").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The AFL++ `cmp_map` struct, use with `StdCmpObserver`
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct AFLCmpMap {
|
||||||
|
headers: [AFLCmpHeader; AFL_CMP_MAP_W],
|
||||||
|
vals: AFLCmpVals,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for AFLCmpMap {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let slice = unsafe {
|
||||||
|
core::slice::from_raw_parts(
|
||||||
|
(self as *const Self) as *const u8,
|
||||||
|
core::mem::size_of::<Self>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
serializer.serialize_bytes(slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for AFLCmpMap {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let bytes = Vec::<u8>::deserialize(deserializer)?;
|
||||||
|
let map: Self = unsafe { core::ptr::read(bytes.as_ptr() as *const _) };
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpMap for AFLCmpMap {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
AFL_CMP_MAP_W
|
||||||
|
}
|
||||||
|
|
||||||
|
fn executions_for(&self, idx: usize) -> usize {
|
||||||
|
self.headers[idx].hits() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usable_executions_for(&self, idx: usize) -> usize {
|
||||||
|
if self.headers[idx]._type() == AFL_CMP_TYPE_INS {
|
||||||
|
if self.executions_for(idx) < AFL_CMP_MAP_H {
|
||||||
|
self.executions_for(idx)
|
||||||
|
} else {
|
||||||
|
AFL_CMP_MAP_H
|
||||||
|
}
|
||||||
|
} else if self.executions_for(idx) < AFL_CMP_MAP_RTN_H {
|
||||||
|
self.executions_for(idx)
|
||||||
|
} else {
|
||||||
|
AFL_CMP_MAP_RTN_H
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
|
||||||
|
if self.headers[idx]._type() == AFL_CMP_TYPE_INS {
|
||||||
|
unsafe {
|
||||||
|
match self.headers[idx].shape() {
|
||||||
|
0 => Some(CmpValues::U8((
|
||||||
|
self.vals.operands[idx][execution].v0 as u8,
|
||||||
|
self.vals.operands[idx][execution].v1 as u8,
|
||||||
|
))),
|
||||||
|
1 => Some(CmpValues::U16((
|
||||||
|
self.vals.operands[idx][execution].v0 as u16,
|
||||||
|
self.vals.operands[idx][execution].v1 as u16,
|
||||||
|
))),
|
||||||
|
3 => Some(CmpValues::U32((
|
||||||
|
self.vals.operands[idx][execution].v0 as u32,
|
||||||
|
self.vals.operands[idx][execution].v1 as u32,
|
||||||
|
))),
|
||||||
|
7 => Some(CmpValues::U64((
|
||||||
|
self.vals.operands[idx][execution].v0,
|
||||||
|
self.vals.operands[idx][execution].v1,
|
||||||
|
))),
|
||||||
|
// TODO handle 128 bits cmps
|
||||||
|
// other => panic!("Invalid CmpLog shape {}", other),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
Some(CmpValues::Bytes((
|
||||||
|
self.vals.fn_operands[idx][execution].v0
|
||||||
|
[..=(self.headers[idx].shape() as usize)]
|
||||||
|
.to_vec(),
|
||||||
|
self.vals.fn_operands[idx][execution].v1
|
||||||
|
[..=(self.headers[idx].shape() as usize)]
|
||||||
|
.to_vec(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
// For performance, we reset just the headers
|
||||||
|
self.headers = unsafe { core::mem::zeroed() };
|
||||||
|
// self.vals.operands = unsafe { core::mem::zeroed() };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -124,6 +124,12 @@ pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned
|
|||||||
|
|
||||||
/// Get the number of set entries with the specified indexes
|
/// Get the number of set entries with the specified indexes
|
||||||
fn how_many_set(&self, indexes: &[usize]) -> usize;
|
fn how_many_set(&self, indexes: &[usize]) -> usize;
|
||||||
|
|
||||||
|
/// Resize the inner map to be smaller (and thus faster to process)
|
||||||
|
/// It returns Some(old size) on success, None on failure
|
||||||
|
fn downsize_map(&mut self, _new_len: usize) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Simple iterator calling `MapObserver::get`
|
/// A Simple iterator calling `MapObserver::get`
|
||||||
@ -417,6 +423,10 @@ where
|
|||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn downsize_map(&mut self, new_len: usize) -> Option<usize> {
|
||||||
|
self.map.downsize(new_len)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, const DIFFERENTIAL: bool> AsSlice for StdMapObserver<'a, T, DIFFERENTIAL>
|
impl<'a, T, const DIFFERENTIAL: bool> AsSlice for StdMapObserver<'a, T, DIFFERENTIAL>
|
||||||
@ -1292,6 +1302,10 @@ where
|
|||||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||||
self.base.how_many_set(indexes)
|
self.base.how_many_set(indexes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn downsize_map(&mut self, new_len: usize) -> Option<usize> {
|
||||||
|
self.base.downsize_map(new_len)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> AsSlice for HitcountsMapObserver<M>
|
impl<M> AsSlice for HitcountsMapObserver<M>
|
||||||
@ -1516,6 +1530,10 @@ where
|
|||||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||||
self.base.how_many_set(indexes)
|
self.base.how_many_set(indexes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn downsize_map(&mut self, new_len: usize) -> Option<usize> {
|
||||||
|
self.base.downsize_map(new_len)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> AsSlice for HitcountsIterableMapObserver<M>
|
impl<M> AsSlice for HitcountsIterableMapObserver<M>
|
||||||
|
@ -8,7 +8,7 @@ use libafl::{
|
|||||||
current_nanos,
|
current_nanos,
|
||||||
launcher::Launcher,
|
launcher::Launcher,
|
||||||
rands::StdRand,
|
rands::StdRand,
|
||||||
shmem::{ShMem, ShMemProvider, StdShMemProvider},
|
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
|
||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
AsMutSlice,
|
AsMutSlice,
|
||||||
},
|
},
|
||||||
@ -108,7 +108,7 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> {
|
|||||||
crashes.push("crashes");
|
crashes.push("crashes");
|
||||||
out_dir.push("queue");
|
out_dir.push("queue");
|
||||||
|
|
||||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
let shmem_provider = UnixShMemProvider::new().expect("Failed to init shared memory");
|
||||||
let mut shmem_provider_client = shmem_provider.clone();
|
let mut shmem_provider_client = shmem_provider.clone();
|
||||||
|
|
||||||
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
let monitor = MultiMonitor::new(|s| println!("{s}"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user