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:
Andrea Fioraldi 2021-05-20 14:05:20 +02:00 committed by GitHub
parent dacfee9be8
commit acc5ed42a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1256 additions and 388 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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/" }

View File

@ -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

View File

@ -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/" }

View File

@ -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

View File

@ -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"] }

View File

@ -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

View File

@ -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/" }

View File

@ -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));

View File

@ -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"] }

View File

@ -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")

View File

@ -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

View File

@ -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"

View File

@ -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 {

View File

@ -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")]

View 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)
}
}

View File

@ -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;

View File

@ -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

View File

@ -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
View 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),
}
}
}

View File

@ -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};

View File

@ -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;

View 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,
}
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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");

View File

@ -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];
}
}

View 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

View File

@ -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
}

View 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

View 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;

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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]))));
}
}

View File

@ -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 {

View 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