diff --git a/build_all_fuzzers.sh b/build_all_fuzzers.sh index d596d7aac6..6ccf0cccde 100755 --- a/build_all_fuzzers.sh +++ b/build_all_fuzzers.sh @@ -6,11 +6,10 @@ cd fuzzers for fuzzer in *; do - echo "[+] Checking fmt, clippy, and building $fuzzer" + echo "[+] Checking fmt and building $fuzzer" cd $fuzzer \ && cargo fmt --all -- --check \ - && ../../clippy.sh --no-clean \ && cargo build \ && cd .. \ || exit 1 -done \ No newline at end of file +done diff --git a/fuzzers/baby_fuzzer/Cargo.toml b/fuzzers/baby_fuzzer/Cargo.toml index f02b1ebbe9..97fe602b1b 100644 --- a/fuzzers/baby_fuzzer/Cargo.toml +++ b/fuzzers/baby_fuzzer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "baby_fuzzer" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 5ae8c24746..abd3a88b44 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frida_libpng" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" build = "build.rs" diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index 7d1caaeaae..f26c88052e 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -13,7 +13,7 @@ use libafl::{ os::parse_core_bind_arg, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, - tuples::tuple_list, + tuples::{tuple_list, Merge}, }, corpus::{ ondisk::OnDiskMetadataFormat, Corpus, InMemoryCorpus, @@ -28,7 +28,7 @@ use libafl::{ fuzzer::{Fuzzer, StdFuzzer}, inputs::{HasTargetBytes, Input}, mutators::{ - scheduled::{havoc_mutations, StdScheduledMutator}, + scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, token_mutations::Tokens, }, observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver}, @@ -411,7 +411,7 @@ unsafe fn fuzz( } // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); + let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/libfuzzer_libmozjpeg/Cargo.toml b/fuzzers/libfuzzer_libmozjpeg/Cargo.toml index 0be5b90c1d..f166ef35fa 100644 --- a/fuzzers/libfuzzer_libmozjpeg/Cargo.toml +++ b/fuzzers/libfuzzer_libmozjpeg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_libmozjpeg" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" @@ -16,7 +16,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "value_profile", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "sancov_value_profile", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs index edf4f29717..5218886e52 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/lib.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/lib.rs @@ -4,14 +4,18 @@ use std::{env, path::PathBuf}; use libafl::{ - bolts::{current_nanos, rands::StdRand, tuples::tuple_list}, + bolts::{ + current_nanos, + rands::StdRand, + tuples::{tuple_list, Merge}, + }, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler}, events::setup_restarting_mgr_std, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback}, fuzzer::{Fuzzer, StdFuzzer}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::StdMapObserver, stages::mutational::StdMutationalStage, @@ -114,7 +118,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re } // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); + let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A random policy to get testcasess from the corpus diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 37362ea4e3..248f76bb5f 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_libpng" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" @@ -22,7 +22,7 @@ num_cpus = "1.0" [dependencies] libafl = { path = "../../libafl/", features = ["default", "introspection"] } # libafl = { path = "../../libafl/", features = ["default"] } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } # 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 5c12a430cc..73a64cb029 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -5,7 +5,7 @@ use core::time::Duration; use std::{env, path::PathBuf}; use libafl::{ - bolts::tuples::tuple_list, + bolts::tuples::{tuple_list, Merge}, bolts::{current_nanos, rands::StdRand}, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, @@ -16,7 +16,7 @@ use libafl::{ feedback_or, feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -116,7 +116,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re } // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); + let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/libfuzzer_libpng_launcher/Cargo.toml b/fuzzers/libfuzzer_libpng_launcher/Cargo.toml index 29807dc614..1eb8695145 100644 --- a/fuzzers/libfuzzer_libpng_launcher/Cargo.toml +++ b/fuzzers/libfuzzer_libpng_launcher/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_libpng_launcher" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" @@ -21,7 +21,7 @@ num_cpus = "1.0" [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } clap = { version = "3.0.0-beta.2", features = ["yaml"] } diff --git a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs index 7d63b52440..b179dce6cd 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs @@ -14,7 +14,7 @@ use libafl::{ os::parse_core_bind_arg, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, - tuples::tuple_list, + tuples::{tuple_list, Merge}, }, corpus::{ Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, @@ -24,7 +24,7 @@ use libafl::{ feedback_or, feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, - mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, @@ -117,7 +117,7 @@ pub fn main() { } // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); + let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); // A minimization+queue policy to get testcasess from the corpus diff --git a/fuzzers/libfuzzer_reachability/Cargo.toml b/fuzzers/libfuzzer_reachability/Cargo.toml index b18ae74a9f..8bace9b6a5 100644 --- a/fuzzers/libfuzzer_reachability/Cargo.toml +++ b/fuzzers/libfuzzer_reachability/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_reachability" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" @@ -21,7 +21,7 @@ num_cpus = "1.0" [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/libfuzzer_reachability/src/lib.rs b/fuzzers/libfuzzer_reachability/src/lib.rs index f51b22cb86..b733a847f1 100644 --- a/fuzzers/libfuzzer_reachability/src/lib.rs +++ b/fuzzers/libfuzzer_reachability/src/lib.rs @@ -11,10 +11,9 @@ use libafl::{ feedbacks::{MapFeedbackState, MaxMapFeedback, ReachabilityFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver}, stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, StdState}, + state::{HasCorpus, StdState}, stats::SimpleStats, Error, }; @@ -97,17 +96,6 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re println!("We're a client, let's fuzz :)"); - // Create a PNG dictionary if not existing - if state.metadata().get::().is_none() { - state.add_metadata(Tokens::new(vec![ - vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), - ])); - } - // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations()); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); diff --git a/fuzzers/libfuzzer_stb_image/Cargo.toml b/fuzzers/libfuzzer_stb_image/Cargo.toml index a49a54a81c..8bdfb95cf8 100644 --- a/fuzzers/libfuzzer_stb_image/Cargo.toml +++ b/fuzzers/libfuzzer_stb_image/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfuzzer_stb_image" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" build = "build.rs" @@ -17,7 +17,7 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } -libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_edges", "libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "sancov_cmplog", "libfuzzer"] } [build-dependencies] cc = { version = "1.0", features = ["parallel"] } diff --git a/fuzzers/libfuzzer_stb_image/build.rs b/fuzzers/libfuzzer_stb_image/build.rs index 50cb1582bc..27c140f6df 100644 --- a/fuzzers/libfuzzer_stb_image/build.rs +++ b/fuzzers/libfuzzer_stb_image/build.rs @@ -14,7 +14,7 @@ fn main() { cc::Build::new() // Use sanitizer coverage to track the edges in the PUT - .flag("-fsanitize-coverage=trace-pc-guard") + .flag("-fsanitize-coverage=trace-pc-guard,trace-cmp") // Take advantage of LTO (needs lld-link set in your cargo config) //.flag("-flto=thin") .flag("-Wno-sign-compare") diff --git a/fuzzers/libfuzzer_stb_image/src/main.rs b/fuzzers/libfuzzer_stb_image/src/main.rs index 090ba79492..ee5efc7431 100644 --- a/fuzzers/libfuzzer_stb_image/src/main.rs +++ b/fuzzers/libfuzzer_stb_image/src/main.rs @@ -15,15 +15,18 @@ use libafl::{ feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, mutators::scheduled::{havoc_mutations, StdScheduledMutator}, - mutators::token_mutations::Tokens, + mutators::token_mutations::I2SRandReplace, observers::{StdMapObserver, TimeObserver}, - stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, StdState}, + stages::{StdMutationalStage, TracingStage}, + state::{HasCorpus, StdState}, stats::SimpleStats, Error, }; -use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM}; +use libafl_targets::{ + libfuzzer_initialize, libfuzzer_test_one_input, CmpLogObserver, CMPLOG_MAP, EDGES_MAP, + MAX_EDGES_NUM, +}; pub fn main() { // Registry the metadata types used in this fuzzer @@ -68,6 +71,9 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); + let cmplog = unsafe { &mut CMPLOG_MAP }; + let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true); + // The state of the edges feedback. let feedback_state = MapFeedbackState::with_observer(&edges_observer); @@ -101,21 +107,6 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re println!("We're a client, let's fuzz :)"); - // Create a PNG dictionary if not existing - if state.metadata().get::().is_none() { - state.add_metadata(Tokens::new(vec![ - vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), - ])); - } - - // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations()); - let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - // A minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); @@ -157,6 +148,31 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re println!("We imported {} inputs from disk.", state.corpus().count()); } + // Secondary harness due to mut ownership + let mut harness = |buf: &[u8]| { + libfuzzer_test_one_input(buf); + ExitKind::Ok + }; + + // Setup a tracing stage in which we log comparisons + let tracing = TracingStage::new(InProcessExecutor::new( + &mut harness, + tuple_list!(cmplog_observer), + &mut fuzzer, + &mut state, + &mut restarting_mgr, + )?); + + // Setup a randomic Input2State stage + let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new()))); + + // Setup a basic mutator + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mutational = StdMutationalStage::new(mutator); + + // The order of the stages matter! + let mut stages = tuple_list!(tracing, i2s, mutational); + fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; // Never reached diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 432becf93a..29b05cee79 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] description = "Slot your own fuzzers together and extend their features using Rust" documentation = "https://docs.rs/libafl" @@ -52,7 +52,7 @@ path = "./examples/llmp_test/main.rs" required-features = ["std"] [dependencies] -libafl_derive = { optional = true, path = "../libafl_derive", version = "0.3.0" } +libafl_derive = { optional = true, path = "../libafl_derive", version = "0.3.1" } tuple_list = "0.1.2" hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible num = "0.4.0" diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index 54b55627b0..e9ae0f0c83 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -308,10 +308,10 @@ where } /// Allows prepending of values to a tuple -pub trait Prepend: TupleList { +pub trait Prepend { /// The Resulting [`TupleList`], of an [`Prepend::prepend()`] call, /// including the prepended entry. - type PreprendResult: TupleList; + type PreprendResult; /// Prepend a value to this tuple, returning a new tuple with prepended value. #[must_use] @@ -319,10 +319,7 @@ pub trait Prepend: TupleList { } /// Implement prepend for tuple list. -impl Prepend for Tail -where - Tail: TupleList, -{ +impl Prepend for Tail { type PreprendResult = Self; fn prepend(self, value: T) -> (T, Self::PreprendResult) { @@ -330,11 +327,11 @@ where } } -/// Append to a `TupeList` -pub trait Append: TupleList { +/// Append to a tuple +pub trait Append { /// The Resulting [`TupleList`], of an [`Append::append()`] call, /// including the appended entry. - type AppendResult: TupleList; + type AppendResult; /// Append Value and return the tuple #[must_use] @@ -353,9 +350,7 @@ impl Append for () { /// Implement append for non-empty tuple list. impl Append for (Head, Tail) where - Self: TupleList, Tail: Append, - (Head, Tail::AppendResult): TupleList, { type AppendResult = (Head, Tail::AppendResult); @@ -365,6 +360,38 @@ where } } +/// Merge two `TupeList` +pub trait Merge { + /// The Resulting [`TupleList`], of an [`Merge::merge()`] call + type MergeResult; + + /// Merge and return the merged tuple + #[must_use] + fn merge(self, value: T) -> Self::MergeResult; +} + +/// Implement merge for an empty tuple list. +impl Merge for () { + type MergeResult = T; + + fn merge(self, value: T) -> Self::MergeResult { + value + } +} + +/// Implement merge for non-empty tuple list. +impl Merge for (Head, Tail) +where + Tail: Merge, +{ + type MergeResult = (Head, Tail::MergeResult); + + fn merge(self, value: T) -> Self::MergeResult { + let (head, tail) = self; + (head, tail.merge(value)) + } +} + /// Iterate over a tuple, executing the given `expr` for each element. #[macro_export] macro_rules! tuple_for_each { diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 4523bfcf34..1dca46f3f5 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -249,7 +249,7 @@ where let client = stats.client_stats_mut_for(sender_id); client.update_corpus_size(*corpus_size as u64); client.update_executions(*executions as u64, *time); - // stats.display(event.name().to_string() + " #" + &sender_id.to_string()); + stats.display(event.name().to_string() + " #" + &sender_id.to_string()); Ok(BrokerEventResult::Forward) } Event::UpdateStats { @@ -260,9 +260,7 @@ where // TODO: The stats buffer should be added on client add. let client = stats.client_stats_mut_for(sender_id); client.update_executions(*executions as u64, *time); - if sender_id == 1 { - stats.display(event.name().to_string() + " #" + &sender_id.to_string()); - } + stats.display(event.name().to_string() + " #" + &sender_id.to_string()); Ok(BrokerEventResult::Handled) } #[cfg(feature = "introspection")] diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs new file mode 100644 index 0000000000..208a349e53 --- /dev/null +++ b/libafl/src/executors/combined.rs @@ -0,0 +1,117 @@ +//! A `CombinedExecutor` wraps a primary executor and a secondary one + +use core::marker::PhantomData; + +use crate::{ + executors::{ + Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, + }, + inputs::Input, + observers::ObserversTuple, + Error, +}; + +/// A [`CombinedExecutor`] wraps a primary executor, forwarding its methods, and a secondary one +pub struct CombinedExecutor +where + A: Executor, + B: Executor, + I: Input, +{ + primary: A, + secondary: B, + phantom: PhantomData, +} + +impl CombinedExecutor +where + A: Executor, + B: Executor, + I: Input, +{ + /// Create a new `CombinedExecutor`, wrapping the given `executor`s. + pub fn new(primary: A, secondary: B) -> Self { + Self { + primary, + secondary, + phantom: PhantomData, + } + } + + /// Retrieve the primary `Executor` that is wrapped by this `CombinedExecutor`. + pub fn primary(&mut self) -> &mut A { + &mut self.primary + } + + /// Retrieve the secondary `Executor` that is wrapped by this `CombinedExecutor`. + pub fn secondary(&mut self) -> &mut B { + &mut self.secondary + } +} + +impl Executor for CombinedExecutor +where + A: Executor, + B: Executor, + I: Input, +{ + fn run_target(&mut self, input: &I) -> Result { + self.primary.run_target(input) + } +} + +impl HasObservers for CombinedExecutor +where + A: Executor + HasObservers, + B: Executor, + I: Input, + OT: ObserversTuple, +{ + #[inline] + fn observers(&self) -> &OT { + self.primary.observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut OT { + self.primary.observers_mut() + } +} + +impl HasObserversHooks for CombinedExecutor +where + A: Executor + HasObservers, + B: Executor, + I: Input, + OT: ObserversTuple + HasExecHooksTuple, +{ +} + +impl HasExecHooks for CombinedExecutor +where + A: Executor + HasExecHooks, + B: Executor, + I: Input, +{ + #[inline] + fn pre_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.primary.pre_exec(fuzzer, state, mgr, input) + } + + #[inline] + fn post_exec( + &mut self, + fuzzer: &mut Z, + state: &mut S, + mgr: &mut EM, + input: &I, + ) -> Result<(), Error> { + self.primary.post_exec(fuzzer, state, mgr, input) + } +} diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 1e13c9614e..808a4a1c44 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -4,6 +4,8 @@ pub mod inprocess; pub use inprocess::InProcessExecutor; pub mod timeout; pub use timeout::TimeoutExecutor; +pub mod combined; +pub use combined::CombinedExecutor; use core::marker::PhantomData; diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index fe9b5fc2f4..1691e32179 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::{ bolts::{ rands::Rand, - tuples::{tuple_list, NamedTuple}, + tuples::{tuple_list, tuple_list_type, NamedTuple}, AsSlice, }, corpus::Corpus, @@ -198,7 +198,34 @@ where /// Get the mutations that compose the Havoc mutator #[must_use] -pub fn havoc_mutations() -> impl MutatorsTuple +pub fn havoc_mutations() -> tuple_list_type!( + BitFlipMutator, + ByteFlipMutator, + ByteIncMutator, + ByteDecMutator, + ByteNegMutator, + ByteRandMutator, + ByteAddMutator, + WordAddMutator, + DwordAddMutator, + QwordAddMutator, + ByteInterestingMutator, + WordInterestingMutator, + DwordInterestingMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesExpandMutator, + BytesInsertMutator, + BytesRandInsertMutator, + BytesSetMutator, + BytesRandSetMutator, + BytesCopyMutator, + BytesSwapMutator, + CrossoverInsertMutator, + CrossoverReplaceMutator, + ) where I: Input + HasBytesVec, S: HasRand + HasCorpus + HasMetadata + HasMaxSize, @@ -230,13 +257,24 @@ where BytesRandSetMutator::new(), BytesCopyMutator::new(), BytesSwapMutator::new(), - TokenInsert::new(), - TokenReplace::new(), CrossoverInsertMutator::new(), CrossoverReplaceMutator::new(), ) } +/// Get the mutations that uses the Tokens metadata +#[must_use] +pub fn tokens_mutations( +) -> tuple_list_type!(TokenInsert, TokenReplace) +where + I: Input + HasBytesVec, + S: HasRand + HasCorpus + HasMetadata + HasMaxSize, + C: Corpus, + R: Rand, +{ + tuple_list!(TokenInsert::new(), TokenReplace::new(),) +} + /// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`]. pub struct LoggerScheduledMutator where diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 1b3903e547..5223155a8e 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -1,7 +1,7 @@ //! Tokens are what afl calls extras or dictionaries. //! They may be inserted as part of mutations during fuzzing. use alloc::vec::Vec; -use core::marker::PhantomData; +use core::{convert::TryInto, marker::PhantomData, mem::size_of}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] @@ -17,6 +17,7 @@ use crate::{ bolts::rands::Rand, inputs::{HasBytesVec, Input}, mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named}, + observers::cmp::{CmpValues, CmpValuesMetadata}, state::{HasMaxSize, HasMetadata, HasRand}, Error, }; @@ -291,6 +292,195 @@ where } } +/// A `I2SRandReplace` [`Mutator`] replaces a random matching input-2-state comparison operand with the other. +/// it needs a valid [`CmpValuesMetadata`] in the state. +#[derive(Default)] +pub struct I2SRandReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + phantom: PhantomData<(I, R, S)>, +} + +impl Mutator for I2SRandReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + #[allow(clippy::too_many_lines)] + 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 cmps_len = { + let meta = state.metadata().get::(); + if meta.is_none() { + return Ok(MutationResult::Skipped); + } + if meta.unwrap().list.is_empty() { + return Ok(MutationResult::Skipped); + } + meta.unwrap().list.len() + }; + let idx = state.rand_mut().below(cmps_len as u64) as usize; + + let off = state.rand_mut().below(size as u64) as usize; + let len = input.bytes().len(); + let bytes = input.bytes_mut(); + + let meta = state.metadata().get::().unwrap(); + let cmp_values = &meta.list[idx]; + + let mut result = MutationResult::Skipped; + match cmp_values { + CmpValues::U8(v) => { + for byte in bytes.iter_mut().take(len).skip(off) { + if *byte == v.0 { + *byte = v.1; + result = MutationResult::Mutated; + break; + } else if *byte == v.1 { + *byte = v.0; + result = MutationResult::Mutated; + break; + } + } + } + CmpValues::U16(v) => { + if len >= size_of::() { + for i in off..len - (size_of::() - 1) { + let val = + u16::from_ne_bytes(bytes[i..i + size_of::()].try_into().unwrap()); + if val == v.0 { + let new_bytes = v.1.to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val.swap_bytes() == v.0 { + let new_bytes = v.1.swap_bytes().to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val == v.1 { + let new_bytes = v.0.to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val.swap_bytes() == v.1 { + let new_bytes = v.0.swap_bytes().to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } + } + } + } + CmpValues::U32(v) => { + if len >= size_of::() { + for i in off..len - (size_of::() - 1) { + let val = + u32::from_ne_bytes(bytes[i..i + size_of::()].try_into().unwrap()); + if val == v.0 { + let new_bytes = v.1.to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val.swap_bytes() == v.0 { + let new_bytes = v.1.swap_bytes().to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val == v.1 { + let new_bytes = v.0.to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val.swap_bytes() == v.1 { + let new_bytes = v.0.swap_bytes().to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } + } + } + } + CmpValues::U64(v) => { + if len >= size_of::() { + for i in off..len - (size_of::() - 1) { + let val = + u64::from_ne_bytes(bytes[i..i + size_of::()].try_into().unwrap()); + if val == v.0 { + let new_bytes = v.1.to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val.swap_bytes() == v.0 { + let new_bytes = v.1.swap_bytes().to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val == v.1 { + let new_bytes = v.0.to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } else if val.swap_bytes() == v.1 { + let new_bytes = v.0.swap_bytes().to_ne_bytes(); + bytes[i..i + size_of::()].copy_from_slice(&new_bytes); + result = MutationResult::Mutated; + break; + } + } + } + } + CmpValues::Bytes(_v) => { + // TODO + // buffer_copy(input.bytes_mut(), token, 0, off, len); + } + } + + //println!("{:?}", result); + + Ok(result) + } +} + +impl Named for I2SRandReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + fn name(&self) -> &str { + "I2SRandReplace" + } +} + +impl I2SRandReplace +where + I: Input + HasBytesVec, + S: HasMetadata + HasRand + HasMaxSize, + R: Rand, +{ + /// Creates a new `I2SRandReplace` struct. + #[must_use] + pub fn new() -> Self { + Self { + phantom: PhantomData, + } + } +} + #[cfg(test)] mod tests { #[cfg(feature = "std")] diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs new file mode 100644 index 0000000000..29fa3b79d3 --- /dev/null +++ b/libafl/src/observers/cmp.rs @@ -0,0 +1,254 @@ +//! The `CmpObserver` provides access to the logged values of CMP instructions + +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::{ + bolts::{ownedref::OwnedRefMut, tuples::Named, AsSlice}, + executors::HasExecHooks, + observers::Observer, + state::HasMetadata, + Error, +}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum CmpValues { + U8((u8, u8)), + U16((u16, u16)), + U32((u32, u32)), + U64((u64, u64)), + Bytes((Vec, Vec)), +} + +impl CmpValues { + #[must_use] + pub fn is_numeric(&self) -> bool { + matches!( + self, + CmpValues::U8(_) | CmpValues::U16(_) | CmpValues::U32(_) | CmpValues::U64(_) + ) + } + + #[must_use] + pub fn to_u64_tuple(&self) -> Option<(u64, u64)> { + match self { + CmpValues::U8(t) => Some((u64::from(t.0), u64::from(t.1))), + CmpValues::U16(t) => Some((u64::from(t.0), u64::from(t.1))), + CmpValues::U32(t) => Some((u64::from(t.0), u64::from(t.1))), + CmpValues::U64(t) => Some(*t), + _ => None, + } + } +} + +/// A state metadata holding a list of values logged from comparisons +#[derive(Default, Serialize, Deserialize)] +pub struct CmpValuesMetadata { + /// A `list` of values. + pub list: Vec, +} + +crate::impl_serdeany!(CmpValuesMetadata); + +impl AsSlice for CmpValuesMetadata { + /// Convert to a slice + #[must_use] + fn as_slice(&self) -> &[CmpValues] { + self.list.as_slice() + } +} + +impl CmpValuesMetadata { + /// Creates a new [`struct@CmpValuesMetadata`] + #[must_use] + pub fn new() -> Self { + Self { list: vec![] } + } +} + +/// A [`CmpMap`] traces comparisons during the current execution +pub trait CmpMap: Serialize + DeserializeOwned { + /// Get the number of cmps + fn len(&self) -> usize; + + /// Get if it is empty + #[must_use] + fn is_empty(&self) -> bool { + self.len() == 0 + } + + // Get the number of executions for a cmp + fn executions_for(&self, idx: usize) -> usize; + + // Get the number of logged executions for a cmp + fn usable_executions_for(&self, idx: usize) -> usize; + + // Get the logged values for a cmp + fn values_of(&self, idx: usize, execution: usize) -> CmpValues; + + /// Reset the state + fn reset(&mut self) -> Result<(), Error>; +} + +/// A [`CmpObserver`] observes the traced comparisons during the current execution using a [`CmpMap`] +pub trait CmpObserver: Observer +where + CM: CmpMap, +{ + /// Get the number of usable cmps (all by default) + fn usable_count(&self) -> usize; + + /// Get the `CmpMap` + fn map(&self) -> &CM; + + /// Get the `CmpMap` (mut) + fn map_mut(&mut self) -> &mut CM; + + /// Add [`CmpValuesMetadata`] to the State including the logged values. + /// This routine does a basic loop filtering because loop index cmps are not interesting. + fn add_cmpvalues_meta(&mut self, state: &mut S) + where + S: HasMetadata, + { + if state.metadata().get::().is_none() { + state.add_metadata(CmpValuesMetadata::new()); + } + let meta = state.metadata_mut().get_mut::().unwrap(); + meta.list.clear(); + let count = self.usable_count(); + for i in 0..count { + let execs = self.map().usable_executions_for(i); + if execs > 0 { + // Recongize loops and discard if needed + if execs > 4 { + let mut increasing_v0 = 0; + let mut increasing_v1 = 0; + let mut decreasing_v0 = 0; + let mut decreasing_v1 = 0; + + let mut last: Option = None; + for j in 0..execs { + let val = self.map().values_of(i, j); + if let Some(l) = last.and_then(|x| x.to_u64_tuple()) { + if let Some(v) = val.to_u64_tuple() { + if l.0.wrapping_add(1) == v.0 { + increasing_v0 += 1; + } + if l.1.wrapping_add(1) == v.1 { + increasing_v1 += 1; + } + if l.0.wrapping_sub(1) == v.0 { + decreasing_v0 += 1; + } + if l.1.wrapping_sub(1) == v.1 { + decreasing_v1 += 1; + } + } + } + last = Some(val); + } + // We check for execs-2 because the logged execs may wrap and have something like + // 8 9 10 3 4 5 6 7 + if increasing_v0 >= execs - 2 + || increasing_v1 >= execs - 2 + || decreasing_v0 >= execs - 2 + || decreasing_v1 >= execs - 2 + { + continue; + } + } + for j in 0..execs { + meta.list.push(self.map().values_of(i, j)); + } + } + } + } +} + +/// A standard [`CmpObserver`] observer +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "CM: serde::de::DeserializeOwned")] +pub struct StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + map: OwnedRefMut<'a, CM>, + size: Option>, + name: String, +} + +impl<'a, CM> CmpObserver for StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + /// Get the number of usable cmps (all by default) + fn usable_count(&self) -> usize { + match &self.size { + None => self.map().len(), + Some(o) => *o.as_ref(), + } + } + + fn map(&self) -> &CM { + self.map.as_ref() + } + + fn map_mut(&mut self) -> &mut CM { + self.map.as_mut() + } +} + +impl<'a, CM> Observer for StdCmpObserver<'a, CM> where CM: CmpMap {} + +impl<'a, CM, EM, I, S, Z> HasExecHooks for StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { + self.map.as_mut().reset()?; + Ok(()) + } +} + +impl<'a, CM> Named for StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + fn name(&self) -> &str { + &self.name + } +} + +impl<'a, CM> StdCmpObserver<'a, CM> +where + CM: CmpMap, +{ + /// Creates a new [`StdCmpObserver`] with the given name and map. + #[must_use] + pub fn new(name: &'static str, map: &'a mut CM) -> Self { + Self { + name: name.to_string(), + size: None, + map: OwnedRefMut::Ref(map), + } + } + + /// Creates a new [`StdCmpObserver`] with the given name, map and reference to variable size. + #[must_use] + pub fn with_size(name: &'static str, map: &'a mut CM, size: &'a mut usize) -> Self { + Self { + name: name.to_string(), + size: Some(OwnedRefMut::Ref(size)), + map: OwnedRefMut::Ref(map), + } + } +} diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index d9d2392337..803603fce7 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -3,6 +3,9 @@ pub mod map; pub use map::*; +pub mod cmp; +pub use cmp::*; + use alloc::string::{String, ToString}; use core::time::Duration; use serde::{Deserialize, Serialize}; diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index f7f6b049d6..a9eebc8316 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -6,9 +6,11 @@ Other stages may enrich [`crate::corpus::Testcase`]s with metadata. /// Mutational stage is the normal fuzzing stage, pub mod mutational; - pub use mutational::{MutationalStage, StdMutationalStage}; +pub mod tracing; +pub use tracing::TracingStage; + //pub mod power; //pub use power::PowerMutationalStage; use crate::Error; diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs new file mode 100644 index 0000000000..3e88771a2a --- /dev/null +++ b/libafl/src/stages/tracing.rs @@ -0,0 +1,113 @@ +use core::{marker::PhantomData, mem::drop}; + +use crate::{ + corpus::Corpus, + executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks}, + inputs::Input, + mark_feature_time, + observers::ObserversTuple, + stages::Stage, + start_timer, + state::{HasClientPerfStats, HasCorpus, HasExecutions}, + Error, +}; + +#[cfg(feature = "introspection")] +use crate::stats::PerfFeature; + +/// The default mutational stage +#[derive(Clone, Debug)] +pub struct TracingStage +where + I: Input, + C: Corpus, + TE: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + S: HasClientPerfStats + HasExecutions + HasCorpus, +{ + tracer_executor: TE, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(C, EM, I, OT, S, TE, Z)>, +} + +impl Stage for TracingStage +where + I: Input, + C: Corpus, + TE: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + S: HasClientPerfStats + HasExecutions + HasCorpus, +{ + #[inline] + fn perform( + &mut self, + fuzzer: &mut Z, + _executor: &mut E, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + start_timer!(state); + let input = state + .corpus() + .get(corpus_idx)? + .borrow_mut() + .load_input()? + .clone(); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + start_timer!(state); + self.tracer_executor + .pre_exec_observers(fuzzer, state, manager, &input)?; + mark_feature_time!(state, PerfFeature::PreExecObservers); + + start_timer!(state); + self.tracer_executor + .pre_exec(fuzzer, state, manager, &input)?; + mark_feature_time!(state, PerfFeature::PreExec); + + start_timer!(state); + drop(self.tracer_executor.run_target(&input)?); + mark_feature_time!(state, PerfFeature::TargetExecution); + + start_timer!(state); + self.tracer_executor + .post_exec(fuzzer, state, manager, &input)?; + mark_feature_time!(state, PerfFeature::PostExec); + + *state.executions_mut() += 1; + + start_timer!(state); + self.tracer_executor + .post_exec_observers(fuzzer, state, manager, &input)?; + mark_feature_time!(state, PerfFeature::PostExecObservers); + + Ok(()) + } +} + +impl TracingStage +where + I: Input, + C: Corpus, + TE: Executor + + HasObservers + + HasExecHooks + + HasObserversHooks, + OT: ObserversTuple + HasExecHooksTuple, + S: HasClientPerfStats + HasExecutions + HasCorpus, +{ + /// Creates a new default mutational stage + pub fn new(tracer_executor: TE) -> Self { + Self { + tracer_executor, + phantom: PhantomData, + } + } +} diff --git a/libafl_cc/Cargo.toml b/libafl_cc/Cargo.toml index 1f59a71246..9500348ac7 100644 --- a/libafl_cc/Cargo.toml +++ b/libafl_cc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_cc" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi "] description = "Commodity library to wrap compilers and link LibAFL" documentation = "https://docs.rs/libafl_cc" diff --git a/libafl_derive/Cargo.toml b/libafl_derive/Cargo.toml index a3cc6e4c59..878fd34d21 100644 --- a/libafl_derive/Cargo.toml +++ b/libafl_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_derive" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi "] description = "Derive proc-macro crate for LibAFL" documentation = "https://docs.rs/libafl_derive" diff --git a/libafl_frida/Cargo.toml b/libafl_frida/Cargo.toml index 62a52cb410..343d34b885 100644 --- a/libafl_frida/Cargo.toml +++ b/libafl_frida/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_frida" -version = "0.3.0" +version = "0.3.1" authors = ["s1341 "] description = "Frida backend library for LibAFL" documentation = "https://docs.rs/libafl_frida" @@ -14,8 +14,8 @@ edition = "2018" cc = { version = "1.0", features = ["parallel"] } [dependencies] -libafl = { path = "../libafl", version = "0.3.0", features = ["std", "libafl_derive"] } -libafl_targets = { path = "../libafl_targets", version = "0.3.0" } +libafl = { path = "../libafl", version = "0.3.1", features = ["std", "libafl_derive"] } +libafl_targets = { path = "../libafl_targets", version = "0.3.1" } nix = "0.20.0" libc = "0.2.92" hashbrown = "0.11" diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index ed9e282800..d053891581 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_targets" -version = "0.3.0" +version = "0.3.1" authors = ["Andrea Fioraldi "] description = "Common code for target instrumentation that can be used combined with LibAFL" documentation = "https://docs.rs/libafl_targets" @@ -12,12 +12,12 @@ edition = "2018" [features] default = [] -pcguard_edges = [] -pcguard_hitcounts = [] libfuzzer = [] -value_profile = [] -cmplog = [] -pcguard = ["pcguard_hitcounts"] +sancov_pcguard_edges = [] +sancov_pcguard_hitcounts = [] +sancov_value_profile = [] +sancov_cmplog = [] +sancov_pcguard = ["sancov_pcguard_hitcounts"] clippy = [] # Ignore compiler warnings during clippy [build-dependencies] @@ -25,3 +25,6 @@ cc = { version = "1.0", features = ["parallel"] } [dependencies] rangemap = "0.1.10" +libafl = { path = "../libafl", version = "0.3", features = [] } +serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib +serde-big-array = "0.3.2" diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 3c7d83ccb5..7950fe902e 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -12,6 +12,23 @@ fn main() { //std::env::set_var("CC", "clang"); //std::env::set_var("CXX", "clang++"); + #[cfg(any(feature = "sancov_value_profile", feature = "sancov_cmplog"))] + { + println!("cargo:rerun-if-changed=src/sancov_cmp.c"); + + let mut sancov_cmp = cc::Build::new(); + + #[cfg(feature = "sancov_value_profile")] + sancov_cmp.define("SANCOV_VALUE_PROFILE", "1"); + + #[cfg(feature = "sancov_cmplog")] + sancov_cmp.define("SANCOV_CMPLOG", "1"); + + sancov_cmp + .file(_src_dir.join("sancov_cmp.c")) + .compile("sancov_cmp"); + } + #[cfg(feature = "libfuzzer")] { println!("cargo:rerun-if-changed=src/libfuzzer_compatibility.c"); @@ -21,24 +38,6 @@ fn main() { .compile("libfuzzer_compatibility"); } - #[cfg(feature = "value_profile")] - { - println!("cargo:rerun-if-changed=src/value_profile.c"); - - cc::Build::new() - .file(_src_dir.join("value_profile.c")) - .compile("value_profile"); - } - - #[cfg(feature = "cmplog")] - { - println!("cargo:rerun-if-changed=src/cmplog.c"); - - cc::Build::new() - .file(_src_dir.join("cmplog.c")) - .compile("cmplog"); - } - println!("cargo:rustc-link-search=native={}", &out_dir); println!("cargo:rerun-if-changed=build.rs"); diff --git a/libafl_targets/src/cmplog.c b/libafl_targets/src/cmplog.c deleted file mode 100644 index c03bfcd14e..0000000000 --- a/libafl_targets/src/cmplog.c +++ /dev/null @@ -1,197 +0,0 @@ -#include - -#define CMPLOG_MAP_W 65536 -#define CMPLOG_MAP_H 32 - -#define CMPLOG_KIND_INS 0 -#define CMPLOG_KIND_RTN 1 - -#ifdef _WIN32 -#define RETADDR (uintptr_t)_ReturnAddress() -#else -#define RETADDR (uintptr_t)__builtin_return_address(0) -#endif - -typedef struct CmpLogHeader { - uint16_t hits; - uint8_t shape; - uint8_t kind; -} CmpLogHeader; - -typedef struct CmpLogOperands { - uint64_t v0; - uint64_t v1; -} CmpLogOperands; - -typedef struct CmpLogMap { - CmpLogHeader headers[CMPLOG_MAP_W]; - CmpLogOperands operands[CMPLOG_MAP_W][CMPLOG_MAP_H]; -} CmpLogMap; - -extern CmpLogMap libafl_cmplog_map; - -extern uint8_t libafl_cmplog_enabled; - -#if defined(__APPLE__) - #pragma weak __sanitizer_cov_trace_const_cmp1 = __sanitizer_cov_trace_cmp1 - #pragma weak __sanitizer_cov_trace_const_cmp2 = __sanitizer_cov_trace_cmp2 - #pragma weak __sanitizer_cov_trace_const_cmp4 = __sanitizer_cov_trace_cmp4 - #pragma weak __sanitizer_cov_trace_const_cmp8 = __sanitizer_cov_trace_cmp8 -#elif defined(_MSC_VER) - #pragma comment(linker, "/alternatename:__sanitizer_cov_trace_const_cmp1=__sanitizer_cov_trace_cmp1") - #pragma comment(linker, "/alternatename:__sanitizer_cov_trace_const_cmp2=__sanitizer_cov_trace_cmp2") - #pragma comment(linker, "/alternatename:__sanitizer_cov_trace_const_cmp4=__sanitizer_cov_trace_cmp4") - #pragma comment(linker, "/alternatename:__sanitizer_cov_trace_const_cmp8=__sanitizer_cov_trace_cmp8") -#else -void __sanitizer_cov_trace_const_cmp1(uint8_t arg1, uint8_t arg2) __attribute__((alias("__sanitizer_cov_trace_cmp1"))); -void __sanitizer_cov_trace_const_cmp2(uint16_t arg1, uint16_t arg2) - __attribute__((alias("__sanitizer_cov_trace_cmp2"))); -void __sanitizer_cov_trace_const_cmp4(uint32_t arg1, uint32_t arg2) - __attribute__((alias("__sanitizer_cov_trace_cmp4"))); -void __sanitizer_cov_trace_const_cmp8(uint64_t arg1, uint64_t arg2) - __attribute__((alias("__sanitizer_cov_trace_cmp8"))); -#endif - - -void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { - - if (!libafl_cmplog_enabled) return; - - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - k &= CMPLOG_MAP_W - 1; - - uint16_t hits; - if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { - libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; - libafl_cmplog_map.headers[k].hits = 1; - libafl_cmplog_map.headers[k].shape = 1; - hits = 0; - } else { - hits = libafl_cmplog_map.headers[k].hits++; - if (libafl_cmplog_map.headers[k].shape < 1) { - libafl_cmplog_map.headers[k].shape = 1; - } - } - - hits &= CMPLOG_MAP_H - 1; - libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; - libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; - -} - -void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { - - if (!libafl_cmplog_enabled) return; - - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - k &= CMPLOG_MAP_W - 1; - - uint16_t hits; - if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { - libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; - libafl_cmplog_map.headers[k].hits = 1; - libafl_cmplog_map.headers[k].shape = 2; - hits = 0; - } else { - hits = libafl_cmplog_map.headers[k].hits++; - if (libafl_cmplog_map.headers[k].shape < 2) { - libafl_cmplog_map.headers[k].shape = 2; - } - } - - hits &= CMPLOG_MAP_H - 1; - libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; - libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; - -} - -void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { - - if (!libafl_cmplog_enabled) return; - - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - k &= CMPLOG_MAP_W - 1; - - uint16_t hits; - if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { - libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; - libafl_cmplog_map.headers[k].hits = 1; - libafl_cmplog_map.headers[k].shape = 4; - hits = 0; - } else { - hits = libafl_cmplog_map.headers[k].hits++; - if (libafl_cmplog_map.headers[k].shape < 4) { - libafl_cmplog_map.headers[k].shape = 4; - } - } - - hits &= CMPLOG_MAP_H - 1; - libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; - libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; -} - -void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { - - if (!libafl_cmplog_enabled) return; - - uintptr_t k = RETADDR; - k = (k >> 4) ^ (k << 8); - k &= CMPLOG_MAP_W - 1; - - uint16_t hits; - if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { - libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; - libafl_cmplog_map.headers[k].hits = 1; - libafl_cmplog_map.headers[k].shape = 8; - hits = 0; - } else { - hits = libafl_cmplog_map.headers[k].hits++; - if (libafl_cmplog_map.headers[k].shape < 8) { - libafl_cmplog_map.headers[k].shape = 8; - } - } - - hits &= CMPLOG_MAP_H - 1; - libafl_cmplog_map.operands[k][hits].v0 = (uint64_t)arg1; - libafl_cmplog_map.operands[k][hits].v1 = (uint64_t)arg2; - -} - -void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { - - if (!libafl_cmplog_enabled) return; - - uint8_t shape = (uint8_t)cases[1]; - if (shape) { - shape /= 8; - } - - for (uint64_t i = 0; i < cases[0]; i++) { - - uintptr_t k = RETADDR + i; - k = (k >> 4) ^ (k << 8); - k &= CMPLOG_MAP_W - 1; - - uint16_t hits; - if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { - libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; - libafl_cmplog_map.headers[k].hits = 1; - libafl_cmplog_map.headers[k].shape = shape; - hits = 0; - } else { - hits = libafl_cmplog_map.headers[k].hits++; - if (libafl_cmplog_map.headers[k].shape < shape) { - libafl_cmplog_map.headers[k].shape = shape; - } - } - - hits &= CMPLOG_MAP_H - 1; - libafl_cmplog_map.operands[k][hits].v0 = val; - libafl_cmplog_map.operands[k][hits].v1 = cases[i + 2]; - - } - -} diff --git a/libafl_targets/src/cmplog.h b/libafl_targets/src/cmplog.h new file mode 100644 index 0000000000..762ed548d3 --- /dev/null +++ b/libafl_targets/src/cmplog.h @@ -0,0 +1,55 @@ +#ifndef __LIBAFL_TARGETS_CMPLOG__ +#define __LIBAFL_TARGETS_CMPLOG__ + +#include "common.h" + +#define CMPLOG_MAP_W 65536 +#define CMPLOG_MAP_H 32 + +#define CMPLOG_KIND_INS 0 +#define CMPLOG_KIND_RTN 1 + +typedef struct CmpLogHeader { + uint16_t hits; + uint8_t shape; + uint8_t kind; +} CmpLogHeader; + +typedef struct CmpLogOperands { + uint64_t v0; + uint64_t v1; +} CmpLogOperands; + +typedef struct CmpLogMap { + CmpLogHeader headers[CMPLOG_MAP_W]; + CmpLogOperands operands[CMPLOG_MAP_W][CMPLOG_MAP_H]; +} CmpLogMap; + +extern CmpLogMap libafl_cmplog_map; + +extern uint8_t libafl_cmplog_enabled; + +static void __libafl_targets_cmplog(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) { + + if (!libafl_cmplog_enabled) return; + + uint16_t hits; + if (libafl_cmplog_map.headers[k].kind != CMPLOG_KIND_INS) { + libafl_cmplog_map.headers[k].kind = CMPLOG_KIND_INS; + libafl_cmplog_map.headers[k].hits = 1; + libafl_cmplog_map.headers[k].shape = shape; + hits = 0; + } else { + hits = libafl_cmplog_map.headers[k].hits++; + if (libafl_cmplog_map.headers[k].shape < shape) { + libafl_cmplog_map.headers[k].shape = shape; + } + } + + hits &= CMPLOG_MAP_H - 1; + libafl_cmplog_map.operands[k][hits].v0 = arg1; + libafl_cmplog_map.operands[k][hits].v1 = arg2; + +} + +#endif diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs index b266982e85..77b7e2671c 100644 --- a/libafl_targets/src/cmplog.rs +++ b/libafl_targets/src/cmplog.rs @@ -1,6 +1,16 @@ //! `CmpLog` logs and reports back values touched during fuzzing. //! The values will then be used in subsequent mutations. +use libafl::{ + bolts::{ownedref::OwnedRefMut, tuples::Named}, + executors::HasExecHooks, + observers::{CmpMap, CmpObserver, CmpValues, Observer}, + state::HasMetadata, + Error, +}; + +use serde::{Deserialize, Serialize}; + // TODO compile time flag /// The `CmpLogMap` W value pub const CMPLOG_MAP_W: usize = 65536; @@ -9,6 +19,8 @@ pub const CMPLOG_MAP_H: usize = 32; /// The `CmpLog` map size pub const CMPLOG_MAP_SIZE: usize = CMPLOG_MAP_W * CMPLOG_MAP_H; +big_array! { BigArray; } + /// `CmpLog` instruction kind pub const CMPLOG_KIND_INS: u8 = 0; /// `CmpLog` return kind @@ -16,7 +28,7 @@ pub const CMPLOG_KIND_RTN: u8 = 1; /// The header for `CmpLog` hits. #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy)] pub struct CmpLogHeader { hits: u16, shape: u8, @@ -25,17 +37,94 @@ pub struct CmpLogHeader { /// The operands logged during `CmpLog`. #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy)] pub struct CmpLogOperands(u64, u64); /// A struct containing the `CmpLog` metadata for a `LibAFL` run. #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct CmpLogMap { + #[serde(with = "BigArray")] headers: [CmpLogHeader; CMPLOG_MAP_W], + #[serde(with = "BigArray")] operands: [[CmpLogOperands; CMPLOG_MAP_H]; CMPLOG_MAP_W], } +impl Default for CmpLogMap { + fn default() -> Self { + Self { + headers: [CmpLogHeader { + hits: 0, + shape: 0, + kind: 0, + }; CMPLOG_MAP_W], + operands: [[CmpLogOperands(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W], + } + } +} + +impl CmpMap for CmpLogMap { + fn len(&self) -> usize { + CMPLOG_MAP_W + } + + fn executions_for(&self, idx: usize) -> usize { + self.headers[idx].hits as usize + } + + fn usable_executions_for(&self, idx: usize) -> usize { + if self.executions_for(idx) < CMPLOG_MAP_H { + self.executions_for(idx) + } else { + CMPLOG_MAP_H + } + } + + fn values_of(&self, idx: usize, execution: usize) -> CmpValues { + if self.headers[idx].kind == CMPLOG_KIND_INS { + match self.headers[idx].shape { + 1 => { + return CmpValues::U8(( + self.operands[idx][execution].0 as u8, + self.operands[idx][execution].1 as u8, + )) + } + 2 => { + return CmpValues::U16(( + self.operands[idx][execution].0 as u16, + self.operands[idx][execution].1 as u16, + )) + } + 4 => { + return CmpValues::U32(( + self.operands[idx][execution].0 as u32, + self.operands[idx][execution].1 as u32, + )) + } + 8 => { + return CmpValues::U64(( + self.operands[idx][execution].0 as u64, + self.operands[idx][execution].1 as u64, + )) + } + _ => {} + }; + } + // TODO bytes + CmpValues::Bytes((vec![], vec![])) + } + + fn reset(&mut self) -> Result<(), Error> { + self.headers = [CmpLogHeader { + hits: 0, + shape: 0, + kind: 0, + }; CMPLOG_MAP_W]; + self.operands = [[CmpLogOperands(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W]; + Ok(()) + } +} + /// The global `CmpLog` map for the current `LibAFL` run. #[no_mangle] pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap { @@ -54,3 +143,88 @@ pub use libafl_cmplog_map as CMPLOG_MAP; pub static mut libafl_cmplog_enabled: u8 = 0; pub use libafl_cmplog_enabled as CMPLOG_ENABLED; + +/// A [`CmpObserver`] observer for `CmpLog` +#[derive(Serialize, Deserialize, Debug)] +pub struct CmpLogObserver<'a> { + map: OwnedRefMut<'a, CmpLogMap>, + size: Option>, + add_meta: bool, + name: String, +} + +impl<'a> CmpObserver for CmpLogObserver<'a> { + /// Get the number of usable cmps (all by default) + fn usable_count(&self) -> usize { + match &self.size { + None => self.map().len(), + Some(o) => *o.as_ref(), + } + } + + fn map(&self) -> &CmpLogMap { + self.map.as_ref() + } + + fn map_mut(&mut self) -> &mut CmpLogMap { + self.map.as_mut() + } +} + +impl<'a> Observer for CmpLogObserver<'a> {} + +impl<'a, EM, I, S, Z> HasExecHooks for CmpLogObserver<'a> +where + S: HasMetadata, +{ + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { + self.map.as_mut().reset()?; + unsafe { + CMPLOG_ENABLED = 1; + } + Ok(()) + } + + fn post_exec( + &mut self, + _fuzzer: &mut Z, + state: &mut S, + _mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { + unsafe { + CMPLOG_ENABLED = 0; + } + if self.add_meta { + self.add_cmpvalues_meta(state); + } + Ok(()) + } +} + +impl<'a> Named for CmpLogObserver<'a> { + fn name(&self) -> &str { + &self.name + } +} + +impl<'a> CmpLogObserver<'a> { + /// Creates a new [`CmpLogObserver`] with the given name. + #[must_use] + pub fn new(name: &'static str, map: &'a mut CmpLogMap, add_meta: bool) -> Self { + Self { + name: name.to_string(), + size: None, + add_meta, + map: OwnedRefMut::Ref(map), + } + } + + // TODO with_size +} diff --git a/libafl_targets/src/common.h b/libafl_targets/src/common.h new file mode 100644 index 0000000000..25f01dd74c --- /dev/null +++ b/libafl_targets/src/common.h @@ -0,0 +1,29 @@ +#ifndef __LIBAFL_TARGETS_COMMON__ +#define __LIBAFL_TARGETS_COMMON__ + +#include + +#ifdef _WIN32 + #define RETADDR (uintptr_t)_ReturnAddress() + #define EXPORT_FN __declspec(dllexport) +#else + #define RETADDR (uintptr_t)__builtin_return_address(0) + #define EXPORT_FN +#endif + +#ifdef __GNUC__ + #define MAX(a, b) \ + ({ \ + \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + \ + }) +#else + #define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + + + +#endif diff --git a/libafl_targets/src/coverage.rs b/libafl_targets/src/coverage.rs new file mode 100644 index 0000000000..bdab4917a6 --- /dev/null +++ b/libafl_targets/src/coverage.rs @@ -0,0 +1,10 @@ +//! Coverage maps as static mut array + +// TODO compile time flag +/// The map size for the edges map. +pub const EDGES_MAP_SIZE: usize = 65536; + +/// The map for edges. +pub static mut EDGES_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; +/// The max count of edges tracked. +pub static mut MAX_EDGES_NUM: usize = 0; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index c03c92017a..4d9130ab0c 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,27 +1,25 @@ //! `libafl_targets` contains runtime code, injected in the target itself during compilation. -#[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] -pub mod pcguard; -#[cfg(any(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] -pub use pcguard::*; +#[macro_use] +extern crate serde_big_array; + +#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] +pub mod sancov_pcguard; +#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] +pub use sancov_pcguard::*; #[cfg(feature = "libfuzzer")] pub mod libfuzzer; #[cfg(feature = "libfuzzer")] pub use libfuzzer::*; -#[cfg(all(feature = "value_profile", feature = "cmplog"))] -#[cfg(not(any(doc, feature = "clippy")))] -compile_error!("the libafl_targets `value_profile` and `cmplog` features are mutually exclusive."); +pub mod coverage; +pub use coverage::*; -#[cfg(feature = "value_profile")] pub mod value_profile; -#[cfg(feature = "value_profile")] pub use value_profile::*; -#[cfg(feature = "cmplog")] pub mod cmplog; -#[cfg(feature = "cmplog")] pub use cmplog::*; pub mod drcov; diff --git a/libafl_targets/src/libfuzzer_compatibility.c b/libafl_targets/src/libfuzzer_compatibility.c index 77a61e9df4..ab336870af 100644 --- a/libafl_targets/src/libfuzzer_compatibility.c +++ b/libafl_targets/src/libfuzzer_compatibility.c @@ -13,8 +13,6 @@ #define LIBFUZZER_MSVC 0 #endif // _MSC_VER -#define EXPORT_FN __declspec(dllexport) - // From Libfuzzer // Intermediate macro to ensure the parameter is expanded before stringified. #define STRINGIFY_(A) #A @@ -82,4 +80,4 @@ EXPORT_FN int libafl_targets_libfuzzer_init(int *argc, char ***argv) { } else { return 0; } -} \ No newline at end of file +} diff --git a/libafl_targets/src/value_profile.c b/libafl_targets/src/sancov_cmp.c similarity index 53% rename from libafl_targets/src/value_profile.c rename to libafl_targets/src/sancov_cmp.c index fb0b45d793..1066557a61 100644 --- a/libafl_targets/src/value_profile.c +++ b/libafl_targets/src/sancov_cmp.c @@ -1,35 +1,11 @@ -#include -#include -#include +#include "common.h" -// TODO compile time flag -#define MAP_SIZE 65536 - -extern uint8_t libafl_cmp_map[MAP_SIZE]; - -#ifdef _WIN32 -#define RETADDR (uintptr_t)_ReturnAddress() -#else -#define RETADDR (uintptr_t)__builtin_return_address(0) +#ifdef SANCOV_VALUE_PROFILE +#include "value_profile.h" #endif -#ifdef __GNUC__ -#define MAX(a, b) \ - ({ \ - \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - \ - }) -#else -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifdef _MSC_VER -#include -#define __builtin_popcount __popcnt -#define __builtin_popcountll __popcnt64 +#ifdef SANCOV_CMPLOG +#include "cmplog.h" #endif #if defined(__APPLE__) @@ -56,8 +32,15 @@ void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); + +#ifdef SANCOV_VALUE_PROFILE k &= MAP_SIZE - 1; - libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + __libafl_targets_value_profile1(k, arg1, arg2); +#endif +#ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; + __libafl_targets_cmplog(k, 1, (uint64_t)arg1, (uint64_t)arg2); +#endif } @@ -65,8 +48,15 @@ void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); + +#ifdef SANCOV_VALUE_PROFILE k &= MAP_SIZE - 1; - libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + __libafl_targets_value_profile2(k, arg1, arg2); +#endif +#ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; + __libafl_targets_cmplog(k, 2, (uint64_t)arg1, (uint64_t)arg2); +#endif } @@ -74,8 +64,15 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); + +#ifdef SANCOV_VALUE_PROFILE k &= MAP_SIZE - 1; - libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + __libafl_targets_value_profile4(k, arg1, arg2); +#endif +#ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; + __libafl_targets_cmplog(k, 4, (uint64_t)arg1, (uint64_t)arg2); +#endif } @@ -83,35 +80,51 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) { uintptr_t k = RETADDR; k = (k >> 4) ^ (k << 8); + +#ifdef SANCOV_VALUE_PROFILE k &= MAP_SIZE - 1; - libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcountll(~(arg1 ^ arg2)))); + __libafl_targets_value_profile8(k, arg1, arg2); +#endif +#ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; + __libafl_targets_cmplog(k, 8, (uint64_t)arg1, (uint64_t)arg2); +#endif } void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) { uintptr_t rt = RETADDR; - if (cases[1] == 64) { - for (uint64_t i = 0; i < cases[0]; i++) { + // if (!cases[1]) return; - uintptr_t k = rt + i; - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcountll(~(val ^ cases[i + 2])))); + for (uint64_t i = 0; i < cases[0]; i++) { + uintptr_t k = rt + i; + k = (k >> 4) ^ (k << 8); + // val , cases[i + 2] +#ifdef SANCOV_VALUE_PROFILE + k &= MAP_SIZE - 1; + switch (cases[1]) { + case 8: + __libafl_targets_value_profile1(k, (uint8_t)val, (uint8_t)cases[i + 2]); + break; + case 16: + __libafl_targets_value_profile2(k, (uint16_t)val, (uint16_t)cases[i + 2]); + break; + case 32: + __libafl_targets_value_profile4(k, (uint32_t)val, (uint32_t)cases[i + 2]); + break; + default: + __libafl_targets_value_profile8(k, val, cases[i + 2]); + break; } +#endif +#ifdef SANCOV_CMPLOG + k &= CMPLOG_MAP_W - 1; + __libafl_targets_cmplog(k, cases[1] / 8, val, cases[i + 2]); +#endif - } else { - - for (uint64_t i = 0; i < cases[0]; i++) { - - uintptr_t k = rt + i; - k = (k >> 4) ^ (k << 8); - k &= MAP_SIZE - 1; - libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(val ^ cases[i + 2])))); - - } } diff --git a/libafl_targets/src/pcguard.rs b/libafl_targets/src/sancov_pcguard.rs similarity index 72% rename from libafl_targets/src/pcguard.rs rename to libafl_targets/src/sancov_pcguard.rs index 789b8c431b..6d5f6dcc19 100644 --- a/libafl_targets/src/pcguard.rs +++ b/libafl_targets/src/sancov_pcguard.rs @@ -1,34 +1,27 @@ //! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`. -#[cfg(all(feature = "pcguard_edges", feature = "pcguard_hitcounts"))] +use crate::coverage::{EDGES_MAP, EDGES_MAP_SIZE, MAX_EDGES_NUM}; + +#[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] #[cfg(not(any(doc, feature = "clippy")))] compile_error!( "the libafl_targets `pcguard_edges` and `pcguard_hitcounts` features are mutually exclusive." ); -// TODO compile time flag -/// The map size for `SanCov` edges. -pub const EDGES_MAP_SIZE: usize = 65536; - -/// The map for `SanCov` edges. -pub static mut EDGES_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; -//pub static mut CMP_MAP: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE]; -/// The max count of edges tracked. -pub static mut MAX_EDGES_NUM: usize = 0; - /// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge. /// /// # Safety /// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position. /// Should usually not be called directly. +#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] #[no_mangle] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { let pos = *guard as usize; - #[cfg(feature = "pcguard_edges")] + #[cfg(feature = "sancov_pcguard_edges")] { *EDGES_MAP.get_unchecked_mut(pos) = 1; } - #[cfg(feature = "pcguard_hitcounts")] + #[cfg(feature = "sancov_pcguard_hitcounts")] { let val = (*EDGES_MAP.get_unchecked(pos) as u8).wrapping_add(1); *EDGES_MAP.get_unchecked_mut(pos) = val; @@ -39,6 +32,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { /// /// # Safety /// Dereferences at `start` and writes to it. +#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] #[no_mangle] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { if start == stop || *start != 0 { diff --git a/libafl_targets/src/value_profile.h b/libafl_targets/src/value_profile.h new file mode 100644 index 0000000000..1b810036f9 --- /dev/null +++ b/libafl_targets/src/value_profile.h @@ -0,0 +1,41 @@ +#ifndef __LIBAFL_TARGETS_VALUE_PROFILE__ +#define __LIBAFL_TARGETS_VALUE_PROFILE__ + +#include "common.h" + +// TODO compile time flag +#define MAP_SIZE 65536 + +extern uint8_t libafl_cmp_map[MAP_SIZE]; + +#ifdef _MSC_VER + #include + #define __builtin_popcount __popcnt + #define __builtin_popcountll __popcnt64 +#endif + +static void __libafl_targets_value_profile1(uintptr_t k, uint8_t arg1, uint8_t arg2) { + + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + +} + +static void __libafl_targets_value_profile2(uintptr_t k, uint16_t arg1, uint16_t arg2) { + + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + +} + +static void __libafl_targets_value_profile4(uintptr_t k, uint32_t arg1, uint32_t arg2) { + + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcount(~(arg1 ^ arg2)))); + +} + +static void __libafl_targets_value_profile8(uintptr_t k, uint64_t arg1, uint64_t arg2) { + + libafl_cmp_map[k] = MAX(libafl_cmp_map[k], (__builtin_popcountll(~(arg1 ^ arg2)))); + +} + +#endif