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},
|
events::{setup_restarting_mgr, EventManager},
|
||||||
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
|
executors::{inprocess::InProcessExecutor, Executor, ExitKind, HasObservers},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::{HasTargetBytes, Input},
|
inputs::{HasTargetBytes, Input},
|
||||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
|
mutators::token_mutations::Tokens,
|
||||||
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
|
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, State},
|
||||||
@ -470,12 +471,12 @@ unsafe fn fuzz(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup a basic mutator with a mutational stage
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = HavocBytesMutator::default();
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let stage = StdMutationalStage::new(mutator);
|
let stage = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
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
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
let mut executor = FridaInProcessExecutor::new(
|
let mut executor = FridaInProcessExecutor::new(
|
||||||
@ -502,12 +503,7 @@ unsafe fn fuzz(
|
|||||||
// In case the corpus is empty (on first run), reset
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(
|
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||||
&mut executor,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
fuzzer.scheduler(),
|
|
||||||
&corpus_dirs,
|
|
||||||
)
|
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&corpus_dirs
|
||||||
@ -515,7 +511,7 @@ unsafe fn fuzz(
|
|||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -10,8 +10,8 @@ use libafl::{
|
|||||||
events::setup_restarting_mgr,
|
events::setup_restarting_mgr,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::scheduled::HavocBytesMutator,
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
mutators::token_mutations::Tokens,
|
mutators::token_mutations::Tokens,
|
||||||
observers::StdMapObserver,
|
observers::StdMapObserver,
|
||||||
stages::mutational::StdMutationalStage,
|
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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = HavocBytesMutator::default();
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let stage = StdMutationalStage::new(mutator);
|
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
|
// 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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(
|
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||||
&mut executor,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
fuzzer.scheduler(),
|
|
||||||
&corpus_dirs,
|
|
||||||
)
|
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -18,7 +18,7 @@ debug = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_targets = { path = "../../libafl_targets/" }
|
libafl_targets = { path = "../../libafl_targets/", features = ["sancov", "libfuzzer_compatibility"] }
|
||||||
# TODO Include it only when building cc
|
# TODO Include it only when building cc
|
||||||
libafl_cc = { path = "../../libafl_cc/" }
|
libafl_cc = { path = "../../libafl_cc/" }
|
||||||
|
|
||||||
|
@ -14,8 +14,9 @@ use libafl::{
|
|||||||
events::setup_restarting_mgr,
|
events::setup_restarting_mgr,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
|
mutators::token_mutations::Tokens,
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = HavocBytesMutator::default();
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let stage = StdMutationalStage::new(mutator);
|
let stage = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(
|
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||||
&mut executor,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
fuzzer.scheduler(),
|
|
||||||
&corpus_dirs,
|
|
||||||
)
|
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -6,16 +6,14 @@ use std::{env, path::PathBuf};
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
bolts::{shmem::UnixShMem, tuples::tuple_list},
|
||||||
corpus::{
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
|
||||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
|
||||||
QueueCorpusScheduler,
|
|
||||||
},
|
|
||||||
events::setup_restarting_mgr,
|
events::setup_restarting_mgr,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
mutators::token_mutations::Tokens,
|
||||||
|
observers::{HitcountsMapObserver, StdMapObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
stages::mutational::StdMutationalStage,
|
||||||
state::{HasCorpus, HasMetadata, State},
|
state::{HasCorpus, HasMetadata, State},
|
||||||
stats::SimpleStats,
|
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_track(&edges_observer, true, false),
|
||||||
MaxMapFeedback::new_with_observer(&cmps_observer),
|
MaxMapFeedback::new_with_observer(&cmps_observer),
|
||||||
MaxMapFeedback::new_with_observer(&allocs_observer),
|
MaxMapFeedback::new_with_observer(&allocs_observer),
|
||||||
TimeFeedback::new()
|
|
||||||
),
|
),
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// 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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = HavocBytesMutator::default();
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let stage = StdMutationalStage::new(mutator);
|
let stage = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
let scheduler = RandCorpusScheduler::new();
|
||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
// A fuzzer with just one stage and a random policy to get testcasess from the corpus
|
||||||
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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
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
|
// Create the executor for an in-process function with just one observer for edge coverage
|
||||||
let mut executor = InProcessExecutor::new(
|
let mut executor = InProcessExecutor::new(
|
||||||
"in-process(edges,cmps,allocs)",
|
"in-process(edges)",
|
||||||
&mut harness,
|
&mut harness,
|
||||||
tuple_list!(
|
tuple_list!(edges_observer, cmps_observer, allocs_observer),
|
||||||
edges_observer,
|
|
||||||
cmps_observer,
|
|
||||||
allocs_observer,
|
|
||||||
TimeObserver::new("time")
|
|
||||||
),
|
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut restarting_mgr,
|
&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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(
|
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||||
&mut executor,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
fuzzer.scheduler(),
|
|
||||||
&corpus_dirs,
|
|
||||||
)
|
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
mkdir -p ./crashes
|
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
|
cp ../../target/release/examples/libfuzzer_libpng ./.libfuzzer_test.elf
|
||||||
|
|
||||||
# The broker
|
# 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
|
# Give the broker time to spawn
|
||||||
sleep 2
|
sleep 2
|
||||||
echo "Spawning client"
|
echo "Spawning client"
|
||||||
# The 1st fuzzer client, pin to cpu 0x1
|
# 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
|
killall .libfuzzer_test.elf
|
||||||
rm -rf ./.libfuzzer_test.elf
|
rm -rf ./.libfuzzer_test.elf
|
||||||
|
@ -15,7 +15,7 @@ use libafl::{
|
|||||||
events::setup_restarting_mgr,
|
events::setup_restarting_mgr,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens},
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
stages::mutational::StdMutationalStage,
|
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
|
// Setup a basic mutator with a mutational stage
|
||||||
let mutator = HavocBytesMutator::default();
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let stage = StdMutationalStage::new(mutator);
|
let stage = StdMutationalStage::new(mutator);
|
||||||
|
|
||||||
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
// A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus
|
||||||
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
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
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |buf: &[u8]| {
|
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
|
// In case the corpus is empty (on first run), reset
|
||||||
if state.corpus().count() < 1 {
|
if state.corpus().count() < 1 {
|
||||||
state
|
state
|
||||||
.load_initial_inputs(
|
.load_initial_inputs(&mut executor, &mut restarting_mgr, &scheduler, &corpus_dirs)
|
||||||
&mut executor,
|
|
||||||
&mut restarting_mgr,
|
|
||||||
fuzzer.scheduler(),
|
|
||||||
&corpus_dirs,
|
|
||||||
)
|
|
||||||
.expect(&format!(
|
.expect(&format!(
|
||||||
"Failed to load initial corpus at {:?}",
|
"Failed to load initial corpus at {:?}",
|
||||||
&corpus_dirs
|
&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());
|
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
|
// Never reached
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -46,7 +46,7 @@ required-features = ["std"]
|
|||||||
tuple_list = "0.1.2"
|
tuple_list = "0.1.2"
|
||||||
hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
||||||
num = "*"
|
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
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
erased-serde = "0.3.12"
|
erased-serde = "0.3.12"
|
||||||
postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat
|
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 core::any::TypeId;
|
||||||
|
|
||||||
|
use xxhash_rust::const_xxh3::xxh3_64;
|
||||||
|
|
||||||
pub trait HasLen {
|
pub trait HasLen {
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
@ -19,13 +21,59 @@ impl HasLen for () {
|
|||||||
|
|
||||||
impl<Head, Tail> HasLen for (Head, Tail)
|
impl<Head, Tail> HasLen for (Head, Tail)
|
||||||
where
|
where
|
||||||
Tail: TupleList + HasLen,
|
Tail: HasLen,
|
||||||
{
|
{
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
1 + self.1.len()
|
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 {
|
pub trait MatchFirstType {
|
||||||
fn match_first_type<T: 'static>(&self) -> Option<&T>;
|
fn match_first_type<T: 'static>(&self) -> Option<&T>;
|
||||||
fn match_first_type_mut<T: 'static>(&mut self) -> Option<&mut 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)
|
impl<Head, Tail> MatchFirstType for (Head, Tail)
|
||||||
where
|
where
|
||||||
Head: 'static,
|
Head: 'static,
|
||||||
Tail: TupleList + MatchFirstType,
|
Tail: MatchFirstType,
|
||||||
{
|
{
|
||||||
fn match_first_type<T: 'static>(&self) -> Option<&T> {
|
fn match_first_type<T: 'static>(&self) -> Option<&T> {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Head>() {
|
if TypeId::of::<T>() == TypeId::of::<Head>() {
|
||||||
@ -75,7 +123,7 @@ impl MatchType for () {
|
|||||||
impl<Head, Tail> MatchType for (Head, Tail)
|
impl<Head, Tail> MatchType for (Head, Tail)
|
||||||
where
|
where
|
||||||
Head: 'static,
|
Head: 'static,
|
||||||
Tail: TupleList + MatchType,
|
Tail: MatchType,
|
||||||
{
|
{
|
||||||
fn match_type<T: 'static>(&self, f: fn(t: &T)) {
|
fn match_type<T: 'static>(&self, f: fn(t: &T)) {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Head>() {
|
if TypeId::of::<T>() == TypeId::of::<Head>() {
|
||||||
@ -98,6 +146,30 @@ pub trait Named {
|
|||||||
fn name(&self) -> &str;
|
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 {
|
pub trait MatchNameAndType {
|
||||||
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T>;
|
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>;
|
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)
|
impl<Head, Tail> MatchNameAndType for (Head, Tail)
|
||||||
where
|
where
|
||||||
Head: 'static + Named,
|
Head: 'static + Named,
|
||||||
Tail: TupleList + MatchNameAndType,
|
Tail: MatchNameAndType,
|
||||||
{
|
{
|
||||||
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T> {
|
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T> {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
|
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
|
||||||
|
@ -246,7 +246,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_event_serde() {
|
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 map = tuple_list!(obv);
|
||||||
let observers_buf = postcard::to_allocvec(&map).unwrap();
|
let observers_buf = postcard::to_allocvec(&map).unwrap();
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ pub use map::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::tuples::{Named, TupleList},
|
bolts::tuples::Named,
|
||||||
corpus::Testcase,
|
corpus::Testcase,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
inputs::Input,
|
inputs::Input,
|
||||||
@ -92,7 +92,7 @@ where
|
|||||||
impl<Head, Tail, I> FeedbacksTuple<I> for (Head, Tail)
|
impl<Head, Tail, I> FeedbacksTuple<I> for (Head, Tail)
|
||||||
where
|
where
|
||||||
Head: Feedback<I>,
|
Head: Feedback<I>,
|
||||||
Tail: FeedbacksTuple<I> + TupleList,
|
Tail: FeedbacksTuple<I>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn is_interesting_all<OT: ObserversTuple>(
|
fn is_interesting_all<OT: ObserversTuple>(
|
||||||
|
@ -42,17 +42,29 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The main fuzzer trait.
|
/// The main fuzzer trait.
|
||||||
pub trait Fuzzer<E, EM, S> {
|
pub trait Fuzzer<E, EM, S, CS> {
|
||||||
/// Fuzz for a single iteration
|
/// Fuzz for a single iteration
|
||||||
/// Returns the index of the last fuzzed corpus item
|
/// 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)
|
/// 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 mut last = current_time();
|
||||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||||
loop {
|
loop {
|
||||||
self.fuzz_one(state, executor, manager)?;
|
self.fuzz_one(state, executor, manager, scheduler)?;
|
||||||
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,10 +72,11 @@ pub trait Fuzzer<E, EM, S> {
|
|||||||
/// Fuzz for n iterations
|
/// Fuzz for n iterations
|
||||||
/// Returns the index of the last fuzzed corpus item
|
/// Returns the index of the last fuzzed corpus item
|
||||||
fn fuzz_loop_for(
|
fn fuzz_loop_for(
|
||||||
&self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
|
scheduler: &CS,
|
||||||
iters: u64,
|
iters: u64,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
if iters == 0 {
|
if iters == 0 {
|
||||||
@ -77,7 +90,7 @@ pub trait Fuzzer<E, EM, S> {
|
|||||||
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
let stats_timeout = STATS_TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
for _ in 0..iters {
|
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)?;
|
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
|
||||||
}
|
}
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
@ -104,9 +117,8 @@ where
|
|||||||
EM: EventManager<I, S>,
|
EM: EventManager<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
scheduler: CS,
|
|
||||||
stages: ST,
|
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>
|
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>
|
impl<CS, ST, E, EM, I, OT, S> HasCorpusScheduler<CS, I, S> for StdFuzzer<CS, ST, E, EM, I, OT, S>
|
||||||
where
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
CS: CorpusScheduler<I, S>,
|
||||||
@ -142,8 +155,9 @@ where
|
|||||||
&mut self.scheduler
|
&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
|
where
|
||||||
CS: CorpusScheduler<I, S>,
|
CS: CorpusScheduler<I, S>,
|
||||||
S: HasExecutions,
|
S: HasExecutions,
|
||||||
@ -178,13 +192,19 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result<usize, Error> {
|
fn fuzz_one(
|
||||||
let idx = self.scheduler().next(state)?;
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
executor: &mut E,
|
||||||
|
manager: &mut EM,
|
||||||
|
scheduler: &CS,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
let idx = scheduler.next(state)?;
|
||||||
|
|
||||||
self.stages()
|
self.stages_mut()
|
||||||
.perform_all(state, executor, manager, self.scheduler(), idx)?;
|
.perform_all(state, executor, manager, scheduler, idx)?;
|
||||||
|
|
||||||
manager.process(state, executor, self.scheduler())?;
|
manager.process(state, executor, scheduler)?;
|
||||||
Ok(idx)
|
Ok(idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,9 +217,8 @@ where
|
|||||||
EM: EventManager<I, S>,
|
EM: EventManager<I, S>,
|
||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
pub fn new(scheduler: CS, stages: ST) -> Self {
|
pub fn new(stages: ST) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scheduler,
|
|
||||||
stages,
|
stages,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ mod tests {
|
|||||||
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
|
corpus::{Corpus, InMemoryCorpus, RandCorpusScheduler, Testcase},
|
||||||
executors::{ExitKind, InProcessExecutor},
|
executors::{ExitKind, InProcessExecutor},
|
||||||
inputs::BytesInput,
|
inputs::BytesInput,
|
||||||
mutators::{mutation_bitflip, ComposedByMutations, StdScheduledMutator},
|
mutators::{mutations::BitFlipMutator, StdScheduledMutator},
|
||||||
stages::StdMutationalStage,
|
stages::StdMutationalStage,
|
||||||
state::{HasCorpus, State},
|
state::{HasCorpus, State},
|
||||||
stats::SimpleStats,
|
stats::SimpleStats,
|
||||||
@ -182,14 +182,14 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut mutator = StdScheduledMutator::new();
|
let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new()));
|
||||||
mutator.add_mutation(mutation_bitflip);
|
|
||||||
let stage = StdMutationalStage::new(mutator);
|
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 {
|
for i in 0..1000 {
|
||||||
fuzzer
|
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));
|
.expect(&format!("Error in iter {}", i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,24 @@ pub use mutations::*;
|
|||||||
pub mod token_mutations;
|
pub mod token_mutations;
|
||||||
pub use 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
|
// 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
|
// 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.
|
/// A mutator takes input, and mutates it.
|
||||||
/// Simple as that.
|
/// Simple as that.
|
||||||
pub trait Mutator<I, S>
|
pub trait Mutator<I, S>
|
||||||
@ -19,15 +32,158 @@ where
|
|||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
/// Mutate a given 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
|
/// Post-process given the outcome of the execution
|
||||||
fn post_exec(
|
fn post_exec(
|
||||||
&self,
|
&mut self,
|
||||||
_state: &mut S,
|
_state: &mut S,
|
||||||
_is_interesting: u32,
|
|
||||||
_stage_idx: i32,
|
_stage_idx: i32,
|
||||||
|
_corpus_idx: Option<usize>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Ok(())
|
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 alloc::vec::Vec;
|
||||||
use core::{
|
use core::{
|
||||||
default::Default,
|
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
bolts::tuples::{tuple_list, NamedTuple},
|
||||||
corpus::Corpus,
|
corpus::Corpus,
|
||||||
inputs::{HasBytesVec, Input},
|
inputs::{HasBytesVec, Input},
|
||||||
mutators::Mutator,
|
mutators::{MutationResult, Mutator, MutatorsTuple},
|
||||||
state::{HasCorpus, HasMaxSize, HasMetadata, HasRand},
|
state::{HasCorpus, HasMaxSize, HasMetadata, HasRand},
|
||||||
utils::Rand,
|
utils::{AsSlice, Rand},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::mutators::mutations::*;
|
pub use crate::mutators::mutations::*;
|
||||||
pub use crate::mutators::token_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
|
where
|
||||||
I: Input,
|
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
|
/// Compute the number of iterations used to apply stacked mutations
|
||||||
fn iterations(&self, state: &mut S, input: &I) -> u64;
|
fn iterations(&self, state: &mut S, input: &I) -> u64;
|
||||||
|
|
||||||
/// Get the next mutation to apply
|
/// 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
|
/// New default implementation for mutate
|
||||||
/// Implementations must forward mutate() to this method
|
/// 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);
|
let num = self.iterations(state, input);
|
||||||
for _ in 0..num {
|
for _ in 0..num {
|
||||||
let idx = self.schedule(self.mutations_count(), state, input);
|
let idx = self.schedule(state, input);
|
||||||
self.mutation_by_idx(idx)(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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasRand<R>,
|
MT: MutatorsTuple<I, S>,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R>,
|
||||||
{
|
{
|
||||||
mutations: Vec<MutationFunction<I, S>>,
|
mutations: MT,
|
||||||
phantom: PhantomData<R>,
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasRand<R>,
|
MT: MutatorsTuple<I, S>,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R>,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"StdScheduledMutator with {} Mutations for Input type {}",
|
"StdScheduledMutator with {} mutations for Input type {}",
|
||||||
self.mutations.len(),
|
self.mutations.len(),
|
||||||
core::any::type_name::<I>()
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasRand<R>,
|
MT: MutatorsTuple<I, S>,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R>,
|
||||||
{
|
{
|
||||||
fn mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> {
|
#[inline]
|
||||||
self.scheduled_mutate(state, input, _stage_idx)
|
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> ComposedByMutations<I, S> for StdScheduledMutator<I, R, S>
|
impl<I, MT, R, S> ComposedByMutations<I, MT, S> for StdScheduledMutator<I, MT, R, S>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasRand<R>,
|
MT: MutatorsTuple<I, S>,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R>,
|
||||||
{
|
{
|
||||||
|
/// Get the mutations
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mutation_by_idx(&self, index: usize) -> MutationFunction<I, S> {
|
fn mutations(&self) -> &MT {
|
||||||
self.mutations[index]
|
&self.mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the mutations (mut)
|
||||||
#[inline]
|
#[inline]
|
||||||
fn mutations_count(&self) -> usize {
|
fn mutations_mut(&mut self) -> &mut MT {
|
||||||
self.mutations.len()
|
&mut self.mutations
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add_mutation(&mut self, mutation: MutationFunction<I, S>) {
|
|
||||||
self.mutations.push(mutation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, R, S> ScheduledMutator<I, S> for StdScheduledMutator<I, R, S>
|
impl<I, MT, R, S> ScheduledMutator<I, MT, S> for StdScheduledMutator<I, MT, R, S>
|
||||||
where
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasRand<R>,
|
MT: MutatorsTuple<I, S>,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R>,
|
||||||
{
|
{
|
||||||
/// Compute the number of iterations used to apply stacked mutations
|
/// Compute the number of iterations used to apply stacked mutations
|
||||||
fn iterations(&self, state: &mut S, _: &I) -> u64 {
|
fn iterations(&self, state: &mut S, _: &I) -> u64 {
|
||||||
@ -110,161 +163,222 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next mutation to apply
|
/// Get the next mutation to apply
|
||||||
fn schedule(&self, mutations_count: usize, state: &mut S, _: &I) -> usize {
|
fn schedule(&self, state: &mut S, _: &I) -> usize {
|
||||||
debug_assert!(mutations_count > 0);
|
debug_assert!(!self.mutations().is_empty());
|
||||||
state.rand_mut().below(mutations_count as u64) as usize
|
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
|
where
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasRand<R>,
|
MT: MutatorsTuple<I, S>,
|
||||||
R: Rand,
|
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
|
/// Create a new StdScheduledMutator instance specifying mutations
|
||||||
pub fn with_mutations(mutations: Vec<MutationFunction<I, S>>) -> Self {
|
pub fn new(mutations: MT) -> Self {
|
||||||
StdScheduledMutator {
|
StdScheduledMutator {
|
||||||
mutations,
|
mutations: mutations,
|
||||||
phantom: PhantomData,
|
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
|
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,
|
I: Input + HasBytesVec,
|
||||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
R: Rand,
|
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,
|
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
|
where
|
||||||
SM: ScheduledMutator<I, S>,
|
|
||||||
I: Input + HasBytesVec,
|
|
||||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
|
I: Input,
|
||||||
|
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R> + HasCorpus<C, I>,
|
||||||
|
SM: ScheduledMutator<I, MT, S>,
|
||||||
{
|
{
|
||||||
/// Mutate bytes
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fn mutate(&self, state: &mut S, input: &mut I, stage_idx: i32) -> Result<(), Error> {
|
write!(
|
||||||
self.scheduled.mutate(state, input, stage_idx)?;
|
f,
|
||||||
/*let num = self.scheduled.iterations(state, input);
|
"LoggerScheduledMutator with {} mutations for Input type {}",
|
||||||
for _ in 0..num {
|
self.scheduled.mutations().len(),
|
||||||
let idx = self.scheduled.schedule(14, state, input);
|
core::any::type_name::<I>()
|
||||||
let mutation = match idx {
|
)
|
||||||
0 => mutation_bitflip,
|
}
|
||||||
1 => mutation_byteflip,
|
}
|
||||||
2 => mutation_byteinc,
|
|
||||||
3 => mutation_bytedec,
|
|
||||||
4 => mutation_byteneg,
|
|
||||||
5 => mutation_byterand,
|
|
||||||
|
|
||||||
6 => mutation_byteadd,
|
impl<C, I, MT, R, S, SM> Mutator<I, S> for LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||||
7 => mutation_wordadd,
|
where
|
||||||
8 => mutation_dwordadd,
|
C: Corpus<I>,
|
||||||
9 => mutation_byteinteresting,
|
I: Input,
|
||||||
10 => mutation_wordinteresting,
|
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||||
11 => mutation_dwordinteresting,
|
R: Rand,
|
||||||
_ => mutation_splice,
|
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(())
|
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
|
where
|
||||||
SM: ScheduledMutator<I, S>,
|
|
||||||
I: Input + HasBytesVec,
|
|
||||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
|
I: Input,
|
||||||
|
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R> + HasCorpus<C, I>,
|
||||||
|
SM: ScheduledMutator<I, MT, S>,
|
||||||
{
|
{
|
||||||
/// Create a new HavocBytesMutator instance given a ScheduledMutator to wrap
|
#[inline]
|
||||||
pub fn new(mut scheduled: SM) -> Self {
|
fn mutations(&self) -> &MT {
|
||||||
scheduled.add_mutation(mutation_bitflip);
|
self.scheduled.mutations()
|
||||||
scheduled.add_mutation(mutation_splice);
|
|
||||||
Self {
|
|
||||||
scheduled,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mutations_mut(&mut self) -> &mut MT {
|
||||||
|
self.scheduled.mutations_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, I, R, S> Default for HavocBytesMutator<C, I, R, S, StdScheduledMutator<I, R, S>>
|
impl<C, I, MT, R, S, SM> ScheduledMutator<I, MT, S> for LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||||
where
|
where
|
||||||
I: Input + HasBytesVec,
|
|
||||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
|
||||||
C: Corpus<I>,
|
C: Corpus<I>,
|
||||||
|
I: Input,
|
||||||
|
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||||
R: Rand,
|
R: Rand,
|
||||||
|
S: HasRand<R> + HasCorpus<C, I>,
|
||||||
|
SM: ScheduledMutator<I, MT, S>,
|
||||||
{
|
{
|
||||||
/// Create a new HavocBytesMutator instance wrapping StdScheduledMutator
|
/// Compute the number of iterations used to apply stacked mutations
|
||||||
fn default() -> Self {
|
fn iterations(&self, state: &mut S, _: &I) -> u64 {
|
||||||
let mut scheduled = StdScheduledMutator::<I, R, S>::new();
|
1 << (1 + state.rand_mut().below(6))
|
||||||
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);
|
/// Get the next mutation to apply
|
||||||
scheduled.add_mutation(mutation_wordadd);
|
fn schedule(&self, state: &mut S, _: &I) -> usize {
|
||||||
scheduled.add_mutation(mutation_dwordadd);
|
debug_assert!(!self.scheduled.mutations().is_empty());
|
||||||
scheduled.add_mutation(mutation_qwordadd);
|
state
|
||||||
scheduled.add_mutation(mutation_byteinteresting);
|
.rand_mut()
|
||||||
scheduled.add_mutation(mutation_wordinteresting);
|
.below(self.scheduled.mutations().len() as u64) as usize
|
||||||
scheduled.add_mutation(mutation_dwordinteresting);
|
}
|
||||||
|
|
||||||
scheduled.add_mutation(mutation_bytesdelete);
|
fn scheduled_mutate(
|
||||||
scheduled.add_mutation(mutation_bytesdelete);
|
&mut self,
|
||||||
scheduled.add_mutation(mutation_bytesdelete);
|
state: &mut S,
|
||||||
scheduled.add_mutation(mutation_bytesdelete);
|
input: &mut I,
|
||||||
scheduled.add_mutation(mutation_bytesexpand);
|
stage_idx: i32,
|
||||||
scheduled.add_mutation(mutation_bytesinsert);
|
) -> Result<MutationResult, Error> {
|
||||||
scheduled.add_mutation(mutation_bytesrandinsert);
|
let mut r = MutationResult::Skipped;
|
||||||
scheduled.add_mutation(mutation_bytesset);
|
let num = self.iterations(state, input);
|
||||||
scheduled.add_mutation(mutation_bytesrandset);
|
self.mutation_log.clear();
|
||||||
scheduled.add_mutation(mutation_bytescopy);
|
for _ in 0..num {
|
||||||
scheduled.add_mutation(mutation_bytesswap);
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scheduled.add_mutation(mutation_tokeninsert);
|
impl<C, I, MT, R, S, SM> LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||||
scheduled.add_mutation(mutation_tokenreplace);
|
where
|
||||||
|
C: Corpus<I>,
|
||||||
scheduled.add_mutation(mutation_crossover_insert);
|
I: Input,
|
||||||
scheduled.add_mutation(mutation_crossover_replace);
|
MT: MutatorsTuple<I, S> + NamedTuple,
|
||||||
//scheduled.add_mutation(mutation_splice);
|
R: Rand,
|
||||||
|
S: HasRand<R> + HasCorpus<C, I>,
|
||||||
HavocBytesMutator {
|
SM: ScheduledMutator<I, MT, S>,
|
||||||
scheduled,
|
{
|
||||||
|
/// Create a new StdScheduledMutator instance without mutations and corpus
|
||||||
|
pub fn new(scheduled: SM) -> Self {
|
||||||
|
Self {
|
||||||
|
scheduled: scheduled,
|
||||||
|
mutation_log: vec![],
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,7 +390,8 @@ mod tests {
|
|||||||
corpus::{Corpus, InMemoryCorpus, Testcase},
|
corpus::{Corpus, InMemoryCorpus, Testcase},
|
||||||
inputs::{BytesInput, HasBytesVec},
|
inputs::{BytesInput, HasBytesVec},
|
||||||
mutators::{
|
mutators::{
|
||||||
scheduled::{mutation_splice, HavocBytesMutator, StdScheduledMutator},
|
mutations::SpliceMutator,
|
||||||
|
scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
Mutator,
|
Mutator,
|
||||||
},
|
},
|
||||||
state::State,
|
state::State,
|
||||||
@ -302,7 +417,8 @@ mod tests {
|
|||||||
|
|
||||||
rand.set_seed(5);
|
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")]
|
#[cfg(feature = "std")]
|
||||||
println!("{:?}", input.bytes());
|
println!("{:?}", input.bytes());
|
||||||
@ -330,7 +446,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ());
|
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);
|
assert_eq!(input, input_prior);
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! Tokens are what afl calls extras or dictionaries.
|
//! Tokens are what afl calls extras or dictionaries.
|
||||||
//! They may be inserted as part of mutations during fuzzing.
|
//! They may be inserted as part of mutations during fuzzing.
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
@ -15,6 +14,7 @@ use crate::{
|
|||||||
utils::Rand,
|
utils::Rand,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -121,13 +121,28 @@ impl Tokens {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a dictionary token
|
#[derive(Default)]
|
||||||
pub fn mutation_tokeninsert<I, R, S>(state: &mut S, input: &mut I) -> Result<MutationResult, Error>
|
pub struct TokenInsert<I, R, S>
|
||||||
where
|
where
|
||||||
I: Input + HasBytesVec,
|
I: Input + HasBytesVec,
|
||||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||||
R: Rand,
|
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 max_size = state.max_size();
|
||||||
let tokens_len = {
|
let tokens_len = {
|
||||||
let meta = state.metadata().get::<Tokens>();
|
let meta = state.metadata().get::<Tokens>();
|
||||||
@ -161,15 +176,55 @@ where
|
|||||||
buffer_copy(input.bytes_mut(), token, 0, off, len);
|
buffer_copy(input.bytes_mut(), token, 0, off, len);
|
||||||
|
|
||||||
Ok(MutationResult::Mutated)
|
Ok(MutationResult::Mutated)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrite with a dictionary token
|
impl<I, R, S> Named for TokenInsert<I, R, S>
|
||||||
pub fn mutation_tokenreplace<I, R, S>(state: &mut S, input: &mut I) -> Result<MutationResult, Error>
|
|
||||||
where
|
where
|
||||||
I: Input + HasBytesVec,
|
I: Input + HasBytesVec,
|
||||||
S: HasMetadata + HasRand<R>,
|
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||||
R: Rand,
|
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();
|
let size = input.bytes().len();
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return Ok(MutationResult::Skipped);
|
return Ok(MutationResult::Skipped);
|
||||||
@ -199,6 +254,31 @@ where
|
|||||||
buffer_copy(input.bytes_mut(), token, 0, off, len);
|
buffer_copy(input.bytes_mut(), token, 0, off, len);
|
||||||
|
|
||||||
Ok(MutationResult::Mutated)
|
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)]
|
#[cfg(test)]
|
||||||
|
@ -9,7 +9,7 @@ use core::time::Duration;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named, TupleList},
|
bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named},
|
||||||
utils::current_time,
|
utils::current_time,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
@ -75,7 +75,7 @@ impl ObserversTuple for () {
|
|||||||
impl<Head, Tail> ObserversTuple for (Head, Tail)
|
impl<Head, Tail> ObserversTuple for (Head, Tail)
|
||||||
where
|
where
|
||||||
Head: Observer,
|
Head: Observer,
|
||||||
Tail: ObserversTuple + TupleList,
|
Tail: ObserversTuple,
|
||||||
{
|
{
|
||||||
fn pre_exec_all(&mut self) -> Result<(), Error> {
|
fn pre_exec_all(&mut self) -> Result<(), Error> {
|
||||||
self.0.pre_exec()?;
|
self.0.pre_exec()?;
|
||||||
@ -145,7 +145,7 @@ mod tests {
|
|||||||
fn test_observer_serde() {
|
fn test_observer_serde() {
|
||||||
let obv = tuple_list!(
|
let obv = tuple_list!(
|
||||||
TimeObserver::new("time"),
|
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();
|
let vec = postcard::to_allocvec(&obv).unwrap();
|
||||||
println!("{:?}", vec);
|
println!("{:?}", vec);
|
||||||
|
@ -18,7 +18,7 @@ where
|
|||||||
{
|
{
|
||||||
/// Run the stage
|
/// Run the stage
|
||||||
fn perform(
|
fn perform(
|
||||||
&self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
@ -34,7 +34,7 @@ where
|
|||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn perform_all(
|
fn perform_all(
|
||||||
&self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
@ -49,7 +49,14 @@ where
|
|||||||
E: Executor<I>,
|
E: Executor<I>,
|
||||||
I: Input,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +70,7 @@ where
|
|||||||
I: Input,
|
I: Input,
|
||||||
{
|
{
|
||||||
fn perform_all(
|
fn perform_all(
|
||||||
&self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
|
@ -40,7 +40,7 @@ where
|
|||||||
|
|
||||||
/// Runs this (mutational) stage for the given testcase
|
/// Runs this (mutational) stage for the given testcase
|
||||||
fn perform_mutational(
|
fn perform_mutational(
|
||||||
&self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
@ -55,11 +55,10 @@ where
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.load_input()?
|
.load_input()?
|
||||||
.clone();
|
.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_mut().post_exec(state, i as i32, corpus_idx)?;
|
||||||
|
|
||||||
self.mutator().post_exec(state, fitness, i as i32)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -131,7 +130,7 @@ where
|
|||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
|
@ -198,7 +198,7 @@ where
|
|||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
scheduler: &CS,
|
||||||
) -> Result<u32, Error>
|
) -> Result<(u32, Option<usize>), Error>
|
||||||
where
|
where
|
||||||
E: Executor<I> + HasObservers<OT>,
|
E: Executor<I> + HasObservers<OT>,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
@ -499,7 +499,7 @@ where
|
|||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
scheduler: &CS,
|
||||||
) -> Result<u32, Error>
|
) -> Result<(u32, Option<usize>), Error>
|
||||||
where
|
where
|
||||||
E: Executor<I> + HasObservers<OT>,
|
E: Executor<I> + HasObservers<OT>,
|
||||||
OT: ObserversTuple,
|
OT: ObserversTuple,
|
||||||
@ -514,11 +514,8 @@ where
|
|||||||
// If the input is a solution, add it to the respective corpus
|
// If the input is a solution, add it to the respective corpus
|
||||||
self.solutions_mut().add(Testcase::new(input.clone()))?;
|
self.solutions_mut().add(Testcase::new(input.clone()))?;
|
||||||
}
|
}
|
||||||
|
let corpus_idx = self.add_if_interesting(&input, fitness, scheduler)?;
|
||||||
if self
|
if corpus_idx.is_some() {
|
||||||
.add_if_interesting(&input, fitness, scheduler)?
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
let observers_buf = manager.serialize_observers(observers)?;
|
let observers_buf = manager.serialize_observers(observers)?;
|
||||||
manager.fire(
|
manager.fire(
|
||||||
self,
|
self,
|
||||||
@ -533,7 +530,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(fitness)
|
Ok((fitness, corpus_idx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,7 +680,7 @@ where
|
|||||||
let mut added = 0;
|
let mut added = 0;
|
||||||
for _ in 0..num {
|
for _ in 0..num {
|
||||||
let input = generator.generate(self.rand_mut())?;
|
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 {
|
if fitness > 0 {
|
||||||
added += 1;
|
added += 1;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sancov", "libfuzzer_compatibility"]
|
default = []
|
||||||
sancov = []
|
sancov = []
|
||||||
libfuzzer_compatibility = []
|
libfuzzer_compatibility = []
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user