#![feature(drain_filter)]
#![feature(never_type)]
#![feature(is_sorted)]
#![feature(link_llvm_intrinsics)]
#![feature(thread_local)]
#![feature(test)]
mod nix_subset;
mod code_coverage_sensor;
mod data_structures;
mod fuzzer;
mod world;
mod pool;
mod signals_handler;
use fuzzcheck_common::arg::{
options_parser, FullCommandLineArguments, COMMAND_FUZZ, COMMAND_MINIFY_CORPUS, COMMAND_MINIFY_INPUT,
CORPUS_SIZE_FLAG, INPUT_FILE_FLAG, IN_CORPUS_FLAG,
};
extern crate fuzzcheck_traits;
use fuzzcheck_traits::*;
use std::borrow::Borrow;
pub fn launch<T, FT, F, M, S>(test: F, mutator: M, serializer: S) -> Result<(), std::io::Error>
where
FT: ?Sized,
T: Clone + Borrow<FT>,
F: Fn(&FT) -> bool,
M: Mutator<T>,
S: Serializer<Value = T>,
fuzzer::Fuzzer<T, FT, F, M, S>: 'static,
{
let env_args: Vec<_> = std::env::args().collect();
let parser = options_parser();
let mut help = format!(
r#""
fuzzcheck <SUBCOMMAND> [OPTIONS]
SUBCOMMANDS:
{fuzz} Run the fuzz test
{tmin} Minify a crashing test input, requires --{input_file}
{cmin} Minify a corpus of test inputs, requires --{in_corpus}
"#,
fuzz = COMMAND_FUZZ,
tmin = COMMAND_MINIFY_INPUT,
input_file = INPUT_FILE_FLAG,
cmin = COMMAND_MINIFY_CORPUS,
in_corpus = IN_CORPUS_FLAG,
);
help += parser.usage("").as_str();
help += format!(
r#""
## Examples:
fuzzcheck {fuzz}
Launch the fuzzer with default options.
fuzzcheck {tmin} --{input_file} "artifacts/crash.json"
Minify the test input defined in the file "artifacts/crash.json".
It will put minified inputs in the folder artifacts/crash.minified/
and name them {{complexity}}-{{hash}}.json.
For example, artifacts/crash.minified/4213--8cd7777109b57b8c.json
is a minified input of complexity 42.13.
fuzzcheck {cmin} --{in_corpus} "fuzz-corpus" --{corpus_size} 25
Minify the corpus defined by the folder "fuzz-corpus", which should
contain JSON-encoded test inputs.
It will remove files from that folder until only the 25 most important
test inputs remain.
"#,
fuzz = COMMAND_FUZZ,
tmin = COMMAND_MINIFY_INPUT,
input_file = INPUT_FILE_FLAG,
cmin = COMMAND_MINIFY_CORPUS,
in_corpus = IN_CORPUS_FLAG,
corpus_size = CORPUS_SIZE_FLAG
)
.as_str();
let args = match FullCommandLineArguments::from_parser(&parser, &env_args[1..]) {
Ok(r) => r,
Err(e) => {
println!("{}\n\n{}", e, help);
std::process::exit(1);
}
};
fuzzer::launch(test, mutator, serializer, args)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Feature(u64);
#[cfg(trace_compares)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct InstrFeatureWithoutTag(u64);
#[cfg(trace_compares)]
impl Feature {
fn from_instr(f: InstrFeatureWithoutTag) -> Self {
Self((f.0 as u64) | (Feature::instr_tag() << Feature::tag_offset()) as u64)
}
}
impl Feature {
fn id_offset() -> u64 {
8
}
fn tag_offset() -> u64 {
62
}
fn indir_tag() -> u64 {
0b01
}
#[cfg(trace_compares)]
fn instr_tag() -> u64 {
0b10
}
fn edge(pc_guard: usize, counter: u16) -> Feature {
let mut feature: u64 = 0;
feature |= ((pc_guard & 0xFFFF_FFFF) as u64) << Feature::id_offset();
feature |= u64::from(Feature::score_from_counter(counter));
Feature(feature)
}
fn erasing_payload(self) -> Self {
if (self.0 >> Self::tag_offset()) == Self::indir_tag() {
self
} else {
Feature(self.0 & 0xFFFF_FFFF_FFFF_FF00)
}
}
fn score_from_counter(counter: u16) -> u8 {
if counter <= 3 {
counter as u8
} else if counter != core::u16::MAX {
(16 - counter.leading_zeros() + 1) as u8
} else {
16
}
}
}
struct FuzzedInput<T: Clone, Mut: Mutator<T>> {
pub value: T,
pub cache: Mut::Cache,
pub mutation_step: Mut::MutationStep,
}
impl<T: Clone, Mut: Mutator<T>> FuzzedInput<T, Mut> {
pub fn new(value: T, cache: Mut::Cache, mutation_step: Mut::MutationStep) -> Self {
Self {
value,
cache,
mutation_step,
}
}
pub fn default(m: &mut Mut) -> Option<Self> {
if let Some((value, cache)) = m.ordered_arbitrary(&mut <_>::default(), 1.0) {
let mutation_step = m.initial_step_from_value(&value);
Some(Self::new(value, cache, mutation_step))
} else {
None
}
}
pub fn new_source(&self, m: &Mut) -> Self {
Self::new(
self.value.clone(),
self.cache.clone(),
m.initial_step_from_value(&self.value),
)
}
pub fn complexity(&self, m: &Mut) -> f64 {
m.complexity(&self.value, &self.cache)
}
pub fn mutate(&mut self, m: &mut Mut, max_cplx: f64) -> Option<Mut::UnmutateToken> {
m.ordered_mutate(&mut self.value, &mut self.cache, &mut self.mutation_step, max_cplx)
}
pub fn unmutate(&mut self, m: &Mut, t: Mut::UnmutateToken) {
m.unmutate(&mut self.value, &mut self.cache, t);
}
}
impl<T: Clone, M: Mutator<T>> Clone for FuzzedInput<T, M> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
cache: self.cache.clone(),
mutation_step: self.mutation_step.clone(),
}
}
}