Add mutator stats method (#40)
* add LogMutation trait * change &self to &mut self * move self.scheduler out of StdFuzzer * reorder generics?, implement post_exec * append metadata to the corresponding testcase in the corpus * turn mutations into Mutators * impl Named for mutations * add LoggerScheduledMutator, add fn get_name() to MutatorTuple * Fix BytesDeleteMutator, and format * remove TupleList bound on Tail * turn TokenInsert, TokenReplace into Mutator, fill havoc_mutations * libfuzzer_libpng * libfuzzer_libpng_cmpalloc * libfuzzer_libmozjpeg * fix tests * fix libfuzzer_libmozjpeg * fix tests * fix LoggerScheduledMutator::mutate * use vec<u8> instead of String * fix post_exec and get_name * fmt * NamedTuple and HasNameIdTuple * always clear mutations log * fix tests * format * remove libafl_targets default features * use vec<string> instead of vec<vec<u8>> * add alloc::string::String * format Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
parent
21b790060d
commit
82f5dad784
@ -13,9 +13,10 @@ use libafl::{
|
||||
events::{setup_restarting_mgr, EventManager},
|
||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::{HasTargetBytes, Input},
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
mutators::token_mutations::Tokens,
|
||||
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
@ -470,12 +471,12 @@ unsafe fn fuzz(
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = HavocBytesMutator::default();
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = FridaInProcessExecutor::new(
|
||||
@ -502,12 +503,7 @@ unsafe fn fuzz(
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
fuzzer.scheduler(),
|
||||
&corpus_dirs,
|
||||
)
|
||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
@ -515,7 +511,7 @@ unsafe fn fuzz(
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
|
@ -10,8 +10,8 @@ use libafl::{
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
mutators::scheduled::HavocBytesMutator,
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
mutators::token_mutations::Tokens,
|
||||
observers::StdMapObserver,
|
||||
stages::mutational::StdMutationalStage,
|
||||
@ -100,11 +100,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = HavocBytesMutator::default();
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
let scheduler = RandCorpusScheduler::new();
|
||||
// A fuzzer with just one stage and a random policy to get testcasess from the corpus
|
||||
let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage));
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
@ -132,12 +133,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
fuzzer.scheduler(),
|
||||
&corpus_dirs,
|
||||
)
|
||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
@ -145,7 +141,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
|
@ -18,7 +18,7 @@ debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_targets = { path = "../../libafl_targets/" }
|
||||
libafl_targets = { path = "../../libafl_targets/", features = ["sancov", "libfuzzer_compatibility"] }
|
||||
# TODO Include it only when building cc
|
||||
libafl_cc = { path = "../../libafl_cc/" }
|
||||
|
||||
|
@ -14,8 +14,9 @@ use libafl::{
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
mutators::token_mutations::Tokens,
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
@ -110,12 +111,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = HavocBytesMutator::default();
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
@ -145,12 +146,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
fuzzer.scheduler(),
|
||||
&corpus_dirs,
|
||||
)
|
||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
@ -158,7 +154,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
|
@ -6,16 +6,14 @@ use std::{env, path::PathBuf};
|
||||
#[cfg(unix)]
|
||||
use libafl::{
|
||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||
corpus::{
|
||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||
QueueCorpusScheduler,
|
||||
},
|
||||
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
mutators::token_mutations::Tokens,
|
||||
observers::{HitcountsMapObserver, StdMapObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasCorpus, HasMetadata, State},
|
||||
stats::SimpleStats,
|
||||
@ -109,7 +107,6 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
|
||||
MaxMapFeedback::new_with_observer(&cmps_observer),
|
||||
MaxMapFeedback::new_with_observer(&allocs_observer),
|
||||
TimeFeedback::new()
|
||||
),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
@ -133,12 +130,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = HavocBytesMutator::default();
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
let scheduler = RandCorpusScheduler::new();
|
||||
// A fuzzer with just one stage and a random policy to get testcasess from the corpus
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
@ -148,14 +145,9 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
|
||||
// Create the executor for an in-process function with just one observer for edge coverage
|
||||
let mut executor = InProcessExecutor::new(
|
||||
"in-process(edges,cmps,allocs)",
|
||||
"in-process(edges)",
|
||||
&mut harness,
|
||||
tuple_list!(
|
||||
edges_observer,
|
||||
cmps_observer,
|
||||
allocs_observer,
|
||||
TimeObserver::new("time")
|
||||
),
|
||||
tuple_list!(edges_observer, cmps_observer, allocs_observer),
|
||||
&mut state,
|
||||
&mut restarting_mgr,
|
||||
)?;
|
||||
@ -171,12 +163,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
fuzzer.scheduler(),
|
||||
&corpus_dirs,
|
||||
)
|
||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
@ -184,7 +171,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
|
@ -2,16 +2,16 @@
|
||||
|
||||
mkdir -p ./crashes
|
||||
|
||||
cargo build --example libfuzzer_libpng --release || exit 1
|
||||
cargo build --example libfuzzer_libpng_cmpalloc --release || exit 1
|
||||
cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf
|
||||
|
||||
# The broker
|
||||
RUST_BACKTRACE=full taskset 0 ./.libfuzzer_test.elf &
|
||||
RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf &
|
||||
# Give the broker time to spawn
|
||||
sleep 2
|
||||
echo "Spawning client"
|
||||
# The 1st fuzzer client, pin to cpu 0x1
|
||||
RUST_BACKTRACE=full taskset 1 ./.libfuzzer_test.elf 2>/dev/null
|
||||
RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf 2>/dev/null
|
||||
|
||||
killall .libfuzzer_test.elf
|
||||
rm -rf ./.libfuzzer_test.elf
|
||||
|
@ -15,7 +15,7 @@ use libafl::{
|
||||
events::setup_restarting_mgr,
|
||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||
stages::mutational::StdMutationalStage,
|
||||
@ -122,12 +122,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
}
|
||||
|
||||
// Setup a basic mutator with a mutational stage
|
||||
let mutator = HavocBytesMutator::default();
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
|
||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage));
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |buf: &[u8]| {
|
||||
@ -159,12 +159,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
// In case the corpus is empty (on first run), reset
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(
|
||||
&mut executor,
|
||||
&mut restarting_mgr,
|
||||
fuzzer.scheduler(),
|
||||
&corpus_dirs,
|
||||
)
|
||||
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||
.expect(&format!(
|
||||
"Failed to load initial corpus at {:?}",
|
||||
&corpus_dirs
|
||||
@ -172,7 +167,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?;
|
||||
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
|
@ -46,7 +46,7 @@ required-features = ["std"]
|
||||
tuple_list = "0.1.2"
|
||||
hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
||||
num = "*"
|
||||
xxhash-rust = { version = "0.8.0", features = ["xxh3"] } # xxh3 hashing for rust
|
||||
xxhash-rust = { version = "0.8.0", features = ["xxh3", "const_xxh3"] } # xxh3 hashing for rust
|
||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||
erased-serde = "0.3.12"
|
||||
postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat
|
||||
|
@ -4,6 +4,8 @@ pub use tuple_list::{tuple_list, tuple_list_type, TupleList};
|
||||
|
||||
use core::any::TypeId;
|
||||
|
||||
use xxhash_rust::const_xxh3::xxh3_64;
|
||||
|
||||
pub trait HasLen {
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool {
|
||||
@ -19,13 +21,59 @@ impl HasLen for () {
|
||||
|
||||
impl<Head, Tail> HasLen for (Head, Tail)
|
||||
where
|
||||
Tail: TupleList + HasLen,
|
||||
Tail: HasLen,
|
||||
{
|
||||
fn len(&self) -> usize {
|
||||
1 + self.1.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasNameId {
|
||||
fn const_name(&self) -> &'static str;
|
||||
|
||||
fn name_id(&self) -> u64 {
|
||||
xxh3_64(self.const_name().as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasNameIdTuple: HasLen {
|
||||
fn get_const_name(&self, index: usize) -> Option<&'static str>;
|
||||
|
||||
fn get_name_id(&self, index: usize) -> Option<u64>;
|
||||
}
|
||||
|
||||
impl HasNameIdTuple for () {
|
||||
fn get_const_name(&self, _index: usize) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_name_id(&self, _index: usize) -> Option<u64> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<Head, Tail> HasNameIdTuple for (Head, Tail)
|
||||
where
|
||||
Head: 'static + HasNameId,
|
||||
Tail: HasNameIdTuple,
|
||||
{
|
||||
fn get_const_name(&self, index: usize) -> Option<&'static str> {
|
||||
if index == 0 {
|
||||
Some(self.0.const_name())
|
||||
} else {
|
||||
self.1.get_const_name(index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_name_id(&self, index: usize) -> Option<u64> {
|
||||
if index == 0 {
|
||||
Some(self.0.name_id())
|
||||
} else {
|
||||
self.1.get_name_id(index - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MatchFirstType {
|
||||
fn match_first_type<T: 'static>(&self) -> Option<&T>;
|
||||
fn match_first_type_mut<T: 'static>(&mut self) -> Option<&mut T>;
|
||||
@ -43,7 +91,7 @@ impl MatchFirstType for () {
|
||||
impl<Head, Tail> MatchFirstType for (Head, Tail)
|
||||
where
|
||||
Head: 'static,
|
||||
Tail: TupleList + MatchFirstType,
|
||||
Tail: MatchFirstType,
|
||||
{
|
||||
fn match_first_type<T: 'static>(&self) -> Option<&T> {
|
||||
if TypeId::of::<T>() == TypeId::of::<Head>() {
|
||||
@ -75,7 +123,7 @@ impl MatchType for () {
|
||||
impl<Head, Tail> MatchType for (Head, Tail)
|
||||
where
|
||||
Head: 'static,
|
||||
Tail: TupleList + MatchType,
|
||||
Tail: MatchType,
|
||||
{
|
||||
fn match_type<T: 'static>(&self, f: fn(t: &T)) {
|
||||
if TypeId::of::<T>() == TypeId::of::<Head>() {
|
||||
@ -98,6 +146,30 @@ pub trait Named {
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
pub trait NamedTuple: HasLen {
|
||||
fn get_name(&self, index: usize) -> Option<&str>;
|
||||
}
|
||||
|
||||
impl NamedTuple for () {
|
||||
fn get_name(&self, _index: usize) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<Head, Tail> NamedTuple for (Head, Tail)
|
||||
where
|
||||
Head: 'static + Named,
|
||||
Tail: NamedTuple,
|
||||
{
|
||||
fn get_name(&self, index: usize) -> Option<&str> {
|
||||
if index == 0 {
|
||||
Some(self.0.name())
|
||||
} else {
|
||||
self.1.get_name(index - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MatchNameAndType {
|
||||
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T>;
|
||||
fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T>;
|
||||
@ -115,7 +187,7 @@ impl MatchNameAndType for () {
|
||||
impl<Head, Tail> MatchNameAndType for (Head, Tail)
|
||||
where
|
||||
Head: 'static + Named,
|
||||
Tail: TupleList + MatchNameAndType,
|
||||
Tail: MatchNameAndType,
|
||||
{
|
||||
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T> {
|
||||
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
|
||||
|
@ -246,7 +246,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_event_serde() {
|
||||
let obv = StdMapObserver::new("test", unsafe { &mut MAP });
|
||||
let obv = StdMapObserver::new("test", unsafe { &mut MAP }, unsafe { MAP.len() });
|
||||
let map = tuple_list!(obv);
|
||||
let observers_buf = postcard::to_allocvec(&map).unwrap();
|
||||
|
||||
|
@ -7,7 +7,7 @@ pub use map::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::{Named, TupleList},
|
||||
bolts::tuples::Named,
|
||||
corpus::Testcase,
|
||||
executors::ExitKind,
|
||||
inputs::Input,
|
||||
@ -92,7 +92,7 @@ where
|
||||
impl<Head, Tail, I> FeedbacksTuple<I> for (Head, Tail)
|
||||
where
|
||||
Head: Feedback<I>,
|
||||
Tail: FeedbacksTuple<I> + TupleList,
|
||||
Tail: FeedbacksTuple<I>,
|
||||
I: Input,
|
||||
{
|
||||
fn is_interesting_all<OT: ObserversTuple>(
|
||||
|
@ -42,17 +42,29 @@ where
|
||||
}
|
||||
|
||||
/// The main fuzzer trait.
|
||||
pub trait Fuzzer<E, EM, S> {
|
||||
pub trait Fuzzer<E, EM, S, CS> {
|
||||
/// Fuzz for a single iteration
|
||||
/// Returns the index of the last fuzzed corpus item
|
||||
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error>;
|
||||
fn fuzz_one(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
scheduler: &CS,
|
||||
) -> Result<usize, Error>;
|
||||
|
||||
/// Fuzz forever (or until stopped)
|
||||
fn fuzz_loop(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<(), Error> {
|
||||
fn fuzz_loop(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
scheduler: &CS,
|
||||
) -> Result<usize, Error> {
|
||||
let mut last = current_time();
|
||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
loop {
|
||||
self.fuzz_one(state, executor, manager)?;
|
||||
self.fuzz_one(state, executor, manager, scheduler)?;
|
||||
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
||||
}
|
||||
}
|
||||
@ -60,10 +72,11 @@ pub trait Fuzzer<E, EM, S> {
|
||||
/// Fuzz for n iterations
|
||||
/// Returns the index of the last fuzzed corpus item
|
||||
fn fuzz_loop_for(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
scheduler: &CS,
|
||||
iters: u64,
|
||||
) -> Result<usize, Error> {
|
||||
if iters == 0 {
|
||||
@ -77,7 +90,7 @@ pub trait Fuzzer<E, EM, S> {
|
||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
|
||||
for _ in 0..iters {
|
||||
ret = self.fuzz_one(state, executor, manager)?;
|
||||
ret = self.fuzz_one(state, executor, manager, scheduler)?;
|
||||
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
||||
}
|
||||
Ok(ret)
|
||||
@ -104,9 +117,8 @@ where
|
||||
EM: EventManager<I, S>,
|
||||
I: Input,
|
||||
{
|
||||
scheduler: CS,
|
||||
stages: ST,
|
||||
phantom: PhantomData<(E, EM, I, OT, S)>,
|
||||
phantom: PhantomData<(CS, E, EM, I, OT, S)>,
|
||||
}
|
||||
|
||||
impl<CS, ST, E, EM, I, OT, S> HasStages<CS, E, EM, I, S, ST> for StdFuzzer<CS, ST, E, EM, I, OT, S>
|
||||
@ -126,6 +138,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl<CS, ST, E, EM, I, OT, S> HasCorpusScheduler<CS, I, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
|
||||
where
|
||||
CS: CorpusScheduler<I, S>,
|
||||
@ -142,8 +155,9 @@ where
|
||||
&mut self.scheduler
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
|
||||
impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S, CS> for StdFuzzer<CS, ST, E, EM, I, OT, S>
|
||||
where
|
||||
CS: CorpusScheduler<I, S>,
|
||||
S: HasExecutions,
|
||||
@ -178,13 +192,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error> {
|
||||
let idx = self.scheduler().next(state)?;
|
||||
fn fuzz_one(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
scheduler: &CS,
|
||||
) -> Result<usize, Error> {
|
||||
let idx = scheduler.next(state)?;
|
||||
|
||||
self.stages()
|
||||
.perform_all(state, executor, manager, self.scheduler(), idx)?;
|
||||
self.stages_mut()
|
||||
.perform_all(state, executor, manager, scheduler, idx)?;
|
||||
|
||||
manager.process(state, executor, self.scheduler())?;
|
||||
manager.process(state, executor, scheduler)?;
|
||||
Ok(idx)
|
||||
}
|
||||
}
|
||||
@ -197,9 +217,8 @@ where
|
||||
EM: EventManager<I, S>,
|
||||
I: Input,
|
||||
{
|
||||
pub fn new(scheduler: CS, stages: ST) -> Self {
|
||||
pub fn new(stages: ST) -> Self {
|
||||
Self {
|
||||
scheduler,
|
||||
stages,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ mod tests {
|
||||
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
|
||||
executors::{ExitKind, InProcessExecutor},
|
||||
inputs::BytesInput,
|
||||
mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator},
|
||||
mutators::{mutations::BitFlipMutator, StdScheduledMutator},
|
||||
stages::StdMutationalStage,
|
||||
state::{HasCorpus, State},
|
||||
stats::SimpleStats,
|
||||
@ -182,14 +182,14 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut mutator = StdScheduledMutator::new();
|
||||
mutator.add_mutation(mutation_bitflip);
|
||||
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
|
||||
let stage = StdMutationalStage::new(mutator);
|
||||
let fuzzer = StdFuzzer::new(RandCorpusScheduler::new(), tuple_list!(stage));
|
||||
let scheduler = RandCorpusScheduler::new();
|
||||
let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
|
||||
|
||||
for i in 0..1000 {
|
||||
fuzzer
|
||||
.fuzz_one(&mut state, &mut executor, &mut event_manager)
|
||||
.fuzz_one(&mut state, &mut executor, &mut event_manager, &scheduler)
|
||||
.expect(&format!("Error in iter {}", i));
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,24 @@ pub use mutations::*;
|
||||
pub mod token_mutations;
|
||||
pub use token_mutations::*;
|
||||
|
||||
use crate::{inputs::Input, Error};
|
||||
use crate::{
|
||||
bolts::tuples::{HasLen, Named},
|
||||
inputs::Input,
|
||||
Error,
|
||||
};
|
||||
|
||||
// TODO mutator stats method that produces something that can be sent with the NewTestcase event
|
||||
// We can use it to report which mutations generated the testcase in the broker logs
|
||||
|
||||
/// The result of a mutation.
|
||||
/// If the mutation got skipped, the target
|
||||
/// will not be executed with the returned input.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum MutationResult {
|
||||
Mutated,
|
||||
Skipped,
|
||||
}
|
||||
|
||||
/// A mutator takes input, and mutates it.
|
||||
/// Simple as that.
|
||||
pub trait Mutator<I, S>
|
||||
@ -19,15 +32,158 @@ where
|
||||
I: Input,
|
||||
{
|
||||
/// Mutate a given input
|
||||
fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error>;
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error>;
|
||||
|
||||
/// Post-process given the outcome of the execution
|
||||
fn post_exec(
|
||||
&self,
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_is_interesting: u32,
|
||||
_stage_idx: i32,
|
||||
_corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MutatorsTuple<I, S>: HasLen
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
fn mutate_all(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error>;
|
||||
|
||||
fn post_exec_all(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
stage_idx: i32,
|
||||
corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
fn get_and_mutate(
|
||||
&mut self,
|
||||
index: usize,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error>;
|
||||
|
||||
fn get_and_post_exec(
|
||||
&mut self,
|
||||
index: usize,
|
||||
state: &mut S,
|
||||
stage_idx: i32,
|
||||
corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl<I, S> MutatorsTuple<I, S> for ()
|
||||
where
|
||||
I: Input,
|
||||
{
|
||||
fn mutate_all(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
Ok(MutationResult::Skipped)
|
||||
}
|
||||
|
||||
fn post_exec_all(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_stage_idx: i32,
|
||||
_corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_and_mutate(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
_state: &mut S,
|
||||
_input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
Ok(MutationResult::Skipped)
|
||||
}
|
||||
|
||||
fn get_and_post_exec(
|
||||
&mut self,
|
||||
_index: usize,
|
||||
_state: &mut S,
|
||||
_stage_idx: i32,
|
||||
_corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Head, Tail, I, S> MutatorsTuple<I, S> for (Head, Tail)
|
||||
where
|
||||
Head: Mutator<I, S> + Named,
|
||||
Tail: MutatorsTuple<I, S>,
|
||||
I: Input,
|
||||
{
|
||||
fn mutate_all(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let r = self.0.mutate(state, input, stage_idx)?;
|
||||
if self.1.mutate_all(state, input, stage_idx)? == MutationResult::Mutated {
|
||||
Ok(MutationResult::Mutated)
|
||||
} else {
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
fn post_exec_all(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
stage_idx: i32,
|
||||
corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error> {
|
||||
self.0.post_exec(state, stage_idx, corpus_idx)?;
|
||||
self.1.post_exec_all(state, stage_idx, corpus_idx)
|
||||
}
|
||||
|
||||
fn get_and_mutate(
|
||||
&mut self,
|
||||
index: usize,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
if index == 0 {
|
||||
self.0.mutate(state, input, stage_idx)
|
||||
} else {
|
||||
self.1.get_and_mutate(index - 1, state, input, stage_idx)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_and_post_exec(
|
||||
&mut self,
|
||||
index: usize,
|
||||
state: &mut S,
|
||||
stage_idx: i32,
|
||||
corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error> {
|
||||
if index == 0 {
|
||||
self.0.post_exec(state, stage_idx, corpus_idx)
|
||||
} else {
|
||||
self.1
|
||||
.get_and_post_exec(index - 1, state, stage_idx, corpus_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,108 +1,161 @@
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
default::Default,
|
||||
fmt::{self, Debug},
|
||||
marker::PhantomData,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::{tuple_list, NamedTuple},
|
||||
corpus::Corpus,
|
||||
inputs::{HasBytesVec, Input},
|
||||
mutators::Mutator,
|
||||
mutators::{MutationResult, Mutator, MutatorsTuple},
|
||||
state::{HasCorpus, HasMaxSize, HasMetadata, HasRand},
|
||||
utils::Rand,
|
||||
utils::{AsSlice, Rand},
|
||||
Error,
|
||||
};
|
||||
|
||||
pub use crate::mutators::mutations::*;
|
||||
pub use crate::mutators::token_mutations::*;
|
||||
|
||||
pub trait ScheduledMutator<I, S>: Mutator<I, S> + ComposedByMutations<I, S>
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MutationsMetadata {
|
||||
pub list: Vec<String>,
|
||||
}
|
||||
|
||||
crate::impl_serdeany!(MutationsMetadata);
|
||||
|
||||
impl AsSlice<String> for MutationsMetadata {
|
||||
fn as_slice(&self) -> &[String] {
|
||||
self.list.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl MutationsMetadata {
|
||||
pub fn new(list: Vec<String>) -> Self {
|
||||
Self { list }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComposedByMutations<I, MT, S>
|
||||
where
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
{
|
||||
/// Get the mutations
|
||||
fn mutations(&self) -> &MT;
|
||||
|
||||
// Get the mutations (mut)
|
||||
fn mutations_mut(&mut self) -> &mut MT;
|
||||
}
|
||||
|
||||
pub trait ScheduledMutator<I, MT, S>: ComposedByMutations<I, MT, S> + Mutator<I, S>
|
||||
where
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
{
|
||||
/// Compute the number of iterations used to apply stacked mutations
|
||||
fn iterations(&self, state: &mut S, input: &I) -> u64;
|
||||
|
||||
/// Get the next mutation to apply
|
||||
fn schedule(&self, mutations_count: usize, state: &mut S, input: &I) -> usize;
|
||||
fn schedule(&self, state: &mut S, input: &I) -> usize;
|
||||
|
||||
/// New default implementation for mutate
|
||||
/// Implementations must forward mutate() to this method
|
||||
fn scheduled_mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
|
||||
fn scheduled_mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let mut r = MutationResult::Skipped;
|
||||
let num = self.iterations(state, input);
|
||||
for _ in 0..num {
|
||||
let idx = self.schedule(self.mutations_count(), state, input);
|
||||
self.mutation_by_idx(idx)(state, input)?;
|
||||
let idx = self.schedule(state, input);
|
||||
let outcome = self
|
||||
.mutations_mut()
|
||||
.get_and_mutate(idx, state, input, stage_idx)?;
|
||||
if outcome == MutationResult::Mutated {
|
||||
r = MutationResult::Mutated;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StdScheduledMutator<I, R, S>
|
||||
pub struct StdScheduledMutator<I, MT, R, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasRand<R>,
|
||||
{
|
||||
mutations: Vec<MutationFunction<I, S>>,
|
||||
phantom: PhantomData<R>,
|
||||
mutations: MT,
|
||||
phantom: PhantomData<(I, R, S)>,
|
||||
}
|
||||
|
||||
impl<I, R, S> Debug for StdScheduledMutator<I, R, S>
|
||||
impl<I, MT, R, S> Debug for StdScheduledMutator<I, MT, R, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasRand<R>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"StdScheduledMutator with {} Mutations for Input type {}",
|
||||
"StdScheduledMutator with {} mutations for Input type {}",
|
||||
self.mutations.len(),
|
||||
core::any::type_name::<I>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> Mutator<I, S> for StdScheduledMutator<I, R, S>
|
||||
impl<I, MT, R, S> Mutator<I, S> for StdScheduledMutator<I, MT, R, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
R: Rand,
|
||||
{
|
||||
fn mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
|
||||
self.scheduled_mutate(state, input, _stage_idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> ComposedByMutations<I, S> for StdScheduledMutator<I, R, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
#[inline]
|
||||
fn mutation_by_idx(&self, index: usize) -> MutationFunction<I, S> {
|
||||
self.mutations[index]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mutations_count(&self) -> usize {
|
||||
self.mutations.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_mutation(&mut self, mutation: MutationFunction<I, S>) {
|
||||
self.mutations.push(mutation)
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
self.scheduled_mutate(state, input, stage_idx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> ScheduledMutator<I, S> for StdScheduledMutator<I, R, S>
|
||||
impl<I, MT, R, S> ComposedByMutations<I, MT, S> for StdScheduledMutator<I, MT, R, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasRand<R>,
|
||||
{
|
||||
/// Get the mutations
|
||||
#[inline]
|
||||
fn mutations(&self) -> &MT {
|
||||
&self.mutations
|
||||
}
|
||||
|
||||
// Get the mutations (mut)
|
||||
#[inline]
|
||||
fn mutations_mut(&mut self) -> &mut MT {
|
||||
&mut self.mutations
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, MT, R, S> ScheduledMutator<I, MT, S> for StdScheduledMutator<I, MT, R, S>
|
||||
where
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasRand<R>,
|
||||
{
|
||||
/// Compute the number of iterations used to apply stacked mutations
|
||||
fn iterations(&self, state: &mut S, _: &I) -> u64 {
|
||||
@ -110,161 +163,222 @@ where
|
||||
}
|
||||
|
||||
/// Get the next mutation to apply
|
||||
fn schedule(&self, mutations_count: usize, state: &mut S, _: &I) -> usize {
|
||||
debug_assert!(mutations_count > 0);
|
||||
state.rand_mut().below(mutations_count as u64) as usize
|
||||
fn schedule(&self, state: &mut S, _: &I) -> usize {
|
||||
debug_assert!(!self.mutations().is_empty());
|
||||
state.rand_mut().below(self.mutations().len() as u64) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> StdScheduledMutator<I, R, S>
|
||||
impl<I, MT, R, S> StdScheduledMutator<I, MT, R, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
MT: MutatorsTuple<I, S>,
|
||||
R: Rand,
|
||||
S: HasRand<R>,
|
||||
{
|
||||
/// Create a new StdScheduledMutator instance without mutations and corpus
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mutations: vec![],
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new StdScheduledMutator instance specifying mutations
|
||||
pub fn with_mutations(mutations: Vec<MutationFunction<I, S>>) -> Self {
|
||||
pub fn new(mutations: MT) -> Self {
|
||||
StdScheduledMutator {
|
||||
mutations,
|
||||
mutations: mutations,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> Default for StdScheduledMutator<I, R, S>
|
||||
/// Get the mutations that compose the Havoc mutator
|
||||
pub fn havoc_mutations<C, I, R, S>() -> impl MutatorsTuple<I, S>
|
||||
where
|
||||
I: Input,
|
||||
S: HasRand<R>,
|
||||
R: Rand,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule some selected byte level mutations given a ScheduledMutator type
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HavocBytesMutator<C, I, R, S, SM>
|
||||
where
|
||||
SM: ScheduledMutator<I, S>,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
{
|
||||
tuple_list!(
|
||||
BitFlipMutator::new(),
|
||||
ByteFlipMutator::new(),
|
||||
ByteIncMutator::new(),
|
||||
ByteDecMutator::new(),
|
||||
ByteNegMutator::new(),
|
||||
ByteRandMutator::new(),
|
||||
ByteAddMutator::new(),
|
||||
WordAddMutator::new(),
|
||||
DwordAddMutator::new(),
|
||||
QwordAddMutator::new(),
|
||||
ByteInterestingMutator::new(),
|
||||
WordInterestingMutator::new(),
|
||||
DwordInterestingMutator::new(),
|
||||
BytesDeleteMutator::new(),
|
||||
BytesDeleteMutator::new(),
|
||||
BytesDeleteMutator::new(),
|
||||
BytesDeleteMutator::new(),
|
||||
BytesExpandMutator::new(),
|
||||
BytesInsertMutator::new(),
|
||||
BytesRandInsertMutator::new(),
|
||||
BytesSetMutator::new(),
|
||||
BytesRandSetMutator::new(),
|
||||
BytesCopyMutator::new(),
|
||||
BytesSwapMutator::new(),
|
||||
TokenInsert::new(),
|
||||
TokenReplace::new(),
|
||||
CrossoverInsertMutator::new(),
|
||||
CrossoverReplaceMutator::new(),
|
||||
)
|
||||
}
|
||||
|
||||
//wraps around StdScheduledMutator
|
||||
pub struct LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||
R: Rand,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
SM: ScheduledMutator<I, MT, S>,
|
||||
{
|
||||
scheduled: SM,
|
||||
phantom: PhantomData<(C, I, R, S)>,
|
||||
mutation_log: Vec<usize>,
|
||||
phantom: PhantomData<(C, I, MT, R, S)>,
|
||||
}
|
||||
|
||||
impl<C, I, R, S, SM> Mutator<I, S> for HavocBytesMutator<C, I, R, S, SM>
|
||||
impl<C, I, MT, R, S, SM> Debug for LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
SM: ScheduledMutator<I, S>,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||
R: Rand,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
SM: ScheduledMutator<I, MT, S>,
|
||||
{
|
||||
/// Mutate bytes
|
||||
fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error> {
|
||||
self.scheduled.mutate(state, input, stage_idx)?;
|
||||
/*let num = self.scheduled.iterations(state, input);
|
||||
for _ in 0..num {
|
||||
let idx = self.scheduled.schedule(14, state, input);
|
||||
let mutation = match idx {
|
||||
0 => mutation_bitflip,
|
||||
1 => mutation_byteflip,
|
||||
2 => mutation_byteinc,
|
||||
3 => mutation_bytedec,
|
||||
4 => mutation_byteneg,
|
||||
5 => mutation_byterand,
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"LoggerScheduledMutator with {} mutations for Input type {}",
|
||||
self.scheduled.mutations().len(),
|
||||
core::any::type_name::<I>()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
6 => mutation_byteadd,
|
||||
7 => mutation_wordadd,
|
||||
8 => mutation_dwordadd,
|
||||
9 => mutation_byteinteresting,
|
||||
10 => mutation_wordinteresting,
|
||||
11 => mutation_dwordinteresting,
|
||||
_ => mutation_splice,
|
||||
impl<C, I, MT, R, S, SM> Mutator<I, S> for LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||
R: Rand,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
SM: ScheduledMutator<I, MT, S>,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
self.scheduled_mutate(state, input, stage_idx)
|
||||
}
|
||||
|
||||
fn post_exec(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_stage_idx: i32,
|
||||
corpus_idx: Option<usize>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(idx) = corpus_idx {
|
||||
let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut();
|
||||
let mut log = Vec::<String>::new();
|
||||
while let Some(idx) = self.mutation_log.pop() {
|
||||
let name = String::from(self.scheduled.mutations().get_name(idx).unwrap()); // TODO maybe return an Error on None
|
||||
log.push(name)
|
||||
}
|
||||
let meta = MutationsMetadata::new(log);
|
||||
testcase.add_metadata(meta);
|
||||
};
|
||||
mutation(self, state, input)?;
|
||||
}*/
|
||||
// Always reset the log for each run
|
||||
self.mutation_log.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, I, R, S, SM> HavocBytesMutator<C, I, R, S, SM>
|
||||
impl<C, I, MT, R, S, SM> ComposedByMutations<I, MT, S>
|
||||
for LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
SM: ScheduledMutator<I, S>,
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||
R: Rand,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
SM: ScheduledMutator<I, MT, S>,
|
||||
{
|
||||
/// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap
|
||||
pub fn new(mut scheduled: SM) -> Self {
|
||||
scheduled.add_mutation(mutation_bitflip);
|
||||
scheduled.add_mutation(mutation_splice);
|
||||
#[inline]
|
||||
fn mutations(&self) -> &MT {
|
||||
self.scheduled.mutations()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mutations_mut(&mut self) -> &mut MT {
|
||||
self.scheduled.mutations_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, I, MT, R, S, SM> ScheduledMutator<I, MT, S> for LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||
R: Rand,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
SM: ScheduledMutator<I, MT, S>,
|
||||
{
|
||||
/// Compute the number of iterations used to apply stacked mutations
|
||||
fn iterations(&self, state: &mut S, _: &I) -> u64 {
|
||||
1 << (1 + state.rand_mut().below(6))
|
||||
}
|
||||
|
||||
/// Get the next mutation to apply
|
||||
fn schedule(&self, state: &mut S, _: &I) -> usize {
|
||||
debug_assert!(!self.scheduled.mutations().is_empty());
|
||||
state
|
||||
.rand_mut()
|
||||
.below(self.scheduled.mutations().len() as u64) as usize
|
||||
}
|
||||
|
||||
fn scheduled_mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let mut r = MutationResult::Skipped;
|
||||
let num = self.iterations(state, input);
|
||||
self.mutation_log.clear();
|
||||
for _ in 0..num {
|
||||
let idx = self.schedule(state, input);
|
||||
self.mutation_log.push(idx);
|
||||
let outcome = self
|
||||
.mutations_mut()
|
||||
.get_and_mutate(idx, state, input, stage_idx)?;
|
||||
if outcome == MutationResult::Mutated {
|
||||
r = MutationResult::Mutated;
|
||||
}
|
||||
}
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, I, MT, R, S, SM> LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
C: Corpus<I>,
|
||||
I: Input,
|
||||
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||
R: Rand,
|
||||
S: HasRand<R> + HasCorpus<C, I>,
|
||||
SM: ScheduledMutator<I, MT, S>,
|
||||
{
|
||||
/// Create a new StdScheduledMutator instance without mutations and corpus
|
||||
pub fn new(scheduled: SM) -> Self {
|
||||
Self {
|
||||
scheduled,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, I, R, S> Default for HavocBytesMutator<C, I, R, S, StdScheduledMutator<I, R, S>>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
{
|
||||
/// Create a new HavocBytesMutator instance wrapping StdScheduledMutator
|
||||
fn default() -> Self {
|
||||
let mut scheduled = StdScheduledMutator::<I, R, S>::new();
|
||||
scheduled.add_mutation(mutation_bitflip);
|
||||
scheduled.add_mutation(mutation_byteflip);
|
||||
scheduled.add_mutation(mutation_byteinc);
|
||||
scheduled.add_mutation(mutation_bytedec);
|
||||
scheduled.add_mutation(mutation_byteneg);
|
||||
scheduled.add_mutation(mutation_byterand);
|
||||
|
||||
scheduled.add_mutation(mutation_byteadd);
|
||||
scheduled.add_mutation(mutation_wordadd);
|
||||
scheduled.add_mutation(mutation_dwordadd);
|
||||
scheduled.add_mutation(mutation_qwordadd);
|
||||
scheduled.add_mutation(mutation_byteinteresting);
|
||||
scheduled.add_mutation(mutation_wordinteresting);
|
||||
scheduled.add_mutation(mutation_dwordinteresting);
|
||||
|
||||
scheduled.add_mutation(mutation_bytesdelete);
|
||||
scheduled.add_mutation(mutation_bytesdelete);
|
||||
scheduled.add_mutation(mutation_bytesdelete);
|
||||
scheduled.add_mutation(mutation_bytesdelete);
|
||||
scheduled.add_mutation(mutation_bytesexpand);
|
||||
scheduled.add_mutation(mutation_bytesinsert);
|
||||
scheduled.add_mutation(mutation_bytesrandinsert);
|
||||
scheduled.add_mutation(mutation_bytesset);
|
||||
scheduled.add_mutation(mutation_bytesrandset);
|
||||
scheduled.add_mutation(mutation_bytescopy);
|
||||
scheduled.add_mutation(mutation_bytesswap);
|
||||
|
||||
scheduled.add_mutation(mutation_tokeninsert);
|
||||
scheduled.add_mutation(mutation_tokenreplace);
|
||||
|
||||
scheduled.add_mutation(mutation_crossover_insert);
|
||||
scheduled.add_mutation(mutation_crossover_replace);
|
||||
//scheduled.add_mutation(mutation_splice);
|
||||
|
||||
HavocBytesMutator {
|
||||
scheduled,
|
||||
scheduled: scheduled,
|
||||
mutation_log: vec![],
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -276,7 +390,8 @@ mod tests {
|
||||
corpus::{Corpus, InMemoryCorpus, Testcase},
|
||||
inputs::{BytesInput, HasBytesVec},
|
||||
mutators::{
|
||||
scheduled::{mutation_splice, HavocBytesMutator, StdScheduledMutator},
|
||||
mutations::SpliceMutator,
|
||||
scheduled::{havoc_mutations, StdScheduledMutator},
|
||||
Mutator,
|
||||
},
|
||||
state::State,
|
||||
@ -302,7 +417,8 @@ mod tests {
|
||||
|
||||
rand.set_seed(5);
|
||||
|
||||
mutation_splice(&mut state, &mut input).unwrap();
|
||||
let mut splice = SpliceMutator::new();
|
||||
splice.mutate(&mut state, &mut input, 0).unwrap();
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
println!("{:?}", input.bytes());
|
||||
@ -330,7 +446,7 @@ mod tests {
|
||||
|
||||
let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
|
||||
|
||||
let havoc = HavocBytesMutator::new(StdScheduledMutator::new());
|
||||
let mut havoc = StdScheduledMutator::new(havoc_mutations());
|
||||
|
||||
assert_eq!(input, input_prior);
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
//! Tokens are what afl calls extras or dictionaries.
|
||||
//! They may be inserted as part of mutations during fuzzing.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::{
|
||||
fs::File,
|
||||
@ -15,6 +14,7 @@ use crate::{
|
||||
utils::Rand,
|
||||
Error,
|
||||
};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -121,13 +121,28 @@ impl Tokens {
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a dictionary token
|
||||
pub fn mutation_tokeninsert<I, R, S>(state: &mut S, input: &mut I) -> Result<MutationResult, Error>
|
||||
#[derive(Default)]
|
||||
pub struct TokenInsert<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
phantom: PhantomData<(I, R, S)>,
|
||||
}
|
||||
|
||||
impl<I, R, S> Mutator<I, S> for TokenInsert<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let max_size = state.max_size();
|
||||
let tokens_len = {
|
||||
let meta = state.metadata().get::<Tokens>();
|
||||
@ -162,14 +177,54 @@ where
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
|
||||
/// Overwrite with a dictionary token
|
||||
pub fn mutation_tokenreplace<I, R, S>(state: &mut S, input: &mut I) -> Result<MutationResult, Error>
|
||||
impl<I, R, S> Named for TokenInsert<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R>,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"TokenInsert"
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> TokenInsert<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TokenReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
phantom: PhantomData<(I, R, S)>,
|
||||
}
|
||||
|
||||
impl<I, R, S> Mutator<I, S> for TokenReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
if size == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
@ -200,6 +255,31 @@ where
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> Named for TokenReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"TokenReplace"
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> TokenReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -9,7 +9,7 @@ use core::time::Duration;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named, TupleList},
|
||||
bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named},
|
||||
utils::current_time,
|
||||
Error,
|
||||
};
|
||||
@ -75,7 +75,7 @@ impl ObserversTuple for () {
|
||||
impl<Head, Tail> ObserversTuple for (Head, Tail)
|
||||
where
|
||||
Head: Observer,
|
||||
Tail: ObserversTuple + TupleList,
|
||||
Tail: ObserversTuple,
|
||||
{
|
||||
fn pre_exec_all(&mut self) -> Result<(), Error> {
|
||||
self.0.pre_exec()?;
|
||||
@ -145,7 +145,7 @@ mod tests {
|
||||
fn test_observer_serde() {
|
||||
let obv = tuple_list!(
|
||||
TimeObserver::new("time"),
|
||||
StdMapObserver::new("map", unsafe { &mut MAP })
|
||||
StdMapObserver::new("map", unsafe { &mut MAP }, unsafe { MAP.len() })
|
||||
);
|
||||
let vec = postcard::to_allocvec(&obv).unwrap();
|
||||
println!("{:?}", vec);
|
||||
|
@ -18,7 +18,7 @@ where
|
||||
{
|
||||
/// Run the stage
|
||||
fn perform(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
@ -34,7 +34,7 @@ where
|
||||
I: Input,
|
||||
{
|
||||
fn perform_all(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
@ -49,7 +49,14 @@ where
|
||||
E: Executor<I>,
|
||||
I: Input,
|
||||
{
|
||||
fn perform_all(&self, _: &mut S, _: &mut E, _: &mut EM, _: &CS, _: usize) -> Result<(), Error> {
|
||||
fn perform_all(
|
||||
&mut self,
|
||||
_: &mut S,
|
||||
_: &mut E,
|
||||
_: &mut EM,
|
||||
_: &CS,
|
||||
_: usize,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -63,7 +70,7 @@ where
|
||||
I: Input,
|
||||
{
|
||||
fn perform_all(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
|
@ -40,7 +40,7 @@ where
|
||||
|
||||
/// Runs this (mutational) stage for the given testcase
|
||||
fn perform_mutational(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
@ -55,11 +55,10 @@ where
|
||||
.borrow_mut()
|
||||
.load_input()?
|
||||
.clone();
|
||||
self.mutator().mutate(state, &mut input_mut, i as i32)?;
|
||||
self.mutator_mut().mutate(state, &mut input_mut, i as i32)?;
|
||||
let (_, corpus_idx) = state.evaluate_input(input_mut, executor, manager, scheduler)?;
|
||||
|
||||
let fitness = state.evaluate_input(input_mut, executor, manager, scheduler)?;
|
||||
|
||||
self.mutator().post_exec(state, fitness, i as i32)?;
|
||||
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -131,7 +130,7 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn perform(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
|
@ -198,7 +198,7 @@ where
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
scheduler: &CS,
|
||||
) -> Result<u32, Error>
|
||||
) -> Result<(u32, Option<usize>), Error>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
@ -499,7 +499,7 @@ where
|
||||
executor: &mut E,
|
||||
manager: &mut EM,
|
||||
scheduler: &CS,
|
||||
) -> Result<u32, Error>
|
||||
) -> Result<(u32, Option<usize>), Error>
|
||||
where
|
||||
E: Executor<I> + HasObservers<OT>,
|
||||
OT: ObserversTuple,
|
||||
@ -514,11 +514,8 @@ where
|
||||
// If the input is a solution, add it to the respective corpus
|
||||
self.solutions_mut().add(Testcase::new(input.clone()))?;
|
||||
}
|
||||
|
||||
if self
|
||||
.add_if_interesting(&input, fitness, scheduler)?
|
||||
.is_some()
|
||||
{
|
||||
let corpus_idx = self.add_if_interesting(&input, fitness, scheduler)?;
|
||||
if corpus_idx.is_some() {
|
||||
let observers_buf = manager.serialize_observers(observers)?;
|
||||
manager.fire(
|
||||
self,
|
||||
@ -533,7 +530,7 @@ where
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(fitness)
|
||||
Ok((fitness, corpus_idx))
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,7 +680,7 @@ where
|
||||
let mut added = 0;
|
||||
for _ in 0..num {
|
||||
let input = generator.generate(self.rand_mut())?;
|
||||
let fitness = self.evaluate_input(input, executor, manager, scheduler)?;
|
||||
let (fitness, _) = self.evaluate_input(input, executor, manager, scheduler)?;
|
||||
if fitness > 0 {
|
||||
added += 1;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = ["sancov", "libfuzzer_compatibility"]
|
||||
default = []
|
||||
sancov = []
|
||||
libfuzzer_compatibility = []
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user