Basic CmpLog (#113)
* libafl_targets: refactor sancov trace-pc * cmp observer * libaf_targets: new structure to isolate sancov * fix C warning * combined executor * cmp observer and feedback * I2SRandReplace mutator * impl CmpMap for CmpLogMap in libafl_targets * cmplog observer * clippy * TracingStage * working random cmplog mutations * enable cmplog for libfuzzer_stb_image * re-enable new testcase stats print * fix update stats display * bump 0.3.1 * clippy * clippy * no clippy for fuzzers/ * fix Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
dacfee9be8
commit
acc5ed42a2
@ -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
|
||||
done
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "baby_fuzzer"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "frida_libpng"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
edition = "2018"
|
||||
build = "build.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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libfuzzer_libmozjpeg"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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/" }
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libfuzzer_libpng"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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/" }
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libfuzzer_libpng_launcher"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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"] }
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libfuzzer_reachability"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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/" }
|
||||
|
||||
|
@ -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::<Tokens>().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));
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libfuzzer_stb_image"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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"] }
|
||||
|
@ -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")
|
||||
|
@ -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::<Tokens>().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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libafl"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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"
|
||||
|
@ -308,10 +308,10 @@ where
|
||||
}
|
||||
|
||||
/// Allows prepending of values to a tuple
|
||||
pub trait Prepend<T>: TupleList {
|
||||
pub trait Prepend<T> {
|
||||
/// 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<T>: TupleList {
|
||||
}
|
||||
|
||||
/// Implement prepend for tuple list.
|
||||
impl<Tail, T> Prepend<T> for Tail
|
||||
where
|
||||
Tail: TupleList,
|
||||
{
|
||||
impl<Tail, T> Prepend<T> 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<T>: TupleList {
|
||||
/// Append to a tuple
|
||||
pub trait Append<T> {
|
||||
/// 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<T> Append<T> for () {
|
||||
/// Implement append for non-empty tuple list.
|
||||
impl<Head, Tail, T> Append<T> for (Head, Tail)
|
||||
where
|
||||
Self: TupleList,
|
||||
Tail: Append<T>,
|
||||
(Head, Tail::AppendResult): TupleList,
|
||||
{
|
||||
type AppendResult = (Head, Tail::AppendResult);
|
||||
|
||||
@ -365,6 +360,38 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge two `TupeList`
|
||||
pub trait Merge<T> {
|
||||
/// 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<T> Merge<T> for () {
|
||||
type MergeResult = T;
|
||||
|
||||
fn merge(self, value: T) -> Self::MergeResult {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement merge for non-empty tuple list.
|
||||
impl<Head, Tail, T> Merge<T> for (Head, Tail)
|
||||
where
|
||||
Tail: Merge<T>,
|
||||
{
|
||||
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 {
|
||||
|
@ -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")]
|
||||
|
117
libafl/src/executors/combined.rs
Normal file
117
libafl/src/executors/combined.rs
Normal file
@ -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<A, B, I>
|
||||
where
|
||||
A: Executor<I>,
|
||||
B: Executor<I>,
|
||||
I: Input,
|
||||
{
|
||||
primary: A,
|
||||
secondary: B,
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<A, B, I> CombinedExecutor<A, B, I>
|
||||
where
|
||||
A: Executor<I>,
|
||||
B: Executor<I>,
|
||||
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<A, B, I> Executor<I> for CombinedExecutor<A, B, I>
|
||||
where
|
||||
A: Executor<I>,
|
||||
B: Executor<I>,
|
||||
I: Input,
|
||||
{
|
||||
fn run_target(&mut self, input: &I) -> Result<ExitKind, Error> {
|
||||
self.primary.run_target(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, I, OT> HasObservers<OT> for CombinedExecutor<A, B, I>
|
||||
where
|
||||
A: Executor<I> + HasObservers<OT>,
|
||||
B: Executor<I>,
|
||||
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<A, B, EM, I, OT, S, Z> HasObserversHooks<EM, I, OT, S, Z> for CombinedExecutor<A, B, I>
|
||||
where
|
||||
A: Executor<I> + HasObservers<OT>,
|
||||
B: Executor<I>,
|
||||
I: Input,
|
||||
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<A, B, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CombinedExecutor<A, B, I>
|
||||
where
|
||||
A: Executor<I> + HasExecHooks<EM, I, S, Z>,
|
||||
B: Executor<I>,
|
||||
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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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<C, I, R, S>() -> impl MutatorsTuple<I, S>
|
||||
pub fn havoc_mutations<C, I, R, S>() -> tuple_list_type!(
|
||||
BitFlipMutator<I, R, S>,
|
||||
ByteFlipMutator<I, R, S>,
|
||||
ByteIncMutator<I, R, S>,
|
||||
ByteDecMutator<I, R, S>,
|
||||
ByteNegMutator<I, R, S>,
|
||||
ByteRandMutator<I, R, S>,
|
||||
ByteAddMutator<I, R, S>,
|
||||
WordAddMutator<I, R, S>,
|
||||
DwordAddMutator<I, R, S>,
|
||||
QwordAddMutator<I, R, S>,
|
||||
ByteInterestingMutator<I, R, S>,
|
||||
WordInterestingMutator<I, R, S>,
|
||||
DwordInterestingMutator<I, R, S>,
|
||||
BytesDeleteMutator<I, R, S>,
|
||||
BytesDeleteMutator<I, R, S>,
|
||||
BytesDeleteMutator<I, R, S>,
|
||||
BytesDeleteMutator<I, R, S>,
|
||||
BytesExpandMutator<I, R, S>,
|
||||
BytesInsertMutator<I, R, S>,
|
||||
BytesRandInsertMutator<I, R, S>,
|
||||
BytesSetMutator<I, R, S>,
|
||||
BytesRandSetMutator<I, R, S>,
|
||||
BytesCopyMutator<I, R, S>,
|
||||
BytesSwapMutator<I, R, S>,
|
||||
CrossoverInsertMutator<C, I, R, S>,
|
||||
CrossoverReplaceMutator<C, I, R, S>,
|
||||
)
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + 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<C, I, R, S>(
|
||||
) -> tuple_list_type!(TokenInsert<I, R, S>, TokenReplace<I, R, S>)
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand<R> + HasCorpus<C, I> + HasMetadata + HasMaxSize,
|
||||
C: Corpus<I>,
|
||||
R: Rand,
|
||||
{
|
||||
tuple_list!(TokenInsert::new(), TokenReplace::new(),)
|
||||
}
|
||||
|
||||
/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`].
|
||||
pub struct LoggerScheduledMutator<C, I, MT, R, S, SM>
|
||||
where
|
||||
|
@ -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<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
phantom: PhantomData<(I, R, S)>,
|
||||
}
|
||||
|
||||
impl<I, R, S> Mutator<I, S> for I2SRandReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32,
|
||||
) -> Result<MutationResult, Error> {
|
||||
let size = input.bytes().len();
|
||||
if size == 0 {
|
||||
return Ok(MutationResult::Skipped);
|
||||
}
|
||||
|
||||
let cmps_len = {
|
||||
let meta = state.metadata().get::<CmpValuesMetadata>();
|
||||
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::<CmpValuesMetadata>().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::<u16>() {
|
||||
for i in off..len - (size_of::<u16>() - 1) {
|
||||
let val =
|
||||
u16::from_ne_bytes(bytes[i..i + size_of::<u16>()].try_into().unwrap());
|
||||
if val == v.0 {
|
||||
let new_bytes = v.1.to_ne_bytes();
|
||||
bytes[i..i + size_of::<u16>()].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::<u16>()].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::<u16>()].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::<u16>()].copy_from_slice(&new_bytes);
|
||||
result = MutationResult::Mutated;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CmpValues::U32(v) => {
|
||||
if len >= size_of::<u32>() {
|
||||
for i in off..len - (size_of::<u32>() - 1) {
|
||||
let val =
|
||||
u32::from_ne_bytes(bytes[i..i + size_of::<u32>()].try_into().unwrap());
|
||||
if val == v.0 {
|
||||
let new_bytes = v.1.to_ne_bytes();
|
||||
bytes[i..i + size_of::<u32>()].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::<u32>()].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::<u32>()].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::<u32>()].copy_from_slice(&new_bytes);
|
||||
result = MutationResult::Mutated;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CmpValues::U64(v) => {
|
||||
if len >= size_of::<u64>() {
|
||||
for i in off..len - (size_of::<u64>() - 1) {
|
||||
let val =
|
||||
u64::from_ne_bytes(bytes[i..i + size_of::<u64>()].try_into().unwrap());
|
||||
if val == v.0 {
|
||||
let new_bytes = v.1.to_ne_bytes();
|
||||
bytes[i..i + size_of::<u64>()].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::<u64>()].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::<u64>()].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::<u64>()].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<I, R, S> Named for I2SRandReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"I2SRandReplace"
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, R, S> I2SRandReplace<I, R, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasMetadata + HasRand<R> + HasMaxSize,
|
||||
R: Rand,
|
||||
{
|
||||
/// Creates a new `I2SRandReplace` struct.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(feature = "std")]
|
||||
|
254
libafl/src/observers/cmp.rs
Normal file
254
libafl/src/observers/cmp.rs
Normal file
@ -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<u8>, Vec<u8>)),
|
||||
}
|
||||
|
||||
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<CmpValues>,
|
||||
}
|
||||
|
||||
crate::impl_serdeany!(CmpValuesMetadata);
|
||||
|
||||
impl AsSlice<CmpValues> 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<CM>: 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<S>(&mut self, state: &mut S)
|
||||
where
|
||||
S: HasMetadata,
|
||||
{
|
||||
if state.metadata().get::<CmpValuesMetadata>().is_none() {
|
||||
state.add_metadata(CmpValuesMetadata::new());
|
||||
}
|
||||
let meta = state.metadata_mut().get_mut::<CmpValuesMetadata>().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<CmpValues> = 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<OwnedRefMut<'a, usize>>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl<'a, CM> CmpObserver<CM> 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<EM, I, S, Z> 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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;
|
||||
|
113
libafl/src/stages/tracing.rs
Normal file
113
libafl/src/stages/tracing.rs
Normal file
@ -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<C, EM, I, OT, S, TE, Z>
|
||||
where
|
||||
I: Input,
|
||||
C: Corpus<I>,
|
||||
TE: Executor<I>
|
||||
+ HasObservers<OT>
|
||||
+ HasExecHooks<EM, I, S, Z>
|
||||
+ HasObserversHooks<EM, I, OT, S, Z>,
|
||||
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
|
||||
S: HasClientPerfStats + HasExecutions + HasCorpus<C, I>,
|
||||
{
|
||||
tracer_executor: TE,
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(C, EM, I, OT, S, TE, Z)>,
|
||||
}
|
||||
|
||||
impl<E, C, EM, I, OT, S, TE, Z> Stage<E, EM, S, Z> for TracingStage<C, EM, I, OT, S, TE, Z>
|
||||
where
|
||||
I: Input,
|
||||
C: Corpus<I>,
|
||||
TE: Executor<I>
|
||||
+ HasObservers<OT>
|
||||
+ HasExecHooks<EM, I, S, Z>
|
||||
+ HasObserversHooks<EM, I, OT, S, Z>,
|
||||
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
|
||||
S: HasClientPerfStats + HasExecutions + HasCorpus<C, I>,
|
||||
{
|
||||
#[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<C, EM, I, OT, S, TE, Z> TracingStage<C, EM, I, OT, S, TE, Z>
|
||||
where
|
||||
I: Input,
|
||||
C: Corpus<I>,
|
||||
TE: Executor<I>
|
||||
+ HasObservers<OT>
|
||||
+ HasExecHooks<EM, I, S, Z>
|
||||
+ HasObserversHooks<EM, I, OT, S, Z>,
|
||||
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
|
||||
S: HasClientPerfStats + HasExecutions + HasCorpus<C, I>,
|
||||
{
|
||||
/// Creates a new default mutational stage
|
||||
pub fn new(tracer_executor: TE) -> Self {
|
||||
Self {
|
||||
tracer_executor,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libafl_cc"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||
description = "Commodity library to wrap compilers and link LibAFL"
|
||||
documentation = "https://docs.rs/libafl_cc"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libafl_derive"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||
description = "Derive proc-macro crate for LibAFL"
|
||||
documentation = "https://docs.rs/libafl_derive"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libafl_frida"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["s1341 <github@shmarya.net>"]
|
||||
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"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libafl_targets"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
|
||||
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"
|
||||
|
@ -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");
|
||||
|
@ -1,197 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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];
|
||||
|
||||
}
|
||||
|
||||
}
|
55
libafl_targets/src/cmplog.h
Normal file
55
libafl_targets/src/cmplog.h
Normal file
@ -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
|
@ -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<OwnedRefMut<'a, usize>>,
|
||||
add_meta: bool,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl<'a> CmpObserver<CmpLogMap> 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<EM, I, S, Z> 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
|
||||
}
|
||||
|
29
libafl_targets/src/common.h
Normal file
29
libafl_targets/src/common.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __LIBAFL_TARGETS_COMMON__
|
||||
#define __LIBAFL_TARGETS_COMMON__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
10
libafl_targets/src/coverage.rs
Normal file
10
libafl_targets/src/coverage.rs
Normal file
@ -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;
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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 <intrin.h>
|
||||
#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]))));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 {
|
41
libafl_targets/src/value_profile.h
Normal file
41
libafl_targets/src/value_profile.h
Normal file
@ -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 <intrin.h>
|
||||
#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
|
Loading…
x
Reference in New Issue
Block a user