From af3d321213973189dfbef864d4035974326d095e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 3 Jan 2022 00:47:17 +0100 Subject: [PATCH] Derive debug for all structs in LibAFL (#442) * documentation, warnings * fixed docs * docs * no_std * test * windows * nautilus docs * more fixes * more docs * nits * windows clippy * docs, windows * nits * debug all the things * derive debug for all core library components * Docu for libafl_targets * nits * reordered generics * add docs to frida, debug * nits * fixes * more docu for frida, nits * more docu * more docu * Sugar docs * debug for qemu * more debug * import debug * fmt * debug * anyap_debug feature no longer needed * tidy up unused fn * indicate if we left out values for struct debug * implement Debug for sugar * debug allthethings * ci --- docs/src/design/metadata.md | 2 +- fuzzers/forkserver_simple/src/main.rs | 4 +- fuzzers/frida_libpng/src/fuzzer.rs | 24 ------- fuzzers/libfuzzer_libpng_ctx/Cargo.toml | 2 +- fuzzers/libfuzzer_libpng_launcher/Cargo.toml | 2 +- fuzzers/tutorial/src/metadata.rs | 2 +- libafl/Cargo.toml | 5 +- libafl/src/bolts/launcher.rs | 22 ++++++ libafl/src/bolts/serdeany.rs | 22 +++--- libafl/src/executors/combined.rs | 8 ++- libafl/src/executors/command.rs | 29 +++++--- libafl/src/executors/forkserver.rs | 37 +++++++--- libafl/src/executors/inprocess.rs | 39 +++++++++- libafl/src/executors/mod.rs | 6 +- libafl/src/executors/shadow.rs | 21 ++++-- libafl/src/executors/timeout.rs | 39 +++++++++- libafl/src/executors/with_observers.rs | 19 +++-- libafl/src/feedbacks/map.rs | 8 +-- libafl/src/feedbacks/mod.rs | 34 ++++++--- libafl/src/mutators/mopt_mutator.rs | 2 +- libafl/src/observers/cmp.rs | 4 +- .../concolic/serialization_format.rs | 25 ++++++- libafl/src/observers/map.rs | 2 +- libafl/src/observers/mod.rs | 6 +- libafl/src/stages/tracing.rs | 4 +- libafl_cc/build.rs | 4 ++ libafl_cc/src/clang.rs | 10 ++- libafl_cc/src/lib.rs | 43 +++++++++++ libafl_derive/src/lib.rs | 47 +++++++++++- libafl_frida/src/alloc.rs | 47 ++++++++++-- libafl_frida/src/asan/asan_rt.rs | 28 +++++++- libafl_frida/src/asan/errors.rs | 3 +- libafl_frida/src/asan/hook_funcs.rs | 6 ++ libafl_frida/src/asan/mod.rs | 2 + libafl_frida/src/cmplog_rt.rs | 44 +++++++++--- libafl_frida/src/coverage_rt.rs | 9 +++ libafl_frida/src/drcov_rt.rs | 2 +- libafl_frida/src/executor.rs | 26 +++++-- libafl_frida/src/helper.rs | 72 ++++++++++--------- libafl_frida/src/lib.rs | 43 +++++++++++ libafl_qemu/src/asan.rs | 1 + libafl_qemu/src/cmplog.rs | 3 +- libafl_qemu/src/edges.rs | 3 +- libafl_qemu/src/emu.rs | 1 + libafl_qemu/src/executor.rs | 24 ++++++- libafl_qemu/src/helper.rs | 8 ++- libafl_qemu/src/snapshot.rs | 2 + libafl_sugar/src/forkserver.rs | 15 +++- libafl_sugar/src/inmemory.rs | 42 ++++++++++- libafl_sugar/src/lib.rs | 47 ++++++++++++ libafl_sugar/src/qemu.rs | 42 ++++++++++- libafl_targets/src/cmplog.rs | 19 ++++- libafl_targets/src/coverage.rs | 6 +- libafl_targets/src/drcov.rs | 3 + libafl_targets/src/lib.rs | 45 ++++++++++++ libafl_targets/src/sancov_8bit.rs | 2 + libafl_targets/src/sancov_cmp.rs | 10 +++ libafl_targets/src/sancov_pcguard.rs | 2 +- 58 files changed, 848 insertions(+), 181 deletions(-) diff --git a/docs/src/design/metadata.md b/docs/src/design/metadata.md index cbc84e05d1..aa3ffaa094 100644 --- a/docs/src/design/metadata.md +++ b/docs/src/design/metadata.md @@ -11,7 +11,7 @@ extern crate serde; use libafl::SerdeAny; use serde::{Serialize, Deserialize}; -#[derive(Serialize, Deserialize, SerdeAny)] +#[derive(Debug, Serialize, Deserialize, SerdeAny)] pub struct MyMetadata { //... } diff --git a/fuzzers/forkserver_simple/src/main.rs b/fuzzers/forkserver_simple/src/main.rs index 6f0dbb6f04..130a5ec9ea 100644 --- a/fuzzers/forkserver_simple/src/main.rs +++ b/fuzzers/forkserver_simple/src/main.rs @@ -65,12 +65,12 @@ pub fn main() { let mut shmem = StdShMemProvider::new().unwrap().new_map(MAP_SIZE).unwrap(); //let the forkserver know the shmid shmem.write_to_env("__AFL_SHM_ID").unwrap(); - let mut shmem_map = shmem.map_mut(); + let shmem_map = shmem.map_mut(); // Create an observation channel using the signals map let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new( "shared_mem", - &mut shmem_map, + shmem_map, )); // Create an observation channel to keep track of the execution time diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index ccf78c69b1..af7bbe0334 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -54,9 +54,6 @@ use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP}; #[cfg(unix)] use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS}; -fn timeout_from_millis_str(time: &str) -> Result { - Ok(Duration::from_millis(time.parse()?)) -} #[derive(Debug, StructOpt)] #[structopt( @@ -113,27 +110,6 @@ struct Opt { )] output: PathBuf, - /* - #[structopt( - parse(try_from_str = timeout_from_millis_str), - short, - long, - help = "Set the exeucution timeout in milliseconds, default is 1000", - name = "TIMEOUT", - default_value = "1000" - )] - timeout: Duration, - - #[structopt( - parse(from_os_str), - short = "x", - long, - help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"", - name = "TOKENS", - multiple = true - )] - tokens: Vec, - */ #[structopt( long, help = "The configuration this fuzzer runs with, for multiprocessing", diff --git a/fuzzers/libfuzzer_libpng_ctx/Cargo.toml b/fuzzers/libfuzzer_libpng_ctx/Cargo.toml index 452edd9560..ab8ad5a6f0 100644 --- a/fuzzers/libfuzzer_libpng_ctx/Cargo.toml +++ b/fuzzers/libfuzzer_libpng_ctx/Cargo.toml @@ -20,7 +20,7 @@ which = { version = "4.0.2" } num_cpus = "1.0" [dependencies] -libafl = { path = "../../libafl/", features = ["std", "anymap_debug", "derive", "llmp_compression", "introspection"] } +libafl = { path = "../../libafl/", features = ["std", "derive", "llmp_compression", "introspection"] } libafl_targets = { path = "../../libafl_targets/", features = ["libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/libfuzzer_libpng_launcher/Cargo.toml b/fuzzers/libfuzzer_libpng_launcher/Cargo.toml index 7c70283460..f003e64ce9 100644 --- a/fuzzers/libfuzzer_libpng_launcher/Cargo.toml +++ b/fuzzers/libfuzzer_libpng_launcher/Cargo.toml @@ -20,7 +20,7 @@ which = { version = "4.0.2" } num_cpus = "1.0" [dependencies] -libafl = { path = "../../libafl/", features = ["std", "anymap_debug", "derive", "llmp_compression", "introspection"] } +libafl = { path = "../../libafl/", features = ["std", "derive", "llmp_compression", "introspection"] } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/tutorial/src/metadata.rs b/fuzzers/tutorial/src/metadata.rs index a31de01645..4ec380751c 100644 --- a/fuzzers/tutorial/src/metadata.rs +++ b/fuzzers/tutorial/src/metadata.rs @@ -13,7 +13,7 @@ use crate::input::PacketData; use serde::{Deserialize, Serialize}; -#[derive(SerdeAny, Serialize, Deserialize)] +#[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct PacketLenMetadata { pub length: u64, } diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 34a4520ff3..4be578f646 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -12,9 +12,8 @@ edition = "2021" build = "build.rs" [features] -default = ["std", "anymap_debug", "derive", "llmp_compression", "rand_trait", "fork"] +default = ["std", "derive", "llmp_compression", "rand_trait", "fork"] std = ["serde_json", "serde_json/std", "hostname", "core_affinity", "nix", "serde/std", "bincode", "wait-timeout", "regex", "build_id", "uuid"] # print, env, launcher ... support -anymap_debug = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint. derive = ["libafl_derive"] # provide derive(SerdeAny) macro. fork = [] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std). rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng` @@ -60,7 +59,7 @@ ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] } libafl_derive = { version = "0.7.0", optional = true, path = "../libafl_derive" } -serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap +serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } miniz_oxide = { version = "0.5", optional = true} core_affinity = { version = "0.5", git = "https://github.com/s1341/core_affinity_rs", rev = "6648a7a", optional = true } hostname = { version = "^0.3", optional = true } # Is there really no gethostname in the stdlib? diff --git a/libafl/src/bolts/launcher.rs b/libafl/src/bolts/launcher.rs index d1995357e6..33e08b6449 100644 --- a/libafl/src/bolts/launcher.rs +++ b/libafl/src/bolts/launcher.rs @@ -24,6 +24,7 @@ use crate::{ Error, }; +use core::fmt::{self, Debug, Formatter}; #[cfg(feature = "std")] use core::marker::PhantomData; #[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] @@ -85,6 +86,27 @@ where phantom_data: PhantomData<(&'a I, &'a OT, &'a S, &'a SP)>, } +impl<'a, CF, I, MT, OT, S, SP> Debug for Launcher<'_, CF, I, MT, OT, S, SP> +where + CF: FnOnce(Option, LlmpRestartingEventManager, usize) -> Result<(), Error>, + I: Input, + OT: ObserversTuple + DeserializeOwned, + MT: Monitor + Clone, + SP: ShMemProvider + 'static, + S: DeserializeOwned, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Launcher") + .field("configuration", &self.configuration) + .field("broker_port", &self.broker_port) + .field("core", &self.cores) + .field("spawn_broker", &self.spawn_broker) + .field("remote_broker_addr", &self.remote_broker_addr) + .field("stdout_file", &self.stdout_file) + .finish_non_exhaustive() + } +} + #[cfg(feature = "std")] impl<'a, CF, I, MT, OT, S, SP> Launcher<'a, CF, I, MT, OT, S, SP> where diff --git a/libafl/src/bolts/serdeany.rs b/libafl/src/bolts/serdeany.rs index 3a43bd2b49..ed6afd3825 100644 --- a/libafl/src/bolts/serdeany.rs +++ b/libafl/src/bolts/serdeany.rs @@ -3,7 +3,10 @@ use serde::{de::DeserializeSeed, Deserialize, Deserializer, Serialize, Serializer}; use alloc::boxed::Box; -use core::any::{Any, TypeId}; +use core::{ + any::{Any, TypeId}, + fmt::Debug, +}; // yolo @@ -30,7 +33,7 @@ pub fn unpack_type_id(id: TypeId) -> u64 { } /// A (de)serializable Any trait -pub trait SerdeAny: Any + erased_serde::Serialize { +pub trait SerdeAny: Any + erased_serde::Serialize + Debug { /// returns this as Any trait fn as_any(&self) -> &dyn Any; /// returns this as mutable Any trait @@ -40,11 +43,11 @@ pub trait SerdeAny: Any + erased_serde::Serialize { } /// Wrap a type for serialization -#[allow(missing_debug_implementations)] -pub struct Wrap<'a, T: ?Sized>(pub &'a T); +#[derive(Debug)] +pub struct Wrap<'a, T: ?Sized + Debug>(pub &'a T); impl<'a, T> Serialize for Wrap<'a, T> where - T: ?Sized + erased_serde::Serialize + 'a, + T: ?Sized + erased_serde::Serialize + 'a + Debug, { /// Serialize the type fn serialize(&self, serializer: S) -> Result @@ -191,9 +194,9 @@ macro_rules! create_serde_registry_for_trait { } } - #[derive(Serialize, Deserialize)] /// A (de)serializable anymap containing (de)serializable trait objects registered /// in the registry + #[derive(Debug, Serialize, Deserialize)] pub struct SerdeAnyMap { map: HashMap>, } @@ -207,6 +210,7 @@ macro_rules! create_serde_registry_for_trait { } } + /* #[cfg(feature = "anymap_debug")] impl fmt::Debug for SerdeAnyMap { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -220,7 +224,7 @@ macro_rules! create_serde_registry_for_trait { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "SerdeAnymap with {} elements", self.len()) } - } + }*/ #[allow(unused_qualifications)] impl SerdeAnyMap { @@ -318,8 +322,8 @@ macro_rules! create_serde_registry_for_trait { } /// A serializable [`HashMap`] wrapper for [`SerdeAny`] types, addressable by name. - #[allow(unused_qualifications, missing_debug_implementations)] - #[derive(Serialize, Deserialize)] + #[allow(unused_qualifications)] + #[derive(Debug, Serialize, Deserialize)] pub struct NamedSerdeAnyMap { map: HashMap>>, } diff --git a/libafl/src/executors/combined.rs b/libafl/src/executors/combined.rs index 9108ea6f90..8a091832c5 100644 --- a/libafl/src/executors/combined.rs +++ b/libafl/src/executors/combined.rs @@ -6,15 +6,16 @@ use crate::{ observers::ObserversTuple, Error, }; +use core::fmt::Debug; /// A [`CombinedExecutor`] wraps a primary executor, forwarding its methods, and a secondary one -#[allow(missing_debug_implementations)] -pub struct CombinedExecutor { +#[derive(Debug)] +pub struct CombinedExecutor { primary: A, secondary: B, } -impl CombinedExecutor { +impl CombinedExecutor { /// Create a new `CombinedExecutor`, wrapping the given `executor`s. pub fn new(primary: A, secondary: B) -> Self where @@ -56,6 +57,7 @@ where impl HasObservers for CombinedExecutor where A: HasObservers, + B: Debug, OT: ObserversTuple, { #[inline] diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index c0d630da32..24c8525422 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -1,5 +1,8 @@ //! The command executor executes a sub program for each run -use core::marker::PhantomData; +use core::{ + fmt::{self, Debug, Formatter}, + marker::PhantomData, +}; #[cfg(feature = "std")] use std::process::Child; @@ -15,15 +18,23 @@ use std::time::Duration; /// A `CommandExecutor` is a wrapper around [`std::process::Command`] to execute a target as a child process. /// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it. -#[allow(missing_debug_implementations)] -pub struct CommandExecutor { +pub struct CommandExecutor { inner: T, /// [`crate::observers::Observer`]s for this executor observers: OT, phantom: PhantomData<(EM, I, S, Z)>, } -impl CommandExecutor { +impl Debug for CommandExecutor { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CommandExecutor") + .field("inner", &self.inner) + .field("observers", &self.observers) + .finish() + } +} + +impl CommandExecutor { /// Accesses the inner value pub fn inner(&mut self) -> &mut T { &mut self.inner @@ -32,7 +43,7 @@ impl CommandExecutor { // this only works on unix because of the reliance on checking the process signal for detecting OOM #[cfg(all(feature = "std", unix))] -impl Executor for CommandExecutor +impl Executor for CommandExecutor where I: Input, T: CommandConfigurator, @@ -72,7 +83,8 @@ where } #[cfg(all(feature = "std", unix))] -impl HasObservers for CommandExecutor +impl HasObservers + for CommandExecutor where I: Input, OT: ObserversTuple, @@ -94,6 +106,7 @@ where /// ``` /// use std::{io::Write, process::{Stdio, Command, Child}}; /// use libafl::{Error, inputs::{Input, HasTargetBytes}, executors::{Executor, command::CommandConfigurator}}; +/// #[derive(Debug)] /// struct MyExecutor; /// /// impl CommandConfigurator for MyExecutor { @@ -122,7 +135,7 @@ where /// } /// ``` #[cfg(all(feature = "std", unix))] -pub trait CommandConfigurator: Sized { +pub trait CommandConfigurator: Sized + Debug { /// Spawns a new process with the given configuration. fn spawn_child( &mut self, @@ -133,7 +146,7 @@ pub trait CommandConfigurator: Sized { ) -> Result; /// Create an `Executor` from this `CommandConfigurator`. - fn into_executor(self, observers: OT) -> CommandExecutor + fn into_executor(self, observers: OT) -> CommandExecutor where OT: ObserversTuple, { diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 6ed6017554..f80d8eac8c 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1,6 +1,10 @@ //! Expose an `Executor` based on a `Forkserver` in order to execute AFL/AFL++ binaries -use core::{marker::PhantomData, time::Duration}; +use core::{ + fmt::{self, Debug, Formatter}, + marker::PhantomData, + time::Duration, +}; use std::{ fs::{File, OpenOptions}, io::{self, prelude::*, ErrorKind, SeekFrom}, @@ -150,8 +154,9 @@ impl ConfigTarget for Command { } } -/// The [`OutFile`] to write to -#[allow(missing_debug_implementations)] +/// The [`OutFile`] to write input to. +/// The target/forkserver will read from this file. +#[derive(Debug)] pub struct OutFile { /// The file file: File, @@ -369,13 +374,13 @@ pub trait HasForkserver { } /// The timeout forkserver executor that wraps around the standard forkserver executor and sets a timeout before each run. -#[allow(missing_debug_implementations)] -pub struct TimeoutForkserverExecutor { +#[derive(Debug)] +pub struct TimeoutForkserverExecutor { executor: E, timeout: TimeSpec, } -impl TimeoutForkserverExecutor { +impl TimeoutForkserverExecutor { /// Create a new [`TimeoutForkserverExecutor`] pub fn new(executor: E, exec_tmout: Duration) -> Result { let milli_sec = exec_tmout.as_millis() as i64; @@ -384,7 +389,7 @@ impl TimeoutForkserverExecutor { } } -impl Executor for TimeoutForkserverExecutor +impl Executor for TimeoutForkserverExecutor where I: Input + HasTargetBytes, E: Executor + HasForkserver, @@ -482,7 +487,6 @@ where /// This [`Executor`] can run binaries compiled for AFL/AFL++ that make use of a forkserver. /// Shared memory feature is also available, but you have to set things up in your code. /// Please refer to AFL++'s docs. -#[allow(missing_debug_implementations)] pub struct ForkserverExecutor where I: Input + HasTargetBytes, @@ -497,6 +501,23 @@ where phantom: PhantomData<(I, S)>, } +impl Debug for ForkserverExecutor +where + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ForkserverExecutor") + .field("target", &self.target) + .field("args", &self.args) + .field("out_file", &self.out_file) + .field("forkserver", &self.forkserver) + .field("observers", &self.observers) + .field("map", &self.map) + .finish() + } +} + impl ForkserverExecutor where I: Input + HasTargetBytes, diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index a58c827078..ac34079631 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -3,7 +3,12 @@ //! //! Needs the `fork` feature flag. -use core::{ffi::c_void, marker::PhantomData, ptr}; +use core::{ + ffi::c_void, + fmt::{self, Debug, Formatter}, + marker::PhantomData, + ptr, +}; #[cfg(any(unix, all(windows, feature = "std")))] use core::{ @@ -42,7 +47,6 @@ use crate::{ /// The inmem executor simply calls a target function, then returns afterwards. #[allow(dead_code)] -#[derive(Debug)] pub struct InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&I) -> ExitKind, @@ -58,6 +62,20 @@ where phantom: PhantomData<(I, S)>, } +impl<'a, H, I, OT, S> Debug for InProcessExecutor<'a, H, I, OT, S> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("InProcessExecutor") + .field("harness_fn", &"") + .field("observers", &self.observers) + .finish_non_exhaustive() + } +} + impl<'a, EM, H, I, OT, S, Z> Executor for InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&I) -> ExitKind, @@ -982,7 +1000,6 @@ where /// [`InProcessForkExecutor`] is an executor that forks the current process before each execution. #[cfg(all(feature = "std", unix))] -#[allow(missing_debug_implementations)] pub struct InProcessForkExecutor<'a, H, I, OT, S, SP> where H: FnMut(&I) -> ExitKind, @@ -996,6 +1013,22 @@ where phantom: PhantomData<(I, S)>, } +#[cfg(all(feature = "std", unix))] +impl<'a, H, I, OT, S, SP> Debug for InProcessForkExecutor<'a, H, I, OT, S, SP> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, + SP: ShMemProvider, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("InProcessForkExecutor") + .field("observers", &self.observers) + .field("shmem_provider", &self.shmem_provider) + .finish() + } +} + #[cfg(all(feature = "std", unix))] impl<'a, EM, H, I, OT, S, Z, SP> Executor for InProcessForkExecutor<'a, H, I, OT, S, SP> diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 90c4688d65..bf2b977789 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -37,6 +37,7 @@ use crate::{ Error, }; +use core::fmt::Debug; use serde::{Deserialize, Serialize}; /// How an execution finished. @@ -57,7 +58,7 @@ pub enum ExitKind { crate::impl_serdeany!(ExitKind); /// Holds a tuple of Observers -pub trait HasObservers +pub trait HasObservers: Debug where OT: ObserversTuple, { @@ -69,7 +70,7 @@ where } /// An executor takes the given inputs, and runs the harness/target. -pub trait Executor +pub trait Executor: Debug where I: Input, { @@ -97,6 +98,7 @@ where /// A simple executor that does nothing. /// If intput len is 0, `run_target` will return Err +#[derive(Debug)] struct NopExecutor {} impl Executor for NopExecutor diff --git a/libafl/src/executors/shadow.rs b/libafl/src/executors/shadow.rs index 06210ec4bb..fcff96d608 100644 --- a/libafl/src/executors/shadow.rs +++ b/libafl/src/executors/shadow.rs @@ -1,6 +1,9 @@ //! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager -use core::marker::PhantomData; +use core::{ + fmt::{self, Debug, Formatter}, + marker::PhantomData, +}; use crate::{ executors::{Executor, ExitKind, HasObservers}, @@ -10,8 +13,7 @@ use crate::{ }; /// A [`ShadowExecutor`] wraps an executor and a set of shadow observers -#[allow(missing_debug_implementations)] -pub struct ShadowExecutor { +pub struct ShadowExecutor { /// The wrapped executor executor: E, /// The shadow observers @@ -20,7 +22,16 @@ pub struct ShadowExecutor { phantom: PhantomData<(I, S)>, } -impl ShadowExecutor +impl Debug for ShadowExecutor { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ShadowExecutor") + .field("executor", &self.executor) + .field("shadow_observers", &self.shadow_observers) + .finish() + } +} + +impl ShadowExecutor where SOT: ObserversTuple, { @@ -65,6 +76,8 @@ where impl HasObservers for ShadowExecutor where + I: Debug, + S: Debug, E: HasObservers, OT: ObserversTuple, SOT: ObserversTuple, diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 612bed56df..93747bce79 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -1,7 +1,10 @@ //! A `TimeoutExecutor` sets a timeout before each target run #[cfg(any(windows, unix))] -use core::time::Duration; +use core::{ + fmt::{self, Debug, Formatter}, + time::Duration, +}; use crate::{ executors::{Executor, ExitKind, HasObservers}, @@ -41,8 +44,23 @@ struct Timeval { pub tv_usec: i64, } +#[cfg(unix)] +impl Debug for Timeval { + #[allow(clippy::cast_sign_loss)] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "Timeval {{ tv_sec: {:?}, tv_usec: {:?} (tv: {:?}) }}", + self.tv_sec, + self.tv_usec, + Duration::new(self.tv_sec as _, (self.tv_usec * 1000) as _) + ) + } +} + #[repr(C)] #[cfg(unix)] +#[derive(Debug)] struct Itimerval { pub it_interval: Timeval, pub it_value: Timeval, @@ -74,7 +92,6 @@ pub(crate) unsafe fn windows_delete_timer_queue(tp_timer: *mut TP_TIMER) { } /// The timeout excutor is a wrapper that sets a timeout before each run -#[allow(missing_debug_implementations)] pub struct TimeoutExecutor { executor: E, #[cfg(unix)] @@ -87,6 +104,24 @@ pub struct TimeoutExecutor { critical: RTL_CRITICAL_SECTION, } +impl Debug for TimeoutExecutor { + #[cfg(windows)] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("TimeoutExecutor") + .field("executor", &self.executor) + .field("milli_sec", &self.milli_sec) + .finish_non_exhaustive() + } + + #[cfg(unix)] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("TimeoutExecutor") + .field("executor", &self.executor) + .field("itimerval", &self.itimerval) + .finish() + } +} + #[cfg(windows)] #[allow(non_camel_case_types)] type PTP_TIMER_CALLBACK = unsafe extern "system" fn( diff --git a/libafl/src/executors/with_observers.rs b/libafl/src/executors/with_observers.rs index 7fbe684f5d..426e7f1de0 100644 --- a/libafl/src/executors/with_observers.rs +++ b/libafl/src/executors/with_observers.rs @@ -1,11 +1,17 @@ //! A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`]. -use crate::{inputs::Input, observers::ObserversTuple, Error}; -use super::{Executor, ExitKind, HasObservers}; +use core::fmt::Debug; + +use crate::{ + executors::{Executor, ExitKind, HasObservers}, + inputs::Input, + observers::ObserversTuple, + Error, +}; /// A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`]. -#[allow(missing_debug_implementations)] -pub struct WithObservers { +#[derive(Debug)] +pub struct WithObservers { executor: E, observers: OT, } @@ -14,6 +20,7 @@ impl Executor for WithObservers where I: Input, E: Executor, + OT: Debug, { fn run_target( &mut self, @@ -26,7 +33,7 @@ where } } -impl HasObservers for WithObservers +impl HasObservers for WithObservers where I: Input, OT: ObserversTuple, @@ -40,7 +47,7 @@ where } } -impl WithObservers { +impl WithObservers { /// Wraps the given [`Executor`] with the given [`ObserversTuple`] to implement [`HasObservers`]. /// /// If the executor already implements [`HasObservers`], then the original implementation will be overshadowed by diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index f95a6ded44..2fd6e24c5a 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -39,7 +39,7 @@ pub type MaxMapOneOrFilledFeedback = MapFeedback; /// A `Reducer` function is used to aggregate values for the novelty search -pub trait Reducer: Serialize + serde::de::DeserializeOwned + 'static +pub trait Reducer: Serialize + serde::de::DeserializeOwned + 'static + Debug where T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, { @@ -112,7 +112,7 @@ where } /// A `IsNovel` function is used to discriminate if a reduced value is considered novel. -pub trait IsNovel: Serialize + serde::de::DeserializeOwned + 'static +pub trait IsNovel: Serialize + serde::de::DeserializeOwned + 'static + Debug where T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, { @@ -270,7 +270,7 @@ where impl FeedbackState for MapFeedbackState where - T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, { fn reset(&mut self) -> Result<(), Error> { self.history_map @@ -357,7 +357,7 @@ where O: MapObserver, N: IsNovel, I: Input, - S: HasFeedbackStates + HasClientPerfMonitor, + S: HasFeedbackStates + HasClientPerfMonitor + Debug, FT: FeedbackStatesTuple, { fn is_interesting( diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 4923f687da..1647e5efd5 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -28,12 +28,16 @@ use crate::{ Error, }; -use core::{marker::PhantomData, time::Duration}; +use core::{ + fmt::{self, Debug, Formatter}, + marker::PhantomData, + time::Duration, +}; /// Feedbacks evaluate the observers. /// Basically, they reduce the information provided by an observer to a value, /// indicating the "interestingness" of the last run. -pub trait Feedback: Named +pub trait Feedback: Named + Debug where I: Input, S: HasClientPerfMonitor, @@ -103,7 +107,7 @@ where /// [`FeedbackState`] is the data associated with a [`Feedback`] that must persist as part /// of the fuzzer State -pub trait FeedbackState: Named + Serialize + serde::de::DeserializeOwned { +pub trait FeedbackState: Named + Serialize + serde::de::DeserializeOwned + Debug { /// Reset the internal state fn reset(&mut self) -> Result<(), Error> { Ok(()) @@ -111,7 +115,7 @@ pub trait FeedbackState: Named + Serialize + serde::de::DeserializeOwned { } /// A haskell-style tuple of feedback states -pub trait FeedbackStatesTuple: MatchName + Serialize + serde::de::DeserializeOwned { +pub trait FeedbackStatesTuple: MatchName + Serialize + serde::de::DeserializeOwned + Debug { /// Resets all the feedback states of the tuple fn reset_all(&mut self) -> Result<(), Error>; } @@ -134,7 +138,7 @@ where } /// A cobined feedback consisting of ultiple [`Feedback`]s -#[allow(missing_debug_implementations)] +#[derive(Debug)] pub struct CombinedFeedback where A: Feedback, @@ -190,7 +194,7 @@ where B: Feedback, FL: FeedbackLogic, I: Input, - S: HasClientPerfMonitor, + S: HasClientPerfMonitor + Debug, { fn is_interesting( &mut self, @@ -253,7 +257,7 @@ where } /// Logical combination of two feedbacks -pub trait FeedbackLogic: 'static +pub trait FeedbackLogic: 'static + Debug where A: Feedback, B: Feedback, @@ -545,7 +549,7 @@ pub type EagerOrFeedback = CombinedFeedback = CombinedFeedback; /// Compose feedbacks with an `NOT` operation -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct NotFeedback where A: Feedback, @@ -559,6 +563,20 @@ where phantom: PhantomData<(I, S)>, } +impl Debug for NotFeedback +where + A: Feedback, + I: Input, + S: HasClientPerfMonitor, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("NotFeedback") + .field("name", &self.name) + .field("first", &self.first) + .finish() + } +} + impl Feedback for NotFeedback where A: Feedback, diff --git a/libafl/src/mutators/mopt_mutator.rs b/libafl/src/mutators/mopt_mutator.rs index d2a49b59bc..c7bbc9ea17 100644 --- a/libafl/src/mutators/mopt_mutator.rs +++ b/libafl/src/mutators/mopt_mutator.rs @@ -130,7 +130,7 @@ impl Debug for MOpt { .field("\n\ncore_operator_cycles", &self.core_operator_cycles) .field("\n\ncore_operator_cycles_v2", &self.core_operator_cycles_v2) .field("\n\ncore_operator_cycles_v3", &self.core_operator_cycles_v3) - .finish() + .finish_non_exhaustive() } } diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index 024c9d38d3..5bc5be7a19 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -4,7 +4,7 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; - +use core::fmt::Debug; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ @@ -79,7 +79,7 @@ impl CmpValuesMetadata { } /// A [`CmpMap`] traces comparisons during the current execution -pub trait CmpMap { +pub trait CmpMap: Debug { /// Get the number of cmps fn len(&self) -> usize; diff --git a/libafl/src/observers/concolic/serialization_format.rs b/libafl/src/observers/concolic/serialization_format.rs index 32c69dc21e..82e50d6be2 100644 --- a/libafl/src/observers/concolic/serialization_format.rs +++ b/libafl/src/observers/concolic/serialization_format.rs @@ -43,7 +43,10 @@ #![cfg(feature = "std")] -use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; +use std::{ + fmt::{self, Debug, Formatter}, + io::{self, Cursor, Read, Seek, SeekFrom, Write}, +}; use bincode::{DefaultOptions, Options}; @@ -56,13 +59,18 @@ fn serialization_options() -> DefaultOptions { } /// A `MessageFileReader` reads a stream of [`SymExpr`] and their corresponding [`SymExprRef`]s from any [`Read`]. -#[allow(missing_debug_implementations)] pub struct MessageFileReader { reader: R, deserializer_config: DefaultOptions, current_id: usize, } +impl Debug for MessageFileReader { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "MessageFileReader {{ current_id: {} }}", self.current_id) + } +} + impl MessageFileReader { /// Construct from the given reader. pub fn from_reader(reader: R) -> Self { @@ -204,7 +212,6 @@ impl MessageFileReader { /// A `MessageFileWriter` writes a stream of [`SymExpr`] to any [`Write`]. For each written expression, it returns /// a [`SymExprRef`] which should be used to refer back to it. -#[allow(missing_debug_implementations)] pub struct MessageFileWriter { id_counter: usize, writer: W, @@ -212,6 +219,18 @@ pub struct MessageFileWriter { serialization_options: DefaultOptions, } +impl Debug for MessageFileWriter +where + W: Write, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("MessageFileWriter") + .field("id_counter", &self.id_counter) + .field("writer_start_position", &self.writer_start_position) + .finish_non_exhaustive() + } +} + impl MessageFileWriter { /// Create a `MessageFileWriter` from the given [`Write`]. pub fn from_writer(mut writer: W) -> io::Result { diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index d203d41252..5819abd9b0 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -25,7 +25,7 @@ use crate::{ }; /// A [`MapObserver`] observes the static map, as oftentimes used for afl-like coverage information -pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned +pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned + Debug where T: PrimInt + Default + Copy + Debug, { diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 1703ea42f6..f1f32a8d13 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -9,7 +9,7 @@ pub use cmp::*; pub mod concolic; use alloc::string::{String, ToString}; -use core::time::Duration; +use core::{fmt::Debug, time::Duration}; use serde::{Deserialize, Serialize}; use crate::{ @@ -22,7 +22,7 @@ use crate::{ /// Observers observe different information about the target. /// They can then be used by various sorts of feedback. -pub trait Observer: Named { +pub trait Observer: Named + Debug { /// The testcase finished execution, calculate any changes. /// Reserved for future use. #[inline] @@ -44,7 +44,7 @@ pub trait Observer: Named { } /// A haskell-style tuple of observers -pub trait ObserversTuple: MatchName { +pub trait ObserversTuple: MatchName + Debug { /// This is called right before the next execution. fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error>; diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index fd45c05278..166c8b5b39 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -1,6 +1,6 @@ //! The tracing stage can trace the target and enrich a testcase with metadata, for example for `CmpLog`. -use core::marker::PhantomData; +use core::{fmt::Debug, marker::PhantomData}; use crate::{ corpus::Corpus, @@ -119,7 +119,7 @@ where E: Executor + HasObservers, OT: ObserversTuple, SOT: ObserversTuple, - S: HasClientPerfMonitor + HasExecutions + HasCorpus, + S: HasClientPerfMonitor + HasExecutions + HasCorpus + Debug, { #[inline] fn perform( diff --git a/libafl_cc/build.rs b/libafl_cc/build.rs index 20ff3e79ef..dd52f6452f 100644 --- a/libafl_cc/build.rs +++ b/libafl_cc/build.rs @@ -104,7 +104,9 @@ fn main() { &mut clang_constants_file, "// These constants are autogenerated by build.rs + /// The path to the `clang` executable pub const CLANG_PATH: &str = {:?}; + /// The path to the `clang++` executable pub const CLANGXX_PATH: &str = {:?}; /// The size of the edges map @@ -165,7 +167,9 @@ fn main() { &mut clang_constants_file, "// These constants are autogenerated by build.rs +/// The path to the `clang` executable pub const CLANG_PATH: &str = \"clang\"; +/// The path to the `clang++` executable pub const CLANGXX_PATH: &str = \"clang++\"; " ) diff --git a/libafl_cc/src/clang.rs b/libafl_cc/src/clang.rs index 8cf15f1271..3931bff8dc 100644 --- a/libafl_cc/src/clang.rs +++ b/libafl_cc/src/clang.rs @@ -22,14 +22,19 @@ fn dll_extension<'a>() -> &'a str { include!(concat!(env!("OUT_DIR"), "/clang_constants.rs")); +/// The supported LLVM passes #[allow(clippy::upper_case_acronyms)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LLVMPasses { //CmpLogIns, + /// The CmpLog pass CmpLogRtn, + /// The AFL coverage pass AFLCoverage, } impl LLVMPasses { + /// Gets the path of the LLVM pass #[must_use] pub fn path(&self) -> PathBuf { match self { @@ -43,6 +48,7 @@ impl LLVMPasses { /// Wrap Clang #[allow(clippy::struct_excessive_bools)] +#[derive(Debug)] pub struct ClangWrapper { is_silent: bool, optimize: bool, @@ -269,11 +275,13 @@ impl ClangWrapper { } } + /// Sets the wrapped `cc` compiler pub fn wrapped_cc(&mut self, cc: String) -> &'_ mut Self { self.wrapped_cc = cc; self } + /// Sets the wrapped `cxx` compiler pub fn wrapped_cxx(&mut self, cxx: String) -> &'_ mut Self { self.wrapped_cxx = cxx; self @@ -291,7 +299,7 @@ impl ClangWrapper { self } - // Add LLVM pass + /// Add LLVM pass pub fn add_pass(&mut self, pass: LLVMPasses) -> &'_ mut Self { self.passes.push(pass); self diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index f79d94aa89..c78300248f 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -1,5 +1,48 @@ //! Compiler Wrapper from `LibAFL` +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::pedantic)] +#![allow( + clippy::unreadable_literal, + clippy::type_repetition_in_bounds, + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::used_underscore_binding, + clippy::ptr_as_ptr, + clippy::missing_panics_doc, + clippy::missing_docs_in_private_items, + clippy::module_name_repetitions, + clippy::unreadable_literal +)] +#![deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +)] +#![deny( + bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true +)] + use std::{convert::Into, path::Path, process::Command, string::String, vec::Vec}; pub mod clang; diff --git a/libafl_derive/src/lib.rs b/libafl_derive/src/lib.rs index 066ad9b9e1..638cb0e092 100644 --- a/libafl_derive/src/lib.rs +++ b/libafl_derive/src/lib.rs @@ -1,8 +1,53 @@ -extern crate proc_macro; +//! Derives for `LibAFL` + +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::pedantic)] +#![allow( + clippy::unreadable_literal, + clippy::type_repetition_in_bounds, + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::used_underscore_binding, + clippy::ptr_as_ptr, + clippy::missing_panics_doc, + clippy::missing_docs_in_private_items, + clippy::module_name_repetitions, + clippy::unreadable_literal +)] +#![deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +)] +#![deny( + bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true +)] + use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput}; +/// Derive macro to implement `SerdeAny`, to use a type in a `SerdeAnyMap` #[proc_macro_derive(SerdeAny)] pub fn libafl_serdeany_derive(input: TokenStream) -> TokenStream { let name = parse_macro_input!(input as DeriveInput).ident; diff --git a/libafl_frida/src/alloc.rs b/libafl_frida/src/alloc.rs index 6badfdea44..1499e5852e 100644 --- a/libafl_frida/src/alloc.rs +++ b/libafl_frida/src/alloc.rs @@ -6,17 +6,28 @@ use nix::{ }; use backtrace::Backtrace; -#[cfg(unix)] +#[cfg(any( + target_os = "linux", + all(target_arch = "aarch64", target_os = "android") +))] use libc::{sysconf, _SC_PAGESIZE}; use rangemap::RangeSet; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, ffi::c_void, io}; +#[cfg(any( + target_os = "linux", + all(target_arch = "aarch64", target_os = "android") +))] +use std::io; +use std::{collections::BTreeMap, ffi::c_void}; use crate::{ asan::errors::{AsanError, AsanErrors}, FridaOptions, }; +/// An allocator wrapper with binary-only address sanitization +#[derive(Debug)] +#[allow(missing_docs)] pub struct Allocator { #[allow(dead_code)] options: FridaOptions, @@ -44,7 +55,9 @@ macro_rules! map_to_shadow { }; } +/// Metadata for an allocation #[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[allow(missing_docs)] pub struct AllocationMetadata { pub address: usize, pub size: usize, @@ -56,6 +69,21 @@ pub struct AllocationMetadata { } impl Allocator { + /// Creates a new [`Allocator`] (not supported on this platform!) + #[cfg(not(any( + target_os = "linux", + all(target_arch = "aarch64", target_os = "android") + )))] + #[must_use] + pub fn new(_: FridaOptions) -> Self { + todo!("Shadow region not yet supported for this platform!"); + } + + /// Creates a new [`Allocator`] + #[cfg(any( + target_os = "linux", + all(target_arch = "aarch64", target_os = "android") + ))] #[must_use] pub fn new(options: FridaOptions) -> Self { let ret = unsafe { sysconf(_SC_PAGESIZE) }; @@ -118,11 +146,6 @@ impl Allocator { shadow_bit = try_shadow_bit; } } - #[cfg(not(any( - target_os = "linux", - all(target_arch = "aarch64", target_os = "android") - )))] - todo!("Shadow region not yet supported for this platform!"); assert!(shadow_bit != 0); // attempt to pre-map the entire shadow-memory space @@ -188,6 +211,7 @@ impl Allocator { None } + /// Allocate a new allocation of the given size. #[must_use] #[allow(clippy::missing_safety_doc)] pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void { @@ -272,6 +296,7 @@ impl Allocator { address } + /// Releases the allocation at the given address. #[allow(clippy::missing_safety_doc)] pub unsafe fn release(&mut self, ptr: *mut c_void) { let mut metadata = if let Some(metadata) = self.allocations.get_mut(&(ptr as usize)) { @@ -302,6 +327,7 @@ impl Allocator { Self::poison(shadow_mapping_start, metadata.size); } + /// Finds the metadata for the allocation at the given address. pub fn find_metadata( &mut self, ptr: usize, @@ -328,6 +354,7 @@ impl Allocator { closest } + /// Resets the allocator contents pub fn reset(&mut self) { let mut tmp_allocations = Vec::new(); for (address, mut allocation) in self.allocations.drain() { @@ -358,6 +385,7 @@ impl Allocator { self.total_allocation_size = 0; } + /// Gets the usable size of the allocation, by allocated pointer pub fn get_usable_size(&self, ptr: *mut c_void) -> usize { match self.allocations.get(&(ptr as usize)) { Some(metadata) => metadata.size, @@ -388,6 +416,7 @@ impl Allocator { } } + /// Poisonn an area in memory pub fn poison(start: usize, size: usize) { // println!("poisoning {:x} for {:x}", start, size / 8 + 1); unsafe { @@ -448,17 +477,21 @@ impl Allocator { (shadow_mapping_start, (end - start) / 8) } + /// Maps the address to a shadow address + #[inline] #[must_use] pub fn map_to_shadow(&self, start: usize) -> usize { map_to_shadow!(self, start) } + /// Checks if the currennt address is one of ours #[inline] pub fn is_managed(&self, ptr: *mut c_void) -> bool { //self.allocations.contains_key(&(ptr as usize)) self.base_mapping_addr <= ptr as usize && (ptr as usize) < self.current_mapping_addr } + /// Checks if any of the allocations has not been freed pub fn check_for_leaks(&self) { for metadata in self.allocations.values() { if !metadata.freed { diff --git a/libafl_frida/src/asan/asan_rt.rs b/libafl_frida/src/asan/asan_rt.rs index 59cd2bb97c..1fbc0fc3e2 100644 --- a/libafl_frida/src/asan/asan_rt.rs +++ b/libafl_frida/src/asan/asan_rt.rs @@ -7,6 +7,7 @@ this helps finding mem errors early. */ use backtrace::Backtrace; +use core::fmt::{self, Debug, Formatter}; use frida_gum::{ModuleDetails, NativePointer, RangeDetails}; use hashbrown::HashMap; use nix::sys::mman::{mmap, MapFlags, ProtFlags}; @@ -69,10 +70,12 @@ const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON; #[cfg(not(target_vendor = "apple"))] const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANONYMOUS; -// sixteen general purpose registers are put in this order, rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8-r15, plus instrumented rip, accessed memory addr and true rip +/// The count of registers that need to be saved by the asan runtime +/// sixteen general purpose registers are put in this order, rax, rbx, rcx, rdx, rbp, rsp, rsi, rdi, r8-r15, plus instrumented rip, accessed memory addr and true rip #[cfg(target_arch = "x86_64")] pub const ASAN_SAVE_REGISTER_COUNT: usize = 19; +/// The registers that need to be saved by the asan runtime, as names #[cfg(target_arch = "x86_64")] pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [ "rax", @@ -96,6 +99,7 @@ pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [ "actual rip", ]; +/// The count of registers that need to be saved by the asan runtime #[cfg(target_arch = "aarch64")] pub const ASAN_SAVE_REGISTER_COUNT: usize = 32; @@ -128,6 +132,17 @@ pub struct AsanRuntime { shadow_check_func: Option bool>, } +impl Debug for AsanRuntime { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("AsanRuntime") + .field("stalked_addresses", &self.stalked_addresses) + .field("options", &self.options) + .field("module_map", &"") + .field("suppressed_addresses", &self.suppressed_addresses) + .finish_non_exhaustive() + } +} + impl AsanRuntime { /// Create a new `AsanRuntime` #[must_use] @@ -261,15 +276,18 @@ impl AsanRuntime { self.allocator.reset(); } + /// Gets the allocator #[must_use] pub fn allocator(&self) -> &Allocator { &self.allocator } + /// Gets the allocator, mut pub fn allocator_mut(&mut self) -> &mut Allocator { &mut self.allocator } + /// The function that checks the shadow byte #[must_use] pub fn shadow_check_func(&self) -> &Option bool> { &self.shadow_check_func @@ -332,7 +350,7 @@ impl AsanRuntime { .map_shadow_for_region(tls_start, tls_end, true); println!( "registering thread with stack {:x}:{:x} and tls {:x}:{:x}", - stack_start as usize, stack_end as usize, tls_start as usize, tls_end as usize + stack_start, stack_end, tls_start, tls_end ); } @@ -416,6 +434,7 @@ impl AsanRuntime { (start, end) } + /// Gets the current instruction pointer #[cfg(target_arch = "aarch64")] #[must_use] #[inline] @@ -423,6 +442,7 @@ impl AsanRuntime { Interceptor::current_invocation().cpu_context().pc() as usize } + /// Gets the current instruction pointer #[cfg(target_arch = "x86_64")] #[must_use] #[inline] @@ -447,7 +467,7 @@ impl AsanRuntime { unsafe extern "C" fn []($($param: $param_type),*) -> $return_type { let mut invocation = Interceptor::current_invocation(); let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime); - let real_address = this.real_address_for_stalked(invocation.return_addr() as usize); + let real_address = this.real_address_for_stalked(invocation.return_addr()); if !this.suppressed_addresses.contains(&real_address) && this.module_map.as_ref().unwrap().find(real_address as u64).is_some() { this.[]($($param),*) } else { @@ -2118,6 +2138,7 @@ impl AsanRuntime { Err(()) } + /// Checks if the current instruction is interesting for address sanitization. #[cfg(all(target_arch = "x86_64", unix))] #[inline] #[allow(clippy::unused_self)] @@ -2182,6 +2203,7 @@ impl AsanRuntime { Err(()) } + /// Emits a asan shadow byte check. #[inline] #[allow(clippy::too_many_lines)] #[allow(clippy::too_many_arguments)] diff --git a/libafl_frida/src/asan/errors.rs b/libafl_frida/src/asan/errors.rs index f0e641764c..b44bb2fba3 100644 --- a/libafl_frida/src/asan/errors.rs +++ b/libafl_frida/src/asan/errors.rs @@ -1,3 +1,4 @@ +//! Errors that can be caught by the `libafl_frida` address sanitizer. #[cfg(target_arch = "x86_64")] use crate::asan::asan_rt::ASAN_SAVE_REGISTER_NAMES; use backtrace::Backtrace; @@ -548,7 +549,7 @@ impl AsanErrors { pub static mut ASAN_ERRORS: Option = None; /// An observer for frida address sanitizer `AsanError`s for a frida executor run -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[allow(clippy::unsafe_derive_deserialize)] pub struct AsanErrorsObserver { errors: OwnedPtr>, diff --git a/libafl_frida/src/asan/hook_funcs.rs b/libafl_frida/src/asan/hook_funcs.rs index 9bd2516bc3..80cd03ba63 100644 --- a/libafl_frida/src/asan/hook_funcs.rs +++ b/libafl_frida/src/asan/hook_funcs.rs @@ -1,3 +1,4 @@ +//! The allocator hooks for address sanitizer. use crate::{ alloc::Allocator, asan::{ @@ -1013,6 +1014,7 @@ impl AsanRuntime { unsafe { atoi(s) } } + /// Hooks `atol` #[inline] pub fn hook_atol(&mut self, s: *const c_char) -> i32 { extern "C" { @@ -1031,6 +1033,7 @@ impl AsanRuntime { unsafe { atol(s) } } + /// Hooks `atoll` #[inline] pub fn hook_atoll(&mut self, s: *const c_char) -> i64 { extern "C" { @@ -1049,6 +1052,7 @@ impl AsanRuntime { unsafe { atoll(s) } } + /// Hooks `wcslen` #[inline] pub fn hook_wcslen(&mut self, s: *const wchar_t) -> usize { extern "C" { @@ -1067,6 +1071,7 @@ impl AsanRuntime { size } + /// Hooks `wcscpy` #[inline] pub fn hook_wcscpy(&mut self, dest: *mut wchar_t, src: *const wchar_t) -> *mut wchar_t { extern "C" { @@ -1098,6 +1103,7 @@ impl AsanRuntime { unsafe { wcscpy(dest, src) } } + /// Hooks `wcscmp` #[inline] pub fn hook_wcscmp(&mut self, s1: *const wchar_t, s2: *const wchar_t) -> i32 { extern "C" { diff --git a/libafl_frida/src/asan/mod.rs b/libafl_frida/src/asan/mod.rs index acf7989985..040461d325 100644 --- a/libafl_frida/src/asan/mod.rs +++ b/libafl_frida/src/asan/mod.rs @@ -1,3 +1,5 @@ +//! Address sanitization using [`frida`](https://frida.re/) pub mod asan_rt; pub mod errors; +#[allow(missing_docs)] pub mod hook_funcs; diff --git a/libafl_frida/src/cmplog_rt.rs b/libafl_frida/src/cmplog_rt.rs index 5a37774f85..835dc40e83 100644 --- a/libafl_frida/src/cmplog_rt.rs +++ b/libafl_frida/src/cmplog_rt.rs @@ -1,9 +1,15 @@ +//! Functionality for [`frida`](https://frida.re)-based binary-only `CmpLog`. +//! With it, a fuzzer can collect feedback about each compare that happenned in the target +//! This allows the fuzzer to potentially solve the compares, if a compare value is directly +//! related to the input. +//! Read the [`RedQueen`](https://www.ndss-symposium.org/ndss-paper/redqueen-fuzzing-with-input-to-state-correspondence/) paper for the general concepts. use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi}; +use libafl_targets; use libafl_targets::CMPLOG_MAP_W; use std::ffi::c_void; -extern crate libafl_targets; extern "C" { + /// Tracks cmplog instructions pub fn __libafl_targets_cmplog_instructions(k: u64, shape: u8, arg1: u64, arg2: u64); } @@ -16,8 +22,13 @@ use frida_gum::{ #[cfg(all(feature = "cmplog", target_arch = "aarch64"))] use crate::helper::FridaInstrumentationHelper; +#[cfg(all(feature = "cmplog", target_arch = "aarch64"))] +/// Speciial CmpLog Cases for `aarch64` +#[derive(Debug)] pub enum SpecialCmpLogCase { + /// Test bit and branch if zero Tbz, + /// Test bit and branch if not zero Tbnz, } @@ -27,21 +38,31 @@ use capstone::{ Capstone, Insn, }; +/// The type of an operand loggged during `CmpLog` +#[derive(Debug)] +#[cfg(all(feature = "cmplog", target_arch = "aarch64"))] +pub enum CmplogOperandType { + /// A Register + Regid(capstone::RegId), + /// An immediate value + Imm(u64), + /// A constant immediate value + Cimm(u64), + /// A memory operand + Mem(capstone::RegId, capstone::RegId, i32, u32), +} + +/// `Frida`-based binary-only innstrumentation that logs compares to the fuzzer +/// `LibAFL` can use this knowledge for powerful mutations. +#[derive(Debug)] pub struct CmpLogRuntime { ops_save_register_and_blr_to_populate: Option>, ops_handle_tbz_masking: Option>, ops_handle_tbnz_masking: Option>, } -#[cfg(all(feature = "cmplog", target_arch = "aarch64"))] -pub enum CmplogOperandType { - Regid(capstone::RegId), - Imm(u64), - Cimm(u64), - Mem(capstone::RegId, capstone::RegId, i32, u32), -} - impl CmpLogRuntime { + /// Create a new [`CmpLogRuntime`] #[must_use] pub fn new() -> CmpLogRuntime { Self { @@ -179,6 +200,9 @@ impl CmpLogRuntime { .into_boxed_slice(), ); } + + /// Initialize this `CmpLog` runtime. + /// This will generate the instrumentation blobs for the current arch. pub fn init(&mut self) { self.generate_instrumentation_blobs(); } @@ -204,9 +228,9 @@ impl CmpLogRuntime { self.ops_handle_tbnz_masking.as_ref().unwrap() } + /// Emit the instrumentation code which is responsible for opernads value extraction and cmplog map population #[cfg(all(feature = "cmplog", target_arch = "aarch64"))] #[inline] - /// Emit the instrumentation code which is responsible for opernads value extraction and cmplog map population pub fn emit_comparison_handling( &self, _address: u64, diff --git a/libafl_frida/src/coverage_rt.rs b/libafl_frida/src/coverage_rt.rs index a3634a4a14..7df6790752 100644 --- a/libafl_frida/src/coverage_rt.rs +++ b/libafl_frida/src/coverage_rt.rs @@ -1,3 +1,4 @@ +//! Functionality regarding binary-only coverage collection. use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi}; use std::ffi::c_void; @@ -11,6 +12,8 @@ use frida_gum::{instruction_writer::InstructionWriter, stalker::StalkerOutput}; /// (Default) map size for frida coverage reporting pub const MAP_SIZE: usize = 64 * 1024; +/// Frida binary-only coverage +#[derive(Debug)] pub struct CoverageRuntime { map: [u8; MAP_SIZE], previous_pc: u64, @@ -25,6 +28,7 @@ impl Default for CoverageRuntime { } impl CoverageRuntime { + /// Create a new coverage runtime #[must_use] pub fn new() -> Self { Self { @@ -35,13 +39,17 @@ impl CoverageRuntime { } } + /// Initialize the coverage runtime pub fn init(&mut self) { self.generate_maybe_log_blob(); } + /// Retrieve the coverage map pointer pub fn map_ptr_mut(&mut self) -> *mut u8 { self.map.as_mut_ptr() } + + /// Retrieve the `maybe_log` code blob, that will write coverage into the map #[must_use] pub fn blob_maybe_log(&self) -> &[u8] { self.blob_maybe_log.as_ref().unwrap() @@ -116,6 +124,7 @@ impl CoverageRuntime { self.blob_maybe_log = Some(ops_vec[..ops_vec.len() - 8].to_vec().into_boxed_slice()); } + /// Emits coverage mapping into the current basic block. #[inline] pub fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) { let writer = output.writer(); diff --git a/libafl_frida/src/drcov_rt.rs b/libafl_frida/src/drcov_rt.rs index c30ba95c9b..cbf08d6089 100644 --- a/libafl_frida/src/drcov_rt.rs +++ b/libafl_frida/src/drcov_rt.rs @@ -9,7 +9,7 @@ use rangemap::RangeMap; use std::hash::Hasher; /// Generates `DrCov` traces -#[derive(Clone, Debug)] +#[derive(Debug, Clone)] pub struct DrCovRuntime { /// The basic blocks of this execution pub drcov_basic_blocks: Vec, diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index c71c5d82d2..41e04d63f7 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -1,13 +1,11 @@ use crate::helper::FridaHelper; -use std::{ffi::c_void, marker::PhantomData}; - +use core::fmt::{self, Debug, Formatter}; use frida_gum::{ stalker::{NoneEventSink, Stalker}, - Gum, NativePointer, + Gum, MemoryRange, NativePointer, }; - -use frida_gum::MemoryRange; +use std::{ffi::c_void, marker::PhantomData}; use libafl::{ executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, @@ -22,6 +20,7 @@ use crate::asan::errors::ASAN_ERRORS; #[cfg(windows)] use libafl::executors::inprocess::{HasInProcessHandlers, InProcessHandlers}; +/// The [`FridaInProcessExecutor`] is an [`Executor`] that executes the target in the same process, usinig [`frida`](https://frida.re/) for binary-only instrumentation. pub struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where FH: FridaHelper<'b>, @@ -38,6 +37,22 @@ where _phantom: PhantomData<&'b u8>, } +impl<'a, 'b, 'c, FH, H, I, OT, S> Debug for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> +where + FH: FridaHelper<'b>, + H: FnMut(&I) -> ExitKind, + I: Input + HasTargetBytes, + OT: ObserversTuple, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("FridaInProcessExecutor") + .field("base", &self.base) + .field("helper", &self.helper) + .field("followed", &self.followed) + .finish_non_exhaustive() + } +} + impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> Executor for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> where @@ -107,6 +122,7 @@ where I: Input + HasTargetBytes, OT: ObserversTuple, { + /// Creates a new [`FridaInProcessExecutor`] pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT, S>, helper: &'c mut FH) -> Self { let mut stalker = Stalker::new(gum); // Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that diff --git a/libafl_frida/src/helper.rs b/libafl_frida/src/helper.rs index 680fced438..98d1387022 100644 --- a/libafl_frida/src/helper.rs +++ b/libafl_frida/src/helper.rs @@ -2,53 +2,39 @@ use libafl::inputs::{HasTargetBytes, Input}; use libafl::Error; use libafl_targets::drcov::DrCovBasicBlock; +#[cfg(feature = "cmplog")] +use crate::cmplog_rt::CmpLogRuntime; +#[cfg(windows)] +use crate::FridaOptions; +#[cfg(unix)] +use crate::{asan::asan_rt::AsanRuntime, FridaOptions}; +use crate::{coverage_rt::CoverageRuntime, drcov_rt::DrCovRuntime}; #[cfg(target_arch = "aarch64")] use capstone::{ arch::{self, arm64::Arm64OperandType, ArchOperand::Arm64Operand, BuildsCapstone}, Capstone, Insn, }; - #[cfg(all(target_arch = "x86_64", unix))] use capstone::{ arch::{self, BuildsCapstone}, Capstone, RegId, }; - -#[cfg(target_arch = "aarch64")] -use num_traits::cast::FromPrimitive; - +use core::fmt::{self, Debug, Formatter}; #[cfg(target_arch = "aarch64")] use frida_gum::instruction_writer::Aarch64Register; - #[cfg(target_arch = "x86_64")] use frida_gum::instruction_writer::X86Register; - -use frida_gum::{ - instruction_writer::InstructionWriter, stalker::Transformer, ModuleDetails, ModuleMap, -}; - #[cfg(unix)] use frida_gum::CpuContext; - -use frida_gum::{Gum, Module, PageProtection}; - -use rangemap::RangeMap; - +use frida_gum::{ + instruction_writer::InstructionWriter, stalker::Transformer, Gum, Module, ModuleDetails, + ModuleMap, PageProtection, +}; #[cfg(unix)] use nix::sys::mman::{mmap, MapFlags, ProtFlags}; - -#[cfg(unix)] -use crate::{asan::asan_rt::AsanRuntime, FridaOptions}; - -#[cfg(windows)] -use crate::FridaOptions; - -use crate::drcov_rt::DrCovRuntime; - -use crate::coverage_rt::CoverageRuntime; - -#[cfg(feature = "cmplog")] -use crate::cmplog_rt::CmpLogRuntime; +#[cfg(target_arch = "aarch64")] +use num_traits::cast::FromPrimitive; +use rangemap::RangeMap; #[cfg(any(target_vendor = "apple"))] const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON; @@ -56,7 +42,7 @@ const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON; const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANONYMOUS; /// An helper that feeds `FridaInProcessExecutor` with user-supplied instrumentation -pub trait FridaHelper<'a> { +pub trait FridaHelper<'a>: Debug { /// Access to the stalker `Transformer` fn transformer(&self) -> &Transformer<'a>; @@ -76,8 +62,10 @@ pub trait FridaHelper<'a> { /// pointer to the frida coverage map fn map_ptr_mut(&mut self) -> *mut u8; + /// Returns the mapped ranges of the target fn ranges(&self) -> &RangeMap; + /// Returns the mapped ranges of the target, mutable fn ranges_mut(&mut self) -> &mut RangeMap; } @@ -98,6 +86,23 @@ pub struct FridaInstrumentationHelper<'a> { options: &'a FridaOptions, } +impl Debug for FridaInstrumentationHelper<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut dbg_me = f.debug_struct("FridaInstrumentationHelper"); + dbg_me + .field("coverage_rt", &self.coverage_rt) + .field("capstone", &self.capstone) + .field("asan_runtime", &self.asan_runtime) + .field("drcov_runtime", &self.drcov_runtime) + .field("ranges", &self.ranges) + .field("module_map", &"") + .field("options", &self.options); + #[cfg(feature = "cmplog")] + dbg_me.field("cmplog_runtime", &self.cmplog_runtime); + dbg_me.finish() + } +} + impl<'a> FridaHelper<'a> for FridaInstrumentationHelper<'a> { fn transformer(&self) -> &Transformer<'a> { self.transformer.as_ref().unwrap() @@ -166,7 +171,7 @@ pub fn get_module_size(module_name: &str) -> usize { let mut code_size = 0; let code_size_ref = &mut code_size; Module::enumerate_ranges(module_name, PageProtection::ReadExecute, move |details| { - *code_size_ref = details.memory_range().size() as usize; + *code_size_ref = details.memory_range().size(); true }); @@ -467,8 +472,9 @@ impl<'a> FridaInstrumentationHelper<'a> { Aarch64Register::from_u32(regint as u32).unwrap() } - // frida registers: https://docs.rs/frida-gum/0.4.0/frida_gum/instruction_writer/enum.X86Register.html - // capstone registers: https://docs.rs/capstone-sys/0.14.0/capstone_sys/x86_reg/index.html + /// The writer registers + /// frida registers: + /// capstone registers: #[cfg(all(target_arch = "x86_64", unix))] #[must_use] #[inline] diff --git a/libafl_frida/src/lib.rs b/libafl_frida/src/lib.rs index 58fed4ff98..e8f01c5254 100644 --- a/libafl_frida/src/lib.rs +++ b/libafl_frida/src/lib.rs @@ -3,6 +3,49 @@ The frida executor is a binary-only mode for `LibAFL`. It can report coverage and, on supported architecutres, even reports memory access errors. */ +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::pedantic)] +#![allow( + clippy::unreadable_literal, + clippy::type_repetition_in_bounds, + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::used_underscore_binding, + clippy::ptr_as_ptr, + clippy::missing_panics_doc, + clippy::missing_docs_in_private_items, + clippy::module_name_repetitions, + clippy::unreadable_literal +)] +#![deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +)] +#![deny( + bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true +)] + /// The frida-asan allocator #[cfg(unix)] pub mod alloc; diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index a77e07c52f..8e91798d5c 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -162,6 +162,7 @@ pub fn init_with_asan(args: &mut Vec, env: &mut [(String, String)]) -> E Emulator::new(args, env) } +#[derive(Debug)] // TODO intrumentation filter pub struct QemuAsanHelper { enabled: bool, diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index e3c1f11d72..c238380a3f 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -11,7 +11,7 @@ use crate::{ helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; -#[derive(Default, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuCmpsMapMetadata { pub map: HashMap, pub current_id: u64, @@ -29,6 +29,7 @@ impl QemuCmpsMapMetadata { libafl::impl_serdeany!(QemuCmpsMapMetadata); +#[derive(Debug)] pub struct QemuCmpLogHelper { filter: QemuInstrumentationFilter, } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index c38a59f283..7da9b452f8 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -10,7 +10,7 @@ use crate::{ helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, }; -#[derive(Default, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuEdgesMapMetadata { pub map: HashMap<(u64, u64), u64>, pub current_id: u64, @@ -28,6 +28,7 @@ impl QemuEdgesMapMetadata { libafl::impl_serdeany!(QemuEdgesMapMetadata); +#[derive(Debug)] pub struct QemuEdgeCoverageHelper { filter: QemuInstrumentationFilter, } diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 81ba5e842d..b4485bbc3f 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -293,6 +293,7 @@ impl Drop for GuestMaps { static mut EMULATOR_IS_INITIALIZED: bool = false; +#[derive(Debug)] pub struct Emulator { _private: (), } diff --git a/libafl_qemu/src/executor.rs b/libafl_qemu/src/executor.rs index 1c0abbc95c..9bdd0f89db 100644 --- a/libafl_qemu/src/executor.rs +++ b/libafl_qemu/src/executor.rs @@ -1,4 +1,10 @@ -use core::{ffi::c_void, mem::transmute, ptr}; +//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL` +use core::{ + ffi::c_void, + fmt::{self, Debug, Formatter}, + mem::transmute, + ptr, +}; use libafl::{ corpus::Corpus, @@ -445,6 +451,22 @@ where inner: InProcessExecutor<'a, H, I, OT, S>, } +impl<'a, H, I, OT, QT, S> Debug for QemuExecutor<'a, H, I, OT, QT, S> +where + H: FnMut(&I) -> ExitKind, + I: Input, + OT: ObserversTuple, + QT: QemuHelperTuple, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("QemuExecutor") + .field("helpers", &self.helpers) + .field("emulator", &self.emulator) + .field("inner", &self.inner) + .finish() + } +} + impl<'a, H, I, OT, QT, S> QemuExecutor<'a, H, I, OT, QT, S> where H: FnMut(&I) -> ExitKind, diff --git a/libafl_qemu/src/helper.rs b/libafl_qemu/src/helper.rs index d8faee25ba..d4b383409d 100644 --- a/libafl_qemu/src/helper.rs +++ b/libafl_qemu/src/helper.rs @@ -1,12 +1,13 @@ +use core::{fmt::Debug, ops::Range}; use libafl::{ bolts::tuples::MatchFirstType, executors::ExitKind, inputs::Input, observers::ObserversTuple, }; -use std::ops::Range; use crate::{emu::Emulator, executor::QemuExecutor}; +/// A helper for `libafl_qemu`. // TODO remove 'static when specialization will be stable -pub trait QemuHelper: 'static +pub trait QemuHelper: 'static + Debug where I: Input, { @@ -23,7 +24,7 @@ where fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {} } -pub trait QemuHelperTuple: MatchFirstType +pub trait QemuHelperTuple: MatchFirstType + Debug where I: Input, { @@ -82,6 +83,7 @@ where } } +#[derive(Debug)] pub enum QemuInstrumentationFilter { AllowList(Vec>), DenyList(Vec>), diff --git a/libafl_qemu/src/snapshot.rs b/libafl_qemu/src/snapshot.rs index 2948ae2e66..761a92d1ae 100644 --- a/libafl_qemu/src/snapshot.rs +++ b/libafl_qemu/src/snapshot.rs @@ -10,12 +10,14 @@ use crate::{ pub const SNAPSHOT_PAGE_SIZE: usize = 4096; +#[derive(Debug)] pub struct SnapshotPageInfo { pub addr: u64, pub dirty: bool, pub data: [u8; SNAPSHOT_PAGE_SIZE], } +#[derive(Debug)] // TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html pub struct QemuSnapshotHelper { pub access_cache: [u64; 4], diff --git a/libafl_sugar/src/forkserver.rs b/libafl_sugar/src/forkserver.rs index 3abbf1dc8e..fa220f3475 100644 --- a/libafl_sugar/src/forkserver.rs +++ b/libafl_sugar/src/forkserver.rs @@ -1,6 +1,7 @@ -use typed_builder::TypedBuilder; - +//! An `afl`-style forkserver fuzzer. +//! Use this if your target has complex state that needs to be reset. use std::{fs, net::SocketAddr, path::PathBuf, time::Duration}; +use typed_builder::TypedBuilder; use libafl::{ bolts::{ @@ -32,9 +33,11 @@ use libafl::{ use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; +/// The default coverage map size to use for forkserver targets pub const DEFAULT_MAP_SIZE: usize = 65536; -#[derive(TypedBuilder)] +/// Creates a Forkserver-based fuzzer. +#[derive(Debug, TypedBuilder)] pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> { /// Laucher configuration (default is random) #[builder(default = None, setter(strip_option))] @@ -74,6 +77,7 @@ pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> { #[allow(clippy::similar_names)] impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { + /// Runs the fuzzer. #[allow(clippy::too_many_lines, clippy::similar_names)] pub fn run(&mut self) { let conf = match self.configuration.as_ref() { @@ -250,6 +254,7 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { } } +/// The python bindings for this sugar #[cfg(feature = "python")] pub mod pybind { use crate::forkserver; @@ -257,6 +262,7 @@ pub mod pybind { use pyo3::prelude::*; use std::path::PathBuf; + /// Python bindings for the `LibAFL` forkserver sugar #[pyclass(unsendable)] struct ForkserverBytesCoverageSugar { input_dirs: Vec, @@ -267,6 +273,7 @@ pub mod pybind { #[pymethods] impl ForkserverBytesCoverageSugar { + /// Create a new [`ForkserverBytesCoverageSugar`] #[new] fn new( input_dirs: Vec, @@ -282,6 +289,7 @@ pub mod pybind { } } + /// Run the fuzzer #[allow(clippy::needless_pass_by_value)] pub fn run(&self, program: String, arguments: Vec) { forkserver::ForkserverBytesCoverageSugar::<{ forkserver::DEFAULT_MAP_SIZE }>::builder() @@ -296,6 +304,7 @@ pub mod pybind { } } + /// Register the module pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/libafl_sugar/src/inmemory.rs b/libafl_sugar/src/inmemory.rs index 041129524e..fd0f6f140d 100644 --- a/libafl_sugar/src/inmemory.rs +++ b/libafl_sugar/src/inmemory.rs @@ -1,6 +1,9 @@ -use typed_builder::TypedBuilder; +//! In-Memory fuzzing made easy. +//! Use this sugar for scaling `libfuzzer`-style fuzzers. +use core::fmt::{self, Debug, Formatter}; use std::{fs, net::SocketAddr, path::PathBuf, time::Duration}; +use typed_builder::TypedBuilder; use libafl::{ bolts::{ @@ -35,6 +38,8 @@ use libafl_targets::{CmpLogObserver, CMPLOG_MAP, EDGES_MAP, MAX_EDGES_NUM}; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; +/// In-Memory fuzzing made easy. +/// Use this sugar for scaling `libfuzzer`-style fuzzers. #[derive(TypedBuilder)] pub struct InMemoryBytesCoverageSugar<'a, H> where @@ -56,6 +61,7 @@ where /// Flag if use CmpLog #[builder(default = false)] use_cmplog: bool, + /// The port used for communication between this fuzzer node and other fuzzer nodes #[builder(default = 1337_u16)] broker_port: u16, /// The list of cores to run on @@ -69,11 +75,39 @@ where harness: Option, } +impl Debug for InMemoryBytesCoverageSugar<'_, H> +where + H: FnMut(&[u8]), +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("InMemoryBytesCoverageSugar") + .field("configuration", &self.configuration) + .field("timeout", &self.timeout) + .field("input_dirs", &self.input_dirs) + .field("output_dir", &self.output_dir) + .field("tokens_file", &self.tokens_file) + .field("use_cmplog", &self.use_cmplog) + .field("broker_port", &self.broker_port) + .field("cores", &self.cores) + .field("remote_broker_addr", &self.remote_broker_addr) + .field( + "harness", + if self.harness.is_some() { + &"" + } else { + &"None" + }, + ) + .finish() + } +} + #[allow(clippy::similar_names)] impl<'a, H> InMemoryBytesCoverageSugar<'a, H> where H: FnMut(&[u8]), { + /// Run the fuzzer #[allow(clippy::too_many_lines, clippy::similar_names)] pub fn run(&mut self) { let conf = match self.configuration.as_ref() { @@ -270,6 +304,7 @@ where } } +/// Python bindings for this sugar #[cfg(feature = "python")] pub mod pybind { use crate::inmemory; @@ -278,6 +313,8 @@ pub mod pybind { use pyo3::types::PyBytes; use std::path::PathBuf; + /// In-Memory fuzzing made easy. + /// Use this sugar for scaling `libfuzzer`-style fuzzers. #[pyclass(unsendable)] struct InMemoryBytesCoverageSugar { input_dirs: Vec, @@ -288,6 +325,7 @@ pub mod pybind { #[pymethods] impl InMemoryBytesCoverageSugar { + /// Create a new [`InMemoryBytesCoverageSugar`] #[new] fn new( input_dirs: Vec, @@ -303,6 +341,7 @@ pub mod pybind { } } + /// Run the fuzzer #[allow(clippy::needless_pass_by_value)] pub fn run(&self, harness: PyObject) { inmemory::InMemoryBytesCoverageSugar::builder() @@ -323,6 +362,7 @@ pub mod pybind { } } + /// Register the module pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/libafl_sugar/src/lib.rs b/libafl_sugar/src/lib.rs index 6e16c960b5..1af670774f 100644 --- a/libafl_sugar/src/lib.rs +++ b/libafl_sugar/src/lib.rs @@ -1,5 +1,48 @@ //! Sugar API to simplify the life of the naive user of `LibAFL` +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::pedantic)] +#![allow( + clippy::unreadable_literal, + clippy::type_repetition_in_bounds, + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::used_underscore_binding, + clippy::ptr_as_ptr, + clippy::missing_panics_doc, + clippy::missing_docs_in_private_items, + clippy::module_name_repetitions, + clippy::unreadable_literal +)] +#![deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +)] +#![deny( + bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true +)] + pub mod inmemory; pub use inmemory::InMemoryBytesCoverageSugar; @@ -13,12 +56,16 @@ pub mod forkserver; #[cfg(target_family = "unix")] pub use forkserver::ForkserverBytesCoverageSugar; +/// Default timeout for a run pub const DEFAULT_TIMEOUT_SECS: u64 = 1200; +/// Default cache size for the corpus in memory. +/// Anything else will be on disk. pub const CORPUS_CACHE_SIZE: usize = 4096; #[cfg(feature = "python")] use pyo3::prelude::*; +/// The sugar python module #[cfg(feature = "python")] #[pymodule] #[pyo3(name = "libafl_sugar")] diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index ca1eea390b..ed1ff9fe42 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -1,6 +1,8 @@ -use typed_builder::TypedBuilder; - +//! In-memory fuzzer with `QEMU`-based binary-only instrumentation +//! +use core::fmt::{self, Debug, Formatter}; use std::{fs, net::SocketAddr, path::PathBuf, time::Duration}; +use typed_builder::TypedBuilder; use libafl::{ bolts::{ @@ -36,6 +38,8 @@ use libafl_targets::CmpLogObserver; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; +/// Sugar to create a `libfuzzer`-style fuzzer that uses +/// `QEMU`-based binary-only instrumentation #[derive(TypedBuilder)] pub struct QemuBytesCoverageSugar<'a, H> where @@ -57,6 +61,8 @@ where /// Flag if use CmpLog #[builder(default = false)] use_cmplog: bool, + /// The port the fuzzing nodes communicate over + /// This will spawn a server on this port, and connect to other brokers using this port. #[builder(default = 1337_u16)] broker_port: u16, /// The list of cores to run on @@ -70,10 +76,38 @@ where harness: Option, } +impl<'a, H> Debug for QemuBytesCoverageSugar<'a, H> +where + H: FnMut(&[u8]), +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("QemuBytesCoverageSugar") + .field("configuration", &self.configuration) + .field("timeout", &self.timeout) + .field("input_dirs", &self.input_dirs) + .field("output_dir", &self.output_dir) + .field("tokens_file", &self.tokens_file) + .field("use_cmplog", &self.use_cmplog) + .field("broker_port", &self.broker_port) + .field("cores", &self.cores) + .field("remote_broker_addr", &self.remote_broker_addr) + .field( + "harness", + if self.harness.is_some() { + &"" + } else { + &"None" + }, + ) + .finish() + } +} + impl<'a, H> QemuBytesCoverageSugar<'a, H> where H: FnMut(&[u8]), { + /// Run the fuzzer #[allow(clippy::too_many_lines, clippy::similar_names)] pub fn run(&mut self, emulator: &Emulator) { let conf = match self.configuration.as_ref() { @@ -330,6 +364,7 @@ where } } +/// python bindings for this sugar #[cfg(feature = "python")] pub mod pybind { use crate::qemu; @@ -349,6 +384,7 @@ pub mod pybind { #[pymethods] impl QemuBytesCoverageSugar { + /// Create a new [`QemuBytesCoverageSugar`] #[new] fn new( input_dirs: Vec, @@ -364,6 +400,7 @@ pub mod pybind { } } + /// Run the fuzzer #[allow(clippy::needless_pass_by_value)] pub fn run(&self, emulator: &Emulator, harness: PyObject) { qemu::QemuBytesCoverageSugar::builder() @@ -384,6 +421,7 @@ pub mod pybind { } } + /// Register this class pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; Ok(()) diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs index 65d4d4ec40..8108af6b35 100644 --- a/libafl_targets/src/cmplog.rs +++ b/libafl_targets/src/cmplog.rs @@ -1,5 +1,8 @@ //! `CmpLog` logs and reports back values touched during fuzzing. //! The values will then be used in subsequent mutations. +//! + +use core::fmt::{self, Debug, Formatter}; use libafl::{ bolts::{ownedref::OwnedRefMut, tuples::Named}, @@ -16,6 +19,7 @@ pub const CMPLOG_MAP_SIZE: usize = CMPLOG_MAP_W * CMPLOG_MAP_H; /// The size of a logged routine argument in bytes pub const CMPLOG_RTN_LEN: usize = 32; +/// The hight of a cmplog routine map pub const CMPLOG_MAP_RTN_H: usize = (CMPLOG_MAP_H * core::mem::size_of::()) / core::mem::size_of::(); @@ -26,6 +30,7 @@ pub const CMPLOG_KIND_RTN: u8 = 1; // void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2) extern "C" { + /// Logs an instruction for feedback during fuzzing pub fn __libafl_targets_cmplog_instructions(k: usize, shape: u8, arg1: u64, arg2: u64); } @@ -48,6 +53,7 @@ pub struct CmpLogInstruction(u64, u64); #[derive(Default, Debug, Clone, Copy)] pub struct CmpLogRoutine([u8; CMPLOG_RTN_LEN], [u8; CMPLOG_RTN_LEN]); +/// Union of cmplog operands and routines #[repr(C)] #[derive(Clone, Copy)] pub union CmpLogVals { @@ -55,9 +61,15 @@ pub union CmpLogVals { routines: [[CmpLogRoutine; CMPLOG_MAP_RTN_H]; CMPLOG_MAP_W], } +impl Debug for CmpLogVals { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CmpLogVals").finish_non_exhaustive() + } +} + /// A struct containing the `CmpLog` metadata for a `LibAFL` run. #[repr(C)] -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct CmpLogMap { headers: [CmpLogHeader; CMPLOG_MAP_W], vals: CmpLogVals, @@ -109,8 +121,8 @@ impl CmpMap for CmpLogMap { self.vals.operands[idx][execution].1 as u32, )), 8 => CmpValues::U64(( - self.vals.operands[idx][execution].0 as u64, - self.vals.operands[idx][execution].1 as u64, + self.vals.operands[idx][execution].0, + self.vals.operands[idx][execution].1, )), other => panic!("Invalid CmpLog shape {}", other), } @@ -155,6 +167,7 @@ pub static mut libafl_cmplog_enabled: u8 = 0; pub use libafl_cmplog_enabled as CMPLOG_ENABLED; /// A [`CmpObserver`] observer for `CmpLog` +#[derive(Debug)] pub struct CmpLogObserver<'a> { map: OwnedRefMut<'a, CmpLogMap>, size: Option>, diff --git a/libafl_targets/src/coverage.rs b/libafl_targets/src/coverage.rs index bfffdc9519..49a8215fb5 100644 --- a/libafl_targets/src/coverage.rs +++ b/libafl_targets/src/coverage.rs @@ -12,22 +12,26 @@ pub use __afl_area_ptr_local as EDGES_MAP; pub static mut MAX_EDGES_NUM: usize = 0; extern "C" { + /// The area pointer points to the edges map. pub static mut __afl_area_ptr: *mut u8; } pub use __afl_area_ptr as EDGES_MAP_PTR; +/// The size of the map for edges. #[no_mangle] pub static mut __afl_map_size: usize = EDGES_MAP_SIZE; pub use __afl_map_size as EDGES_MAP_PTR_SIZE; +/// Gets the edges map from the `EDGES_MAP_PTR` raw pointer. #[must_use] pub fn edges_map_from_ptr<'a>() -> &'a mut [u8] { unsafe { - assert!(!EDGES_MAP_PTR.is_null()); + debug_assert!(!EDGES_MAP_PTR.is_null()); from_raw_parts_mut(EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE) } } +/// Gets the current maximum number of edges tracked. #[must_use] pub fn edges_max_num() -> usize { unsafe { diff --git a/libafl_targets/src/drcov.rs b/libafl_targets/src/drcov.rs index 9176a1bc2b..ae480c6877 100644 --- a/libafl_targets/src/drcov.rs +++ b/libafl_targets/src/drcov.rs @@ -13,7 +13,9 @@ use std::{ /// A basic block struct #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DrCovBasicBlock { + /// Start of this basic block pub start: usize, + /// End of this basic block pub end: usize, } @@ -26,6 +28,7 @@ struct DrCovBasicBlockEntry { } /// A writer for `DrCov` files +#[derive(Debug)] pub struct DrCovWriter<'a> { module_mapping: &'a RangeMap, } diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 1376ac9c8a..387aad7324 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,5 +1,50 @@ //! `libafl_targets` contains runtime code, injected in the target itself during compilation. +//! +//! +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(clippy::pedantic)] +#![allow( + clippy::unreadable_literal, + clippy::type_repetition_in_bounds, + clippy::missing_errors_doc, + clippy::cast_possible_truncation, + clippy::used_underscore_binding, + clippy::ptr_as_ptr, + clippy::missing_panics_doc, + clippy::missing_docs_in_private_items, + clippy::module_name_repetitions, + clippy::unreadable_literal +)] +#![deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +)] +#![deny( + bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true +)] +#[allow(unused_imports)] #[macro_use] extern crate alloc; diff --git a/libafl_targets/src/sancov_8bit.rs b/libafl_targets/src/sancov_8bit.rs index 6a7fb63ecb..1bf9714780 100644 --- a/libafl_targets/src/sancov_8bit.rs +++ b/libafl_targets/src/sancov_8bit.rs @@ -2,6 +2,8 @@ use alloc::vec::Vec; use core::slice::from_raw_parts_mut; +/// A [`Vec`] of `8-bit-counters` maps for multiple modules. +/// They are initialized by calling [`__sanitizer_cov_8bit_counters_init`]( pub static mut COUNTERS_MAPS: Vec<&'static mut [u8]> = Vec::new(); /// Initialize the sancov `8-bit-counters` - usually called by `llvm`. diff --git a/libafl_targets/src/sancov_cmp.rs b/libafl_targets/src/sancov_cmp.rs index 9f78b643e2..fbb60a983c 100644 --- a/libafl_targets/src/sancov_cmp.rs +++ b/libafl_targets/src/sancov_cmp.rs @@ -1,15 +1,25 @@ +//! Sanitizer Coverage comparison functions extern "C" { + /// Trace an 8 bit `cmp` pub fn __sanitizer_cov_trace_cmp1(v0: u8, v1: u8); + /// Trace a 16 bit `cmp` pub fn __sanitizer_cov_trace_cmp2(v0: u16, v1: u16); + /// Trace a 32 bit `cmp` pub fn __sanitizer_cov_trace_cmp4(v0: u32, v1: u32); + /// Trace a 64 bit `cmp` pub fn __sanitizer_cov_trace_cmp8(v0: u64, v1: u64); + /// Trace an 8 bit constant `cmp` pub fn __sanitizer_cov_trace_const_cmp1(v0: u8, v1: u8); + /// Trace a 16 bit constant `cmp` pub fn __sanitizer_cov_trace_const_cmp2(v0: u16, v1: u16); + /// Trace a 32 bit constant `cmp` pub fn __sanitizer_cov_trace_const_cmp4(v0: u32, v1: u32); + /// Trace a 64 bit constant `cmp` pub fn __sanitizer_cov_trace_const_cmp8(v0: u64, v1: u64); + /// Trace a switch statement pub fn __sanitizer_cov_trace_switch(val: u64, cases: *const u64); } diff --git a/libafl_targets/src/sancov_pcguard.rs b/libafl_targets/src/sancov_pcguard.rs index 05daa71c75..11236ae5e8 100644 --- a/libafl_targets/src/sancov_pcguard.rs +++ b/libafl_targets/src/sancov_pcguard.rs @@ -39,7 +39,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { } #[cfg(feature = "sancov_pcguard_hitcounts")] { - let val = (*EDGES_MAP.get_unchecked(pos) as u8).wrapping_add(1); + let val = (*EDGES_MAP.get_unchecked(pos)).wrapping_add(1); *EDGES_MAP.get_unchecked_mut(pos) = val; } }