From 82f5dad7845367a1defadecac917f7b72d55448d Mon Sep 17 00:00:00 2001 From: Toka Date: Thu, 25 Mar 2021 21:04:18 +0900 Subject: [PATCH] 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 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 instead of vec> * add alloc::string::String * format Co-authored-by: Andrea Fioraldi --- fuzzers/frida_libpng/src/fuzzer.rs | 18 +- fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs | 18 +- fuzzers/libfuzzer_libpng/Cargo.toml | 2 +- fuzzers/libfuzzer_libpng/src/lib.rs | 18 +- .../libfuzzer_libpng_cmpalloc/src/fuzzer.rs | 41 +- fuzzers/libfuzzer_libpng_cmpalloc/test.sh | 6 +- fuzzers/libfuzzer_libpng_old/src/fuzzer.rs | 15 +- libafl/Cargo.toml | 2 +- libafl/src/bolts/tuples.rs | 80 +- libafl/src/events/mod.rs | 2 +- libafl/src/feedbacks/mod.rs | 4 +- libafl/src/fuzzer.rs | 51 +- libafl/src/lib.rs | 10 +- libafl/src/mutators/mod.rs | 164 +- libafl/src/mutators/mutations.rs | 2102 ++++++++++++----- libafl/src/mutators/scheduled.rs | 406 ++-- libafl/src/mutators/token_mutations.rs | 198 +- libafl/src/observers/mod.rs | 6 +- libafl/src/stages/mod.rs | 15 +- libafl/src/stages/mutational.rs | 11 +- libafl/src/state/mod.rs | 15 +- libafl_targets/Cargo.toml | 2 +- 22 files changed, 2293 insertions(+), 893 deletions(-) diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index 51950b31ef..885a62279f 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -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(()) diff --git a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs index 4634c7fd31..0d31d983e2 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/fuzzer.rs @@ -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, 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, 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, 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(()) diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index a6dab10d39..ba4698b223 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -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/" } diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 8f7c53a243..56dfdead1a 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -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, 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, 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, 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(()) diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs index c78a6d88d4..5d53ac67a9 100644 --- a/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_cmpalloc/src/fuzzer.rs @@ -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, 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, 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, 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, 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, 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(()) diff --git a/fuzzers/libfuzzer_libpng_cmpalloc/test.sh b/fuzzers/libfuzzer_libpng_cmpalloc/test.sh index f707f77271..47e79afb3f 100755 --- a/fuzzers/libfuzzer_libpng_cmpalloc/test.sh +++ b/fuzzers/libfuzzer_libpng_cmpalloc/test.sh @@ -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 diff --git a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs index 163a4c43ed..8653a61586 100644 --- a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs @@ -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, 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, 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, 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(()) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index fbaefb34af..2c1506c38e 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -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 diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index c44f74102d..4b5bf0513c 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -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 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; +} + +impl HasNameIdTuple for () { + fn get_const_name(&self, _index: usize) -> Option<&'static str> { + None + } + + fn get_name_id(&self, _index: usize) -> Option { + None + } +} + +impl 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 { + if index == 0 { + Some(self.0.name_id()) + } else { + self.1.get_name_id(index - 1) + } + } +} + pub trait MatchFirstType { fn match_first_type(&self) -> Option<&T>; fn match_first_type_mut(&mut self) -> Option<&mut T>; @@ -43,7 +91,7 @@ impl MatchFirstType for () { impl MatchFirstType for (Head, Tail) where Head: 'static, - Tail: TupleList + MatchFirstType, + Tail: MatchFirstType, { fn match_first_type(&self) -> Option<&T> { if TypeId::of::() == TypeId::of::() { @@ -75,7 +123,7 @@ impl MatchType for () { impl MatchType for (Head, Tail) where Head: 'static, - Tail: TupleList + MatchType, + Tail: MatchType, { fn match_type(&self, f: fn(t: &T)) { if TypeId::of::() == TypeId::of::() { @@ -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 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(&self, name: &str) -> Option<&T>; fn match_name_type_mut(&mut self, name: &str) -> Option<&mut T>; @@ -115,7 +187,7 @@ impl MatchNameAndType for () { impl MatchNameAndType for (Head, Tail) where Head: 'static + Named, - Tail: TupleList + MatchNameAndType, + Tail: MatchNameAndType, { fn match_name_type(&self, name: &str) -> Option<&T> { if TypeId::of::() == TypeId::of::() && name == self.0.name() { diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 01cb94038d..b2c089a699 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -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(); diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 5a156539eb..53e2cf4b8e 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -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 FeedbacksTuple for (Head, Tail) where Head: Feedback, - Tail: FeedbacksTuple + TupleList, + Tail: FeedbacksTuple, I: Input, { fn is_interesting_all( diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 2bddff0b5a..062ae077cc 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -42,17 +42,29 @@ where } /// The main fuzzer trait. -pub trait Fuzzer { +pub trait Fuzzer { /// 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; + fn fuzz_one( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + scheduler: &CS, + ) -> Result; /// 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 { 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 { /// 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 { if iters == 0 { @@ -77,7 +90,7 @@ pub trait Fuzzer { 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: Input, { - scheduler: CS, stages: ST, - phantom: PhantomData<(E, EM, I, OT, S)>, + phantom: PhantomData<(CS, E, EM, I, OT, S)>, } impl HasStages for StdFuzzer @@ -126,6 +138,7 @@ where } } +/* impl HasCorpusScheduler for StdFuzzer where CS: CorpusScheduler, @@ -142,8 +155,9 @@ where &mut self.scheduler } } +*/ -impl Fuzzer for StdFuzzer +impl Fuzzer for StdFuzzer where CS: CorpusScheduler, S: HasExecutions, @@ -178,13 +192,19 @@ where } } - fn fuzz_one(&self, state: &mut S, executor: &mut E, manager: &mut EM) -> Result { - let idx = self.scheduler().next(state)?; + fn fuzz_one( + &mut self, + state: &mut S, + executor: &mut E, + manager: &mut EM, + scheduler: &CS, + ) -> Result { + 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: Input, { - pub fn new(scheduler: CS, stages: ST) -> Self { + pub fn new(stages: ST) -> Self { Self { - scheduler, stages, phantom: PhantomData, } diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index c58307a045..0cc2676e27 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -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)); } diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index e0eaf420ea..878beaa3dc 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -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 @@ -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; /// 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, ) -> Result<(), Error> { Ok(()) } } + +pub trait MutatorsTuple: HasLen +where + I: Input, +{ + fn mutate_all( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result; + + fn post_exec_all( + &mut self, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; + + fn get_and_mutate( + &mut self, + index: usize, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result; + + fn get_and_post_exec( + &mut self, + index: usize, + state: &mut S, + stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error>; +} + +impl MutatorsTuple for () +where + I: Input, +{ + fn mutate_all( + &mut self, + _state: &mut S, + _input: &mut I, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn post_exec_all( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_and_mutate( + &mut self, + _index: usize, + _state: &mut S, + _input: &mut I, + _stage_idx: i32, + ) -> Result { + Ok(MutationResult::Skipped) + } + + fn get_and_post_exec( + &mut self, + _index: usize, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option, + ) -> Result<(), Error> { + Ok(()) + } +} + +impl MutatorsTuple for (Head, Tail) +where + Head: Mutator + Named, + Tail: MutatorsTuple, + I: Input, +{ + fn mutate_all( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + 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, + ) -> 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 { + 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, + ) -> 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) + } + } +} diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 041c4b057a..d1743572b1 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1,42 +1,20 @@ //! A wide variety of mutations used during fuzzing. use crate::{ + bolts::tuples::Named, corpus::Corpus, inputs::{HasBytesVec, Input}, + mutators::{MutationResult, Mutator}, state::{HasCorpus, HasMaxSize, HasRand}, utils::Rand, Error, }; use alloc::{borrow::ToOwned, vec::Vec}; -use core::cmp::{max, min}; - -/// The result of a mutation. -/// If the mutation got skipped, the target -/// will not be executed with the returned input. -#[derive(Clone, Copy, Debug)] -pub enum MutationResult { - Mutated, - Skipped, -} - -// TODO maybe the mutator arg is not needed -/// The generic function type that identifies mutations -pub type MutationFunction = fn(&mut S, &mut I) -> Result; - -pub trait ComposedByMutations -where - I: Input, -{ - /// Get a mutation by index - fn mutation_by_idx(&self, index: usize) -> MutationFunction; - - /// Get the number of mutations - fn mutations_count(&self) -> usize; - - /// Add a mutation - fn add_mutation(&mut self, mutation: MutationFunction); -} +use core::{ + cmp::{max, min}, + marker::PhantomData, +}; /// Mem move in the own vec #[inline] @@ -112,578 +90,1530 @@ const INTERESTING_32: [i32; 27] = [ ]; /// Bitflip mutation for inputs with a bytes vector -pub fn mutation_bitflip(state: &mut S, input: &mut I) -> Result +#[derive(Default)] +pub struct BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BitFlipMutator" + } +} + +impl BitFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byteflip mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) ^= 0xff; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteFlipMutator" + } +} + +impl ByteFlipMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte increment mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx); + *ptr = (*ptr).wrapping_add(1); + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteIncMutator" + } +} + +impl ByteIncMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte decrement mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx); + *ptr = (*ptr).wrapping_sub(1); + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteDecMutator" + } +} + +impl ByteDecMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte negate mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx)); + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteNegMutator" + } +} + +impl ByteNegMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte random mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) = state.rand_mut().below(256) as u8; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteRandMutator" + } +} + +impl ByteRandMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u8; + match state.rand_mut().below(2) { + 0 => *ptr = (*ptr).wrapping_add(num), + _ => *ptr = (*ptr).wrapping_sub(num), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteAddMutator" + } +} + +impl ByteAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Word add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 2 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u16; + match state.rand_mut().below(4) { + 0 => *ptr = (*ptr).wrapping_add(num), + 1 => *ptr = (*ptr).wrapping_sub(num), + 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), + _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "WordAddMutator" + } +} + +impl WordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Dword add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 4 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u32; + match state.rand_mut().below(4) { + 0 => *ptr = (*ptr).wrapping_add(num), + 1 => *ptr = (*ptr).wrapping_sub(num), + 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), + _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "DwordAddMutator" + } +} + +impl DwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Qword add mutation for inputs with a bytes vector +#[derive(Default)] +pub struct QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 8 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 7) as usize; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64; + let num = 1 + state.rand_mut().below(ARITH_MAX) as u64; + match state.rand_mut().below(4) { + 0 => *ptr = (*ptr).wrapping_add(num), + 1 => *ptr = (*ptr).wrapping_sub(num), + 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), + _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), + }; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "QwordAddMutator" + } +} + +impl QwordAddMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Byte interesting mutation for inputs with a bytes vector +#[derive(Default)] +pub struct ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().is_empty() { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; + let val = + INTERESTING_8[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u8; + unsafe { + // Moar speed, no bound check + *input.bytes_mut().get_unchecked_mut(idx) = val; + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "ByteInterestingMutator" + } +} + +impl ByteInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Word interesting mutation for inputs with a bytes vector +#[derive(Default)] +pub struct WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 2 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; + let val = + INTERESTING_16[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u16; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; + if state.rand_mut().below(2) == 0 { + *ptr = val; + } else { + *ptr = val.swap_bytes(); + } + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "WordInterestingMutator" + } +} + +impl WordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Dword interesting mutation for inputs with a bytes vector +#[derive(Default)] +pub struct DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + if input.bytes().len() < 4 { + Ok(MutationResult::Skipped) + } else { + let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; + let val = + INTERESTING_32[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u32; + unsafe { + // Moar speed, no bound check + let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; + if state.rand_mut().below(2) == 0 { + *ptr = val; + } else { + *ptr = val.swap_bytes(); + } + } + Ok(MutationResult::Mutated) + } + } +} + +impl Named for DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "DwordInterestingMutator" + } +} + +impl DwordInterestingMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes delete mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size <= 2 { + return Ok(MutationResult::Skipped); + } + + let off = state.rand_mut().below(size as u64) as usize; + let len = state.rand_mut().below((size - off) as u64) as usize; + input.bytes_mut().drain(off..off + len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesDeleteMutator" + } +} + +impl BytesDeleteMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes expand mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesExpandMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let bit = state.rand_mut().below((input.bytes().len() << 3) as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(bit >> 3) ^= (128 >> (bit & 7)) as u8; - } - Ok(MutationResult::Mutated) - } + phantom: PhantomData<(I, R, S)>, } -pub fn mutation_byteflip(state: &mut S, input: &mut I) -> Result +impl Mutator for BytesExpandMutator where I: Input + HasBytesVec, - S: HasRand, + S: HasRand + HasMaxSize, R: Rand, { - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) ^= 0xff; - } - Ok(MutationResult::Mutated) - } -} + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; -pub fn mutation_byteinc(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx); - *ptr = (*ptr).wrapping_add(1); - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_bytedec(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx); - *ptr = (*ptr).wrapping_sub(1); - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byteneg(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) = !(*input.bytes().get_unchecked(idx)); - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byterand(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) = state.rand_mut().below(256) as u8; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byteadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut u8; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u8; - match state.rand_mut().below(2) { - 0 => *ptr = (*ptr).wrapping_add(num), - _ => *ptr = (*ptr).wrapping_sub(num), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_wordadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 2 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u16; - match state.rand_mut().below(4) { - 0 => *ptr = (*ptr).wrapping_add(num), - 1 => *ptr = (*ptr).wrapping_sub(num), - 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), - _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_dwordadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 4 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u32; - match state.rand_mut().below(4) { - 0 => *ptr = (*ptr).wrapping_add(num), - 1 => *ptr = (*ptr).wrapping_sub(num), - 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), - _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_qwordadd(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 8 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 7) as usize; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u64; - let num = 1 + state.rand_mut().below(ARITH_MAX) as u64; - match state.rand_mut().below(4) { - 0 => *ptr = (*ptr).wrapping_add(num), - 1 => *ptr = (*ptr).wrapping_sub(num), - 2 => *ptr = ((*ptr).swap_bytes().wrapping_add(num)).swap_bytes(), - _ => *ptr = ((*ptr).swap_bytes().wrapping_sub(num)).swap_bytes(), - }; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_byteinteresting( - state: &mut S, - input: &mut I, -) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().is_empty() { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64) as usize; - let val = INTERESTING_8[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u8; - unsafe { - // Moar speed, no bound check - *input.bytes_mut().get_unchecked_mut(idx) = val; - } - Ok(MutationResult::Mutated) - } -} - -pub fn mutation_wordinteresting( - state: &mut S, - input: &mut I, -) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - if input.bytes().len() < 2 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 1) as usize; - let val = - INTERESTING_16[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u16; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u16; - if state.rand_mut().below(2) == 0 { - *ptr = val; + if size + len > max_size { + if max_size > size { + len = max_size - size; } else { - *ptr = val.swap_bytes(); + return Ok(MutationResult::Skipped); } } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + Ok(MutationResult::Mutated) } } -pub fn mutation_dwordinteresting( - state: &mut S, - input: &mut I, -) -> Result +impl Named for BytesExpandMutator where I: Input + HasBytesVec, - S: HasRand, + S: HasRand + HasMaxSize, R: Rand, { - if input.bytes().len() < 4 { - Ok(MutationResult::Skipped) - } else { - let idx = state.rand_mut().below(input.bytes().len() as u64 - 3) as usize; - let val = - INTERESTING_32[state.rand_mut().below(INTERESTING_8.len() as u64) as usize] as u32; - unsafe { - // Moar speed, no bound check - let ptr = input.bytes_mut().get_unchecked_mut(idx) as *mut _ as *mut u32; - if state.rand_mut().below(2) == 0 { - *ptr = val; + fn name(&self) -> &str { + "BytesExpandMutator" + } +} + +impl BytesExpandMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes insert mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; } else { - *ptr = val.swap_bytes(); + return Ok(MutationResult::Skipped); } } + + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + Ok(MutationResult::Mutated) } } -pub fn mutation_bytesdelete(state: &mut S, input: &mut I) -> Result -where - I: Input + HasBytesVec, - S: HasRand, - R: Rand, -{ - let size = input.bytes().len(); - if size <= 2 { - return Ok(MutationResult::Skipped); - } - - let off = state.rand_mut().below(size as u64) as usize; - let len = state.rand_mut().below((size - off) as u64) as usize; - input.bytes_mut().drain(off..off + len); - - Ok(MutationResult::Mutated) -} - -pub fn mutation_bytesexpand(state: &mut S, input: &mut I) -> Result +impl Named for BytesInsertMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } + fn name(&self) -> &str { + "BytesInsertMutator" } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesinsert(state: &mut S, input: &mut I) -> Result +impl BytesInsertMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); + pub fn new() -> Self { + Self { + phantom: PhantomData, } } - - let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesrandinsert( - state: &mut S, - input: &mut I, -) -> Result +/// Bytes random insert mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesRandInsertMutator where I: Input + HasBytesVec, S: HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - let mut len = 1 + state.rand_mut().below(16) as usize; + phantom: PhantomData<(I, R, S)>, +} - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); +impl Mutator for BytesRandInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + let mut len = 1 + state.rand_mut().below(16) as usize; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + let val = state.rand_mut().below(256) as u8; + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "BytesRandInsertMutator" + } +} + +impl BytesRandInsertMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, } } - - let val = state.rand_mut().below(256) as u8; - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesset(state: &mut S, input: &mut I) -> Result +/// Bytes set mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } - let off = state.rand_mut().below(size as u64) as usize; - let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; - - let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; - - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) + phantom: PhantomData<(I, R, S)>, } -pub fn mutation_bytesrandset(state: &mut S, input: &mut I) -> Result +impl Mutator for BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = input.bytes()[state.rand_mut().below(size as u64) as usize]; + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) } - let off = state.rand_mut().below(size as u64) as usize; - let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; - - let val = state.rand_mut().below(256) as u8; - - buffer_set(input.bytes_mut(), off, len, val); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytescopy(state: &mut S, input: &mut I) -> Result +impl Named for BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size <= 1 { - return Ok(MutationResult::Skipped); + fn name(&self) -> &str { + "BytesSetMutator" } - - let from = state.rand_mut().below(input.bytes().len() as u64) as usize; - let to = state.rand_mut().below(input.bytes().len() as u64) as usize; - let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; - - buffer_self_copy(input.bytes_mut(), from, to, len); - - Ok(MutationResult::Mutated) } -pub fn mutation_bytesswap(state: &mut S, input: &mut I) -> Result +impl BytesSetMutator where I: Input + HasBytesVec, S: HasRand, R: Rand, { - let size = input.bytes().len(); - if size <= 1 { - return Ok(MutationResult::Skipped); + pub fn new() -> Self { + Self { + phantom: PhantomData, + } } - - let first = state.rand_mut().below(input.bytes().len() as u64) as usize; - let second = state.rand_mut().below(input.bytes().len() as u64) as usize; - let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; - - let tmp = input.bytes()[first..(first + len)].to_vec(); - buffer_self_copy(input.bytes_mut(), second, first, len); - buffer_copy(input.bytes_mut(), &tmp, 0, second, len); - - Ok(MutationResult::Mutated) } -/// Crossover insert mutation -pub fn mutation_crossover_insert( - state: &mut S, - input: &mut I, -) -> Result +/// Bytes random set mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size == 0 { + return Ok(MutationResult::Skipped); + } + let off = state.rand_mut().below(size as u64) as usize; + let len = 1 + state.rand_mut().below(min(16, size - off) as u64) as usize; + + let val = state.rand_mut().below(256) as u8; + + buffer_set(input.bytes_mut(), off, len, val); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesRandSetMutator" + } +} + +impl BytesRandSetMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes copy mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let from = state.rand_mut().below(input.bytes().len() as u64) as usize; + let to = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(from, to)) as u64) as usize; + + buffer_self_copy(input.bytes_mut(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesCopyMutator" + } +} + +impl BytesCopyMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Bytes swap mutation for inputs with a bytes vector +#[derive(Default)] +pub struct BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size <= 1 { + return Ok(MutationResult::Skipped); + } + + let first = state.rand_mut().below(input.bytes().len() as u64) as usize; + let second = state.rand_mut().below(input.bytes().len() as u64) as usize; + let len = 1 + state.rand_mut().below((size - max(first, second)) as u64) as usize; + + let tmp = input.bytes()[first..(first + len)].to_vec(); + buffer_self_copy(input.bytes_mut(), second, first, len); + buffer_copy(input.bytes_mut(), &tmp, 0, second, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + fn name(&self) -> &str { + "BytesSwapMutator" + } +} + +impl BytesSwapMutator +where + I: Input + HasBytesVec, + S: HasRand, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Crossover insert mutation for inputs with a bytes vector +#[derive(Default)] +pub struct CrossoverInsertMutator where C: Corpus, I: Input + HasBytesVec, R: Rand, S: HasRand + HasCorpus + HasMaxSize, { - let size = input.bytes().len(); - - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } - - let other_size = state - .corpus() - .get(idx)? - .borrow_mut() - .load_input()? - .bytes() - .len(); - if other_size < 2 { - return Ok(MutationResult::Skipped); - } - - let max_size = state.max_size(); - let from = state.rand_mut().below(other_size as u64) as usize; - let to = state.rand_mut().below(size as u64) as usize; - let mut len = state.rand_mut().below((other_size - from) as u64) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), to, to + len, size - to); - buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); - - Ok(MutationResult::Mutated) + phantom: PhantomData<(C, I, R, S)>, } -/// Crossover replace mutation -pub fn mutation_crossover_replace( - state: &mut S, - input: &mut I, -) -> Result +impl Mutator for CrossoverInsertMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus + HasMaxSize, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { + return Ok(MutationResult::Skipped); + } + + let max_size = state.max_size(); + let from = state.rand_mut().below(other_size as u64) as usize; + let to = state.rand_mut().below(size as u64) as usize; + let mut len = state.rand_mut().below((other_size - from) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), to, to + len, size - to); + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for CrossoverInsertMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus + HasMaxSize, +{ + fn name(&self) -> &str { + "CrossoverInsertMutator" + } +} + +impl CrossoverInsertMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus + HasMaxSize, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +/// Crossover replace mutation for inputs with a bytes vector +#[derive(Default)] +pub struct CrossoverReplaceMutator where C: Corpus, I: Input + HasBytesVec, R: Rand, S: HasRand + HasCorpus, { - let size = input.bytes().len(); + phantom: PhantomData<(C, I, R, S)>, +} - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { +impl Mutator for CrossoverReplaceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { + return Ok(MutationResult::Skipped); + } + } + + let other_size = state + .corpus() + .get(idx)? + .borrow_mut() + .load_input()? + .bytes() + .len(); + if other_size < 2 { return Ok(MutationResult::Skipped); } + + let from = state.rand_mut().below(other_size as u64) as usize; + let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; + let to = state.rand_mut().below((size - len) as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + + buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); + + Ok(MutationResult::Mutated) } +} - let other_size = state - .corpus() - .get(idx)? - .borrow_mut() - .load_input()? - .bytes() - .len(); - if other_size < 2 { - return Ok(MutationResult::Skipped); +impl Named for CrossoverReplaceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn name(&self) -> &str { + "CrossoverReplaceMutator" } +} - let from = state.rand_mut().below(other_size as u64) as usize; - let len = state.rand_mut().below(min(other_size - from, size) as u64) as usize; - let to = state.rand_mut().below((size - len) as u64) as usize; - - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - buffer_copy(input.bytes_mut(), other.bytes(), from, to, len); - - Ok(MutationResult::Mutated) +impl CrossoverReplaceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } } /// Returns the first and last diff position between the given vectors, stopping at the min len @@ -702,52 +1632,96 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) { (first_diff, last_diff) } -/// Splicing mutation from AFL -pub fn mutation_splice(state: &mut S, input: &mut I) -> Result +/// Splice mutation for inputs with a bytes vector +#[derive(Default)] +pub struct SpliceMutator where C: Corpus, I: Input + HasBytesVec, R: Rand, S: HasRand + HasCorpus, { - // We don't want to use the testcase we're already using for splicing - let count = state.corpus().count(); - let idx = state.rand_mut().below(count as u64) as usize; - if let Some(cur) = state.corpus().current() { - if idx == *cur { - return Ok(MutationResult::Skipped); - } - } + phantom: PhantomData<(C, I, R, S)>, +} - let (first_diff, last_diff) = { - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - - let mut counter = 0; - loop { - let (f, l) = locate_diffs(input.bytes(), other.bytes()); - - if f != l && f >= 0 && l >= 2 { - break (f, l); - } - if counter == 3 { +impl Mutator for SpliceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + // We don't want to use the testcase we're already using for splicing + let count = state.corpus().count(); + let idx = state.rand_mut().below(count as u64) as usize; + if let Some(cur) = state.corpus().current() { + if idx == *cur { return Ok(MutationResult::Skipped); } - counter += 1; } - }; - let split_at = state - .rand_mut() - .between(first_diff as u64, last_diff as u64) as usize; + let (first_diff, last_diff) = { + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; - let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); - let other = other_testcase.load_input()?; - input - .bytes_mut() - .splice(split_at.., other.bytes()[split_at..].iter().cloned()); + let mut counter = 0; + loop { + let (f, l) = locate_diffs(input.bytes(), other.bytes()); - Ok(MutationResult::Mutated) + if f != l && f >= 0 && l >= 2 { + break (f, l); + } + if counter == 3 { + return Ok(MutationResult::Skipped); + } + counter += 1; + } + }; + + let split_at = state + .rand_mut() + .between(first_diff as u64, last_diff as u64) as usize; + + let mut other_testcase = state.corpus().get(idx)?.borrow_mut(); + let other = other_testcase.load_input()?; + input + .bytes_mut() + .splice(split_at.., other.bytes()[split_at..].iter().cloned()); + + Ok(MutationResult::Mutated) + } +} + +impl Named for SpliceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + fn name(&self) -> &str { + "SpliceMutator" + } +} + +impl SpliceMutator +where + C: Corpus, + I: Input + HasBytesVec, + R: Rand, + S: HasRand + HasCorpus, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } } // Converts a hex u8 to its u8 value: 'A' -> 10 etc. @@ -801,13 +1775,50 @@ mod tests { use super::*; use crate::{ + bolts::tuples::tuple_list, + bolts::tuples::HasLen, corpus::{Corpus, InMemoryCorpus}, inputs::BytesInput, - mutators::{mutation_tokeninsert, mutation_tokenreplace}, - state::State, + mutators::MutatorsTuple, + state::{HasMetadata, State}, utils::StdRand, }; + fn test_mutations() -> impl MutatorsTuple + where + I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata + HasMaxSize, + C: Corpus, + 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(), + ) + } + #[test] fn test_mutators() { let mut inputs = vec![ @@ -829,43 +1840,16 @@ mod tests { let mut state = State::new(rand, corpus, (), InMemoryCorpus::new(), ()); - let mut mutations: Vec> = vec![]; - - mutations.push(mutation_bitflip); - mutations.push(mutation_byteflip); - mutations.push(mutation_byteinc); - mutations.push(mutation_bytedec); - mutations.push(mutation_byteneg); - mutations.push(mutation_byterand); - mutations.push(mutation_byteadd); - mutations.push(mutation_wordadd); - mutations.push(mutation_dwordadd); - mutations.push(mutation_qwordadd); - mutations.push(mutation_byteinteresting); - mutations.push(mutation_wordinteresting); - mutations.push(mutation_dwordinteresting); - - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesdelete); - mutations.push(mutation_bytesexpand); - mutations.push(mutation_bytesinsert); - mutations.push(mutation_bytesrandinsert); - mutations.push(mutation_bytesset); - mutations.push(mutation_bytesrandset); - mutations.push(mutation_bytescopy); - mutations.push(mutation_bytesswap); - - mutations.push(mutation_tokeninsert); - mutations.push(mutation_tokenreplace); - + let mut mutations = test_mutations(); for _ in 0..2 { let mut new_testcases = vec![]; - for mutation in &mutations { + for idx in 0..(mutations.len()) { for input in inputs.iter() { let mut mutant = input.clone(); - match mutation(&mut state, &mut mutant).unwrap() { + match mutations + .get_and_mutate(idx, &mut state, &mut mutant, 0) + .unwrap() + { MutationResult::Mutated => new_testcases.push(mutant), MutationResult::Skipped => (), }; diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index 2c309d46df..5aefac6747 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -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: Mutator + ComposedByMutations +#[derive(Serialize, Deserialize)] +pub struct MutationsMetadata { + pub list: Vec, +} + +crate::impl_serdeany!(MutationsMetadata); + +impl AsSlice for MutationsMetadata { + fn as_slice(&self) -> &[String] { + self.list.as_slice() + } +} + +impl MutationsMetadata { + pub fn new(list: Vec) -> Self { + Self { list } + } +} + +pub trait ComposedByMutations where I: Input, + MT: MutatorsTuple, +{ + /// Get the mutations + fn mutations(&self) -> &MT; + + // Get the mutations (mut) + fn mutations_mut(&mut self) -> &mut MT; +} + +pub trait ScheduledMutator: ComposedByMutations + Mutator +where + I: Input, + MT: MutatorsTuple, { /// 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 { + 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 +pub struct StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { - mutations: Vec>, - phantom: PhantomData, + mutations: MT, + phantom: PhantomData<(I, R, S)>, } -impl Debug for StdScheduledMutator +impl Debug for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { 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::() ) } } -impl Mutator for StdScheduledMutator +impl Mutator for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { - fn mutate(&self, state: &mut S, input: &mut I, _stage_idx: i32) -> Result<(), Error> { - self.scheduled_mutate(state, input, _stage_idx) + #[inline] + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) } } -impl ComposedByMutations for StdScheduledMutator +impl ComposedByMutations for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { + /// Get the mutations #[inline] - fn mutation_by_idx(&self, index: usize) -> MutationFunction { - self.mutations[index] + fn mutations(&self) -> &MT { + &self.mutations } + // Get the mutations (mut) #[inline] - fn mutations_count(&self) -> usize { - self.mutations.len() - } - - #[inline] - fn add_mutation(&mut self, mutation: MutationFunction) { - self.mutations.push(mutation) + fn mutations_mut(&mut self) -> &mut MT { + &mut self.mutations } } -impl ScheduledMutator for StdScheduledMutator +impl ScheduledMutator for StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { /// 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 StdScheduledMutator +impl StdScheduledMutator where I: Input, - S: HasRand, + MT: MutatorsTuple, R: Rand, + S: HasRand, { - /// 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>) -> Self { + pub fn new(mutations: MT) -> Self { StdScheduledMutator { - mutations, + mutations: mutations, phantom: PhantomData, } } } -impl Default for StdScheduledMutator +/// Get the mutations that compose the Havoc mutator +pub fn havoc_mutations() -> impl MutatorsTuple where - I: Input, - S: HasRand, - R: Rand, -{ - fn default() -> Self { - Self::new() - } -} - -/// Schedule some selected byte level mutations given a ScheduledMutator type -#[derive(Clone, Debug)] -pub struct HavocBytesMutator -where - SM: ScheduledMutator, I: Input + HasBytesVec, S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, 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 +where + C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, + R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { scheduled: SM, - phantom: PhantomData<(C, I, R, S)>, + mutation_log: Vec, + phantom: PhantomData<(C, I, MT, R, S)>, } -impl Mutator for HavocBytesMutator +impl Debug for LoggerScheduledMutator where - SM: ScheduledMutator, - I: Input + HasBytesVec, - S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { - /// 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::() + ) + } +} - 6 => mutation_byteadd, - 7 => mutation_wordadd, - 8 => mutation_dwordadd, - 9 => mutation_byteinteresting, - 10 => mutation_wordinteresting, - 11 => mutation_dwordinteresting, - _ => mutation_splice, - }; - mutation(self, state, input)?; - }*/ +impl Mutator for LoggerScheduledMutator +where + C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, + R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + self.scheduled_mutate(state, input, stage_idx) + } + + fn post_exec( + &mut self, + state: &mut S, + _stage_idx: i32, + corpus_idx: Option, + ) -> Result<(), Error> { + if let Some(idx) = corpus_idx { + let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut(); + let mut log = Vec::::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); + }; + // Always reset the log for each run + self.mutation_log.clear(); Ok(()) } } -impl HavocBytesMutator +impl ComposedByMutations + for LoggerScheduledMutator where - SM: ScheduledMutator, - I: Input + HasBytesVec, - S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { - /// 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); - Self { - scheduled, - phantom: PhantomData, - } + #[inline] + fn mutations(&self) -> &MT { + self.scheduled.mutations() + } + + #[inline] + fn mutations_mut(&mut self) -> &mut MT { + self.scheduled.mutations_mut() } } -impl Default for HavocBytesMutator> +impl ScheduledMutator for LoggerScheduledMutator where - I: Input + HasBytesVec, - S: HasRand + HasCorpus + HasMetadata + HasMaxSize, C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, { - /// Create a new HavocBytesMutator instance wrapping StdScheduledMutator - fn default() -> Self { - let mut scheduled = StdScheduledMutator::::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); + /// 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)) + } - 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); + /// 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 + } - 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); + fn scheduled_mutate( + &mut self, + state: &mut S, + input: &mut I, + stage_idx: i32, + ) -> Result { + 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) + } +} - 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, +impl LoggerScheduledMutator +where + C: Corpus, + I: Input, + MT: MutatorsTuple + NamedTuple, + R: Rand, + S: HasRand + HasCorpus, + SM: ScheduledMutator, +{ + /// Create a new StdScheduledMutator instance without mutations and corpus + pub fn new(scheduled: SM) -> Self { + Self { + 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); diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 715bac7f35..240f2afae0 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -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,84 +121,164 @@ impl Tokens { } } -/// Insert a dictionary token -pub fn mutation_tokeninsert(state: &mut S, input: &mut I) -> Result +#[derive(Default)] +pub struct TokenInsert where I: Input + HasBytesVec, S: HasMetadata + HasRand + HasMaxSize, R: Rand, { - let max_size = state.max_size(); - let tokens_len = { - let meta = state.metadata().get::(); - if meta.is_none() { - return Ok(MutationResult::Skipped); - } - if meta.unwrap().tokens().is_empty() { - return Ok(MutationResult::Skipped); - } - meta.unwrap().tokens().len() - }; - let token_idx = state.rand_mut().below(tokens_len as u64) as usize; - - let size = input.bytes().len(); - let off = state.rand_mut().below((size + 1) as u64) as usize; - - let meta = state.metadata().get::().unwrap(); - let token = &meta.tokens()[token_idx]; - let mut len = token.len(); - - if size + len > max_size { - if max_size > size { - len = max_size - size; - } else { - return Ok(MutationResult::Skipped); - } - } - - input.bytes_mut().resize(size + len, 0); - buffer_self_copy(input.bytes_mut(), off, off + len, size - off); - buffer_copy(input.bytes_mut(), token, 0, off, len); - - Ok(MutationResult::Mutated) + phantom: PhantomData<(I, R, S)>, } -/// Overwrite with a dictionary token -pub fn mutation_tokenreplace(state: &mut S, input: &mut I) -> Result +impl Mutator for TokenInsert where I: Input + HasBytesVec, - S: HasMetadata + HasRand, + S: HasMetadata + HasRand + HasMaxSize, R: Rand, { - let size = input.bytes().len(); - if size == 0 { - return Ok(MutationResult::Skipped); - } + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let max_size = state.max_size(); + let tokens_len = { + let meta = state.metadata().get::(); + if meta.is_none() { + return Ok(MutationResult::Skipped); + } + if meta.unwrap().tokens().is_empty() { + return Ok(MutationResult::Skipped); + } + meta.unwrap().tokens().len() + }; + let token_idx = state.rand_mut().below(tokens_len as u64) as usize; - let tokens_len = { - let meta = state.metadata().get::(); - if meta.is_none() { + let size = input.bytes().len(); + let off = state.rand_mut().below((size + 1) as u64) as usize; + + let meta = state.metadata().get::().unwrap(); + let token = &meta.tokens()[token_idx]; + let mut len = token.len(); + + if size + len > max_size { + if max_size > size { + len = max_size - size; + } else { + return Ok(MutationResult::Skipped); + } + } + + input.bytes_mut().resize(size + len, 0); + buffer_self_copy(input.bytes_mut(), off, off + len, size - off); + buffer_copy(input.bytes_mut(), token, 0, off, len); + + Ok(MutationResult::Mutated) + } +} + +impl Named for TokenInsert +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "TokenInsert" + } +} + +impl TokenInsert +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + +#[derive(Default)] +pub struct TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32, + ) -> Result { + let size = input.bytes().len(); + if size == 0 { return Ok(MutationResult::Skipped); } - if meta.unwrap().tokens().is_empty() { - return Ok(MutationResult::Skipped); + + let tokens_len = { + let meta = state.metadata().get::(); + if meta.is_none() { + return Ok(MutationResult::Skipped); + } + if meta.unwrap().tokens().is_empty() { + return Ok(MutationResult::Skipped); + } + meta.unwrap().tokens().len() + }; + let token_idx = state.rand_mut().below(tokens_len as u64) as usize; + + let off = state.rand_mut().below(size as u64) as usize; + + let meta = state.metadata().get::().unwrap(); + let token = &meta.tokens()[token_idx]; + let mut len = token.len(); + if off + len > size { + len = size - off; } - meta.unwrap().tokens().len() - }; - let token_idx = state.rand_mut().below(tokens_len as u64) as usize; - let off = state.rand_mut().below(size as u64) as usize; + buffer_copy(input.bytes_mut(), token, 0, off, len); - let meta = state.metadata().get::().unwrap(); - let token = &meta.tokens()[token_idx]; - let mut len = token.len(); - if off + len > size { - len = size - off; + Ok(MutationResult::Mutated) } +} - buffer_copy(input.bytes_mut(), token, 0, off, len); +impl Named for TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "TokenReplace" + } +} - Ok(MutationResult::Mutated) +impl TokenReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } } #[cfg(test)] diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 7404ceeeb9..26798fdaf1 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -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 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); diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 6d91508a58..b324779990 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -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: 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, diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index cd872b3462..9c9b4d3731 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -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, diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 5f4cf154f5..b55cec8fc1 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -198,7 +198,7 @@ where executor: &mut E, manager: &mut EM, scheduler: &CS, - ) -> Result + ) -> Result<(u32, Option), Error> where E: Executor + HasObservers, OT: ObserversTuple, @@ -499,7 +499,7 @@ where executor: &mut E, manager: &mut EM, scheduler: &CS, - ) -> Result + ) -> Result<(u32, Option), Error> where E: Executor + HasObservers, 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; } diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index f7c5fb3646..efec6f17cf 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Andrea Fioraldi "] edition = "2018" [features] -default = ["sancov", "libfuzzer_compatibility"] +default = [] sancov = [] libfuzzer_compatibility = []