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
This commit is contained in:
Dominik Maier 2022-01-03 00:47:17 +01:00 committed by GitHub
parent efc804fe7d
commit af3d321213
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 848 additions and 181 deletions

View File

@ -11,7 +11,7 @@ extern crate serde;
use libafl::SerdeAny; use libafl::SerdeAny;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, SerdeAny)] #[derive(Debug, Serialize, Deserialize, SerdeAny)]
pub struct MyMetadata { pub struct MyMetadata {
//... //...
} }

View File

@ -65,12 +65,12 @@ pub fn main() {
let mut shmem = StdShMemProvider::new().unwrap().new_map(MAP_SIZE).unwrap(); let mut shmem = StdShMemProvider::new().unwrap().new_map(MAP_SIZE).unwrap();
//let the forkserver know the shmid //let the forkserver know the shmid
shmem.write_to_env("__AFL_SHM_ID").unwrap(); 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 // Create an observation channel using the signals map
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new( let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
"shared_mem", "shared_mem",
&mut shmem_map, shmem_map,
)); ));
// Create an observation channel to keep track of the execution time // Create an observation channel to keep track of the execution time

View File

@ -54,9 +54,6 @@ use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP};
#[cfg(unix)] #[cfg(unix)]
use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS}; use libafl_frida::asan::errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS};
fn timeout_from_millis_str(time: &str) -> Result<Duration, Error> {
Ok(Duration::from_millis(time.parse()?))
}
#[derive(Debug, StructOpt)] #[derive(Debug, StructOpt)]
#[structopt( #[structopt(
@ -113,27 +110,6 @@ struct Opt {
)] )]
output: PathBuf, 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<PathBuf>,
*/
#[structopt( #[structopt(
long, long,
help = "The configuration this fuzzer runs with, for multiprocessing", help = "The configuration this fuzzer runs with, for multiprocessing",

View File

@ -20,7 +20,7 @@ which = { version = "4.0.2" }
num_cpus = "1.0" num_cpus = "1.0"
[dependencies] [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"] } libafl_targets = { path = "../../libafl_targets/", features = ["libfuzzer"] }
# TODO Include it only when building cc # TODO Include it only when building cc
libafl_cc = { path = "../../libafl_cc/" } libafl_cc = { path = "../../libafl_cc/" }

View File

@ -20,7 +20,7 @@ which = { version = "4.0.2" }
num_cpus = "1.0" num_cpus = "1.0"
[dependencies] [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"] } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] }
# TODO Include it only when building cc # TODO Include it only when building cc
libafl_cc = { path = "../../libafl_cc/" } libafl_cc = { path = "../../libafl_cc/" }

View File

@ -13,7 +13,7 @@ use crate::input::PacketData;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(SerdeAny, Serialize, Deserialize)] #[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct PacketLenMetadata { pub struct PacketLenMetadata {
pub length: u64, pub length: u64,
} }

View File

@ -12,9 +12,8 @@ edition = "2021"
build = "build.rs" build = "build.rs"
[features] [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 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. 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). 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` 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"] } intervaltree = { version = "0.2.7", default-features = false, features = ["serde"] }
libafl_derive = { version = "0.7.0", optional = true, path = "../libafl_derive" } 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} miniz_oxide = { version = "0.5", optional = true}
core_affinity = { version = "0.5", git = "https://github.com/s1341/core_affinity_rs", rev = "6648a7a", 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? hostname = { version = "^0.3", optional = true } # Is there really no gethostname in the stdlib?

View File

@ -24,6 +24,7 @@ use crate::{
Error, Error,
}; };
use core::fmt::{self, Debug, Formatter};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use core::marker::PhantomData; use core::marker::PhantomData;
#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))] #[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)>, 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<S>, LlmpRestartingEventManager<I, OT, S, SP>, usize) -> Result<(), Error>,
I: Input,
OT: ObserversTuple<I, S> + 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")] #[cfg(feature = "std")]
impl<'a, CF, I, MT, OT, S, SP> Launcher<'a, CF, I, MT, OT, S, SP> impl<'a, CF, I, MT, OT, S, SP> Launcher<'a, CF, I, MT, OT, S, SP>
where where

View File

@ -3,7 +3,10 @@
use serde::{de::DeserializeSeed, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de::DeserializeSeed, Deserialize, Deserializer, Serialize, Serializer};
use alloc::boxed::Box; use alloc::boxed::Box;
use core::any::{Any, TypeId}; use core::{
any::{Any, TypeId},
fmt::Debug,
};
// yolo // yolo
@ -30,7 +33,7 @@ pub fn unpack_type_id(id: TypeId) -> u64 {
} }
/// A (de)serializable Any trait /// 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 /// returns this as Any trait
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
/// returns this as mutable Any trait /// returns this as mutable Any trait
@ -40,11 +43,11 @@ pub trait SerdeAny: Any + erased_serde::Serialize {
} }
/// Wrap a type for serialization /// Wrap a type for serialization
#[allow(missing_debug_implementations)] #[derive(Debug)]
pub struct Wrap<'a, T: ?Sized>(pub &'a T); pub struct Wrap<'a, T: ?Sized + Debug>(pub &'a T);
impl<'a, T> Serialize for Wrap<'a, T> impl<'a, T> Serialize for Wrap<'a, T>
where where
T: ?Sized + erased_serde::Serialize + 'a, T: ?Sized + erased_serde::Serialize + 'a + Debug,
{ {
/// Serialize the type /// Serialize the type
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@ -191,9 +194,9 @@ macro_rules! create_serde_registry_for_trait {
} }
} }
#[derive(Serialize, Deserialize)]
/// A (de)serializable anymap containing (de)serializable trait objects registered /// A (de)serializable anymap containing (de)serializable trait objects registered
/// in the registry /// in the registry
#[derive(Debug, Serialize, Deserialize)]
pub struct SerdeAnyMap { pub struct SerdeAnyMap {
map: HashMap<u64, Box<dyn $trait_name>>, map: HashMap<u64, Box<dyn $trait_name>>,
} }
@ -207,6 +210,7 @@ macro_rules! create_serde_registry_for_trait {
} }
} }
/*
#[cfg(feature = "anymap_debug")] #[cfg(feature = "anymap_debug")]
impl fmt::Debug for SerdeAnyMap { impl fmt::Debug for SerdeAnyMap {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 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 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SerdeAnymap with {} elements", self.len()) write!(f, "SerdeAnymap with {} elements", self.len())
} }
} }*/
#[allow(unused_qualifications)] #[allow(unused_qualifications)]
impl SerdeAnyMap { impl SerdeAnyMap {
@ -318,8 +322,8 @@ macro_rules! create_serde_registry_for_trait {
} }
/// A serializable [`HashMap`] wrapper for [`SerdeAny`] types, addressable by name. /// A serializable [`HashMap`] wrapper for [`SerdeAny`] types, addressable by name.
#[allow(unused_qualifications, missing_debug_implementations)] #[allow(unused_qualifications)]
#[derive(Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct NamedSerdeAnyMap { pub struct NamedSerdeAnyMap {
map: HashMap<u64, HashMap<u64, Box<dyn $trait_name>>>, map: HashMap<u64, HashMap<u64, Box<dyn $trait_name>>>,
} }

View File

@ -6,15 +6,16 @@ use crate::{
observers::ObserversTuple, observers::ObserversTuple,
Error, Error,
}; };
use core::fmt::Debug;
/// A [`CombinedExecutor`] wraps a primary executor, forwarding its methods, and a secondary one /// A [`CombinedExecutor`] wraps a primary executor, forwarding its methods, and a secondary one
#[allow(missing_debug_implementations)] #[derive(Debug)]
pub struct CombinedExecutor<A, B> { pub struct CombinedExecutor<A: Debug, B: Debug> {
primary: A, primary: A,
secondary: B, secondary: B,
} }
impl<A, B> CombinedExecutor<A, B> { impl<A: Debug, B: Debug> CombinedExecutor<A, B> {
/// Create a new `CombinedExecutor`, wrapping the given `executor`s. /// Create a new `CombinedExecutor`, wrapping the given `executor`s.
pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self pub fn new<EM, I, S, Z>(primary: A, secondary: B) -> Self
where where
@ -56,6 +57,7 @@ where
impl<A, B, I, OT, S> HasObservers<I, OT, S> for CombinedExecutor<A, B> impl<A, B, I, OT, S> HasObservers<I, OT, S> for CombinedExecutor<A, B>
where where
A: HasObservers<I, OT, S>, A: HasObservers<I, OT, S>,
B: Debug,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
{ {
#[inline] #[inline]

View File

@ -1,5 +1,8 @@
//! The command executor executes a sub program for each run //! 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")] #[cfg(feature = "std")]
use std::process::Child; 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. /// 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. /// 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<EM, I, OT: Debug, S, T: Debug, Z> {
pub struct CommandExecutor<EM, I, S, Z, T, OT> {
inner: T, inner: T,
/// [`crate::observers::Observer`]s for this executor /// [`crate::observers::Observer`]s for this executor
observers: OT, observers: OT,
phantom: PhantomData<(EM, I, S, Z)>, phantom: PhantomData<(EM, I, S, Z)>,
} }
impl<EM, I, S, Z, T, OT> CommandExecutor<EM, I, S, Z, T, OT> { impl<EM, I, OT: Debug, S, T: Debug, Z> Debug for CommandExecutor<EM, I, OT, S, T, Z> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("CommandExecutor")
.field("inner", &self.inner)
.field("observers", &self.observers)
.finish()
}
}
impl<EM, I, OT: Debug, S, T: Debug, Z> CommandExecutor<EM, I, OT, S, T, Z> {
/// Accesses the inner value /// Accesses the inner value
pub fn inner(&mut self) -> &mut T { pub fn inner(&mut self) -> &mut T {
&mut self.inner &mut self.inner
@ -32,7 +43,7 @@ impl<EM, I, S, Z, T, OT> CommandExecutor<EM, I, S, Z, T, OT> {
// this only works on unix because of the reliance on checking the process signal for detecting OOM // this only works on unix because of the reliance on checking the process signal for detecting OOM
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
impl<EM, I, S, Z, T, OT> Executor<EM, I, S, Z> for CommandExecutor<EM, I, S, Z, T, OT> impl<EM, I, OT: Debug, S, T: Debug, Z> Executor<EM, I, S, Z> for CommandExecutor<EM, I, OT, S, T, Z>
where where
I: Input, I: Input,
T: CommandConfigurator<EM, I, S, Z>, T: CommandConfigurator<EM, I, S, Z>,
@ -72,7 +83,8 @@ where
} }
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
impl<EM, I, S, Z, T, OT> HasObservers<I, OT, S> for CommandExecutor<EM, I, S, Z, T, OT> impl<EM, I, OT: Debug, S, T: Debug, Z> HasObservers<I, OT, S>
for CommandExecutor<EM, I, OT, S, T, Z>
where where
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
@ -94,6 +106,7 @@ where
/// ``` /// ```
/// use std::{io::Write, process::{Stdio, Command, Child}}; /// use std::{io::Write, process::{Stdio, Command, Child}};
/// use libafl::{Error, inputs::{Input, HasTargetBytes}, executors::{Executor, command::CommandConfigurator}}; /// use libafl::{Error, inputs::{Input, HasTargetBytes}, executors::{Executor, command::CommandConfigurator}};
/// #[derive(Debug)]
/// struct MyExecutor; /// struct MyExecutor;
/// ///
/// impl<EM, I: Input + HasTargetBytes, S, Z> CommandConfigurator<EM, I, S, Z> for MyExecutor { /// impl<EM, I: Input + HasTargetBytes, S, Z> CommandConfigurator<EM, I, S, Z> for MyExecutor {
@ -122,7 +135,7 @@ where
/// } /// }
/// ``` /// ```
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
pub trait CommandConfigurator<EM, I: Input, S, Z>: Sized { pub trait CommandConfigurator<EM, I: Input, S, Z>: Sized + Debug {
/// Spawns a new process with the given configuration. /// Spawns a new process with the given configuration.
fn spawn_child( fn spawn_child(
&mut self, &mut self,
@ -133,7 +146,7 @@ pub trait CommandConfigurator<EM, I: Input, S, Z>: Sized {
) -> Result<Child, Error>; ) -> Result<Child, Error>;
/// Create an `Executor` from this `CommandConfigurator`. /// Create an `Executor` from this `CommandConfigurator`.
fn into_executor<OT>(self, observers: OT) -> CommandExecutor<EM, I, S, Z, Self, OT> fn into_executor<OT: Debug>(self, observers: OT) -> CommandExecutor<EM, I, OT, S, Self, Z>
where where
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
{ {

View File

@ -1,6 +1,10 @@
//! Expose an `Executor` based on a `Forkserver` in order to execute AFL/AFL++ binaries //! 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::{ use std::{
fs::{File, OpenOptions}, fs::{File, OpenOptions},
io::{self, prelude::*, ErrorKind, SeekFrom}, io::{self, prelude::*, ErrorKind, SeekFrom},
@ -150,8 +154,9 @@ impl ConfigTarget for Command {
} }
} }
/// The [`OutFile`] to write to /// The [`OutFile`] to write input to.
#[allow(missing_debug_implementations)] /// The target/forkserver will read from this file.
#[derive(Debug)]
pub struct OutFile { pub struct OutFile {
/// The file /// The file
file: 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. /// The timeout forkserver executor that wraps around the standard forkserver executor and sets a timeout before each run.
#[allow(missing_debug_implementations)] #[derive(Debug)]
pub struct TimeoutForkserverExecutor<E> { pub struct TimeoutForkserverExecutor<E: Debug> {
executor: E, executor: E,
timeout: TimeSpec, timeout: TimeSpec,
} }
impl<E> TimeoutForkserverExecutor<E> { impl<E: Debug> TimeoutForkserverExecutor<E> {
/// Create a new [`TimeoutForkserverExecutor`] /// Create a new [`TimeoutForkserverExecutor`]
pub fn new(executor: E, exec_tmout: Duration) -> Result<Self, Error> { pub fn new(executor: E, exec_tmout: Duration) -> Result<Self, Error> {
let milli_sec = exec_tmout.as_millis() as i64; let milli_sec = exec_tmout.as_millis() as i64;
@ -384,7 +389,7 @@ impl<E> TimeoutForkserverExecutor<E> {
} }
} }
impl<E, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutForkserverExecutor<E> impl<E: Debug, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutForkserverExecutor<E>
where where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
E: Executor<EM, I, S, Z> + HasForkserver, E: Executor<EM, I, S, Z> + HasForkserver,
@ -482,7 +487,6 @@ where
/// This [`Executor`] can run binaries compiled for AFL/AFL++ that make use of a forkserver. /// 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. /// Shared memory feature is also available, but you have to set things up in your code.
/// Please refer to AFL++'s docs. <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md> /// Please refer to AFL++'s docs. <https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md>
#[allow(missing_debug_implementations)]
pub struct ForkserverExecutor<I, OT, S> pub struct ForkserverExecutor<I, OT, S>
where where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
@ -497,6 +501,23 @@ where
phantom: PhantomData<(I, S)>, phantom: PhantomData<(I, S)>,
} }
impl<I, OT, S> Debug for ForkserverExecutor<I, OT, S>
where
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
{
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<I, OT, S> ForkserverExecutor<I, OT, S> impl<I, OT, S> ForkserverExecutor<I, OT, S>
where where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,

View File

@ -3,7 +3,12 @@
//! //!
//! Needs the `fork` feature flag. //! 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")))] #[cfg(any(unix, all(windows, feature = "std")))]
use core::{ use core::{
@ -42,7 +47,6 @@ use crate::{
/// The inmem executor simply calls a target function, then returns afterwards. /// The inmem executor simply calls a target function, then returns afterwards.
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)]
pub struct InProcessExecutor<'a, H, I, OT, S> pub struct InProcessExecutor<'a, H, I, OT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
@ -58,6 +62,20 @@ where
phantom: PhantomData<(I, S)>, 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<I, S>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("InProcessExecutor")
.field("harness_fn", &"<fn>")
.field("observers", &self.observers)
.finish_non_exhaustive()
}
}
impl<'a, EM, H, I, OT, S, Z> Executor<EM, I, S, Z> for InProcessExecutor<'a, H, I, OT, S> impl<'a, EM, H, I, OT, S, Z> Executor<EM, I, S, Z> for InProcessExecutor<'a, H, I, OT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
@ -982,7 +1000,6 @@ where
/// [`InProcessForkExecutor`] is an executor that forks the current process before each execution. /// [`InProcessForkExecutor`] is an executor that forks the current process before each execution.
#[cfg(all(feature = "std", unix))] #[cfg(all(feature = "std", unix))]
#[allow(missing_debug_implementations)]
pub struct InProcessForkExecutor<'a, H, I, OT, S, SP> pub struct InProcessForkExecutor<'a, H, I, OT, S, SP>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
@ -996,6 +1013,22 @@ where
phantom: PhantomData<(I, S)>, 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<I, S>,
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))] #[cfg(all(feature = "std", unix))]
impl<'a, EM, H, I, OT, S, Z, SP> Executor<EM, I, S, Z> impl<'a, EM, H, I, OT, S, Z, SP> Executor<EM, I, S, Z>
for InProcessForkExecutor<'a, H, I, OT, S, SP> for InProcessForkExecutor<'a, H, I, OT, S, SP>

View File

@ -37,6 +37,7 @@ use crate::{
Error, Error,
}; };
use core::fmt::Debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// How an execution finished. /// How an execution finished.
@ -57,7 +58,7 @@ pub enum ExitKind {
crate::impl_serdeany!(ExitKind); crate::impl_serdeany!(ExitKind);
/// Holds a tuple of Observers /// Holds a tuple of Observers
pub trait HasObservers<I, OT, S> pub trait HasObservers<I, OT, S>: Debug
where where
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
{ {
@ -69,7 +70,7 @@ where
} }
/// An executor takes the given inputs, and runs the harness/target. /// An executor takes the given inputs, and runs the harness/target.
pub trait Executor<EM, I, S, Z> pub trait Executor<EM, I, S, Z>: Debug
where where
I: Input, I: Input,
{ {
@ -97,6 +98,7 @@ where
/// A simple executor that does nothing. /// A simple executor that does nothing.
/// If intput len is 0, `run_target` will return Err /// If intput len is 0, `run_target` will return Err
#[derive(Debug)]
struct NopExecutor {} struct NopExecutor {}
impl<EM, I, S, Z> Executor<EM, I, S, Z> for NopExecutor impl<EM, I, S, Z> Executor<EM, I, S, Z> for NopExecutor

View File

@ -1,6 +1,9 @@
//! A `ShadowExecutor` wraps an executor to have shadow observer that will not be considered by the feedbacks and the manager //! 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::{ use crate::{
executors::{Executor, ExitKind, HasObservers}, executors::{Executor, ExitKind, HasObservers},
@ -10,8 +13,7 @@ use crate::{
}; };
/// A [`ShadowExecutor`] wraps an executor and a set of shadow observers /// A [`ShadowExecutor`] wraps an executor and a set of shadow observers
#[allow(missing_debug_implementations)] pub struct ShadowExecutor<E: Debug, I: Debug, S, SOT: Debug> {
pub struct ShadowExecutor<E, I, S, SOT> {
/// The wrapped executor /// The wrapped executor
executor: E, executor: E,
/// The shadow observers /// The shadow observers
@ -20,7 +22,16 @@ pub struct ShadowExecutor<E, I, S, SOT> {
phantom: PhantomData<(I, S)>, phantom: PhantomData<(I, S)>,
} }
impl<E, I, S, SOT> ShadowExecutor<E, I, S, SOT> impl<E: Debug, I: Debug, S, SOT: Debug> Debug for ShadowExecutor<E, I, S, SOT> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("ShadowExecutor")
.field("executor", &self.executor)
.field("shadow_observers", &self.shadow_observers)
.finish()
}
}
impl<E: Debug, I: Debug, S, SOT: Debug> ShadowExecutor<E, I, S, SOT>
where where
SOT: ObserversTuple<I, S>, SOT: ObserversTuple<I, S>,
{ {
@ -65,6 +76,8 @@ where
impl<E, I, OT, S, SOT> HasObservers<I, OT, S> for ShadowExecutor<E, I, S, SOT> impl<E, I, OT, S, SOT> HasObservers<I, OT, S> for ShadowExecutor<E, I, S, SOT>
where where
I: Debug,
S: Debug,
E: HasObservers<I, OT, S>, E: HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>, SOT: ObserversTuple<I, S>,

View File

@ -1,7 +1,10 @@
//! A `TimeoutExecutor` sets a timeout before each target run //! A `TimeoutExecutor` sets a timeout before each target run
#[cfg(any(windows, unix))] #[cfg(any(windows, unix))]
use core::time::Duration; use core::{
fmt::{self, Debug, Formatter},
time::Duration,
};
use crate::{ use crate::{
executors::{Executor, ExitKind, HasObservers}, executors::{Executor, ExitKind, HasObservers},
@ -41,8 +44,23 @@ struct Timeval {
pub tv_usec: i64, 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)] #[repr(C)]
#[cfg(unix)] #[cfg(unix)]
#[derive(Debug)]
struct Itimerval { struct Itimerval {
pub it_interval: Timeval, pub it_interval: Timeval,
pub it_value: 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 /// The timeout excutor is a wrapper that sets a timeout before each run
#[allow(missing_debug_implementations)]
pub struct TimeoutExecutor<E> { pub struct TimeoutExecutor<E> {
executor: E, executor: E,
#[cfg(unix)] #[cfg(unix)]
@ -87,6 +104,24 @@ pub struct TimeoutExecutor<E> {
critical: RTL_CRITICAL_SECTION, critical: RTL_CRITICAL_SECTION,
} }
impl<E: Debug> Debug for TimeoutExecutor<E> {
#[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)] #[cfg(windows)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
type PTP_TIMER_CALLBACK = unsafe extern "system" fn( type PTP_TIMER_CALLBACK = unsafe extern "system" fn(

View File

@ -1,11 +1,17 @@
//! A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`]. //! 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`]. /// A wrapper for any [`Executor`] to make it implement [`HasObservers`] using a given [`ObserversTuple`].
#[allow(missing_debug_implementations)] #[derive(Debug)]
pub struct WithObservers<E, OT> { pub struct WithObservers<E: Debug, OT: Debug> {
executor: E, executor: E,
observers: OT, observers: OT,
} }
@ -14,6 +20,7 @@ impl<EM, I, S, Z, E, OT> Executor<EM, I, S, Z> for WithObservers<E, OT>
where where
I: Input, I: Input,
E: Executor<EM, I, S, Z>, E: Executor<EM, I, S, Z>,
OT: Debug,
{ {
fn run_target( fn run_target(
&mut self, &mut self,
@ -26,7 +33,7 @@ where
} }
} }
impl<I, S, E, OT> HasObservers<I, OT, S> for WithObservers<E, OT> impl<I, S, E: Debug, OT: Debug> HasObservers<I, OT, S> for WithObservers<E, OT>
where where
I: Input, I: Input,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
@ -40,7 +47,7 @@ where
} }
} }
impl<E, OT> WithObservers<E, OT> { impl<E: Debug, OT: Debug> WithObservers<E, OT> {
/// Wraps the given [`Executor`] with the given [`ObserversTuple`] to implement [`HasObservers`]. /// 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 /// If the executor already implements [`HasObservers`], then the original implementation will be overshadowed by

View File

@ -39,7 +39,7 @@ pub type MaxMapOneOrFilledFeedback<FT, I, O, S, T> =
MapFeedback<FT, I, OneOrFilledIsNovel, O, MaxReducer, S, T>; MapFeedback<FT, I, OneOrFilledIsNovel, O, MaxReducer, S, T>;
/// A `Reducer` function is used to aggregate values for the novelty search /// A `Reducer` function is used to aggregate values for the novelty search
pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static + Debug
where where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, 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. /// A `IsNovel` function is used to discriminate if a reduced value is considered novel.
pub trait IsNovel<T>: Serialize + serde::de::DeserializeOwned + 'static pub trait IsNovel<T>: Serialize + serde::de::DeserializeOwned + 'static + Debug
where where
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned,
{ {
@ -270,7 +270,7 @@ where
impl<T> FeedbackState for MapFeedbackState<T> impl<T> FeedbackState for MapFeedbackState<T>
where 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> { fn reset(&mut self) -> Result<(), Error> {
self.history_map self.history_map
@ -357,7 +357,7 @@ where
O: MapObserver<T>, O: MapObserver<T>,
N: IsNovel<T>, N: IsNovel<T>,
I: Input, I: Input,
S: HasFeedbackStates<FT> + HasClientPerfMonitor, S: HasFeedbackStates<FT> + HasClientPerfMonitor + Debug,
FT: FeedbackStatesTuple, FT: FeedbackStatesTuple,
{ {
fn is_interesting<EM, OT>( fn is_interesting<EM, OT>(

View File

@ -28,12 +28,16 @@ use crate::{
Error, Error,
}; };
use core::{marker::PhantomData, time::Duration}; use core::{
fmt::{self, Debug, Formatter},
marker::PhantomData,
time::Duration,
};
/// Feedbacks evaluate the observers. /// Feedbacks evaluate the observers.
/// Basically, they reduce the information provided by an observer to a value, /// Basically, they reduce the information provided by an observer to a value,
/// indicating the "interestingness" of the last run. /// indicating the "interestingness" of the last run.
pub trait Feedback<I, S>: Named pub trait Feedback<I, S>: Named + Debug
where where
I: Input, I: Input,
S: HasClientPerfMonitor, S: HasClientPerfMonitor,
@ -103,7 +107,7 @@ where
/// [`FeedbackState`] is the data associated with a [`Feedback`] that must persist as part /// [`FeedbackState`] is the data associated with a [`Feedback`] that must persist as part
/// of the fuzzer State /// 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 /// Reset the internal state
fn reset(&mut self) -> Result<(), Error> { fn reset(&mut self) -> Result<(), Error> {
Ok(()) Ok(())
@ -111,7 +115,7 @@ pub trait FeedbackState: Named + Serialize + serde::de::DeserializeOwned {
} }
/// A haskell-style tuple of feedback states /// 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 /// Resets all the feedback states of the tuple
fn reset_all(&mut self) -> Result<(), Error>; fn reset_all(&mut self) -> Result<(), Error>;
} }
@ -134,7 +138,7 @@ where
} }
/// A cobined feedback consisting of ultiple [`Feedback`]s /// A cobined feedback consisting of ultiple [`Feedback`]s
#[allow(missing_debug_implementations)] #[derive(Debug)]
pub struct CombinedFeedback<A, B, I, S, FL> pub struct CombinedFeedback<A, B, I, S, FL>
where where
A: Feedback<I, S>, A: Feedback<I, S>,
@ -190,7 +194,7 @@ where
B: Feedback<I, S>, B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>, FL: FeedbackLogic<A, B, I, S>,
I: Input, I: Input,
S: HasClientPerfMonitor, S: HasClientPerfMonitor + Debug,
{ {
fn is_interesting<EM, OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
@ -253,7 +257,7 @@ where
} }
/// Logical combination of two feedbacks /// Logical combination of two feedbacks
pub trait FeedbackLogic<A, B, I, S>: 'static pub trait FeedbackLogic<A, B, I, S>: 'static + Debug
where where
A: Feedback<I, S>, A: Feedback<I, S>,
B: Feedback<I, S>, B: Feedback<I, S>,
@ -545,7 +549,7 @@ pub type EagerOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicEagerOr
pub type FastOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicFastOr>; pub type FastOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicFastOr>;
/// Compose feedbacks with an `NOT` operation /// Compose feedbacks with an `NOT` operation
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct NotFeedback<A, I, S> pub struct NotFeedback<A, I, S>
where where
A: Feedback<I, S>, A: Feedback<I, S>,
@ -559,6 +563,20 @@ where
phantom: PhantomData<(I, S)>, phantom: PhantomData<(I, S)>,
} }
impl<A, I, S> Debug for NotFeedback<A, I, S>
where
A: Feedback<I, S>,
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<A, I, S> Feedback<I, S> for NotFeedback<A, I, S> impl<A, I, S> Feedback<I, S> for NotFeedback<A, I, S>
where where
A: Feedback<I, S>, A: Feedback<I, S>,

View File

@ -130,7 +130,7 @@ impl Debug for MOpt {
.field("\n\ncore_operator_cycles", &self.core_operator_cycles) .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_v2", &self.core_operator_cycles_v2)
.field("\n\ncore_operator_cycles_v3", &self.core_operator_cycles_v3) .field("\n\ncore_operator_cycles_v3", &self.core_operator_cycles_v3)
.finish() .finish_non_exhaustive()
} }
} }

View File

@ -4,7 +4,7 @@ use alloc::{
string::{String, ToString}, string::{String, ToString},
vec::Vec, vec::Vec,
}; };
use core::fmt::Debug;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{ use crate::{
@ -79,7 +79,7 @@ impl CmpValuesMetadata {
} }
/// A [`CmpMap`] traces comparisons during the current execution /// A [`CmpMap`] traces comparisons during the current execution
pub trait CmpMap { pub trait CmpMap: Debug {
/// Get the number of cmps /// Get the number of cmps
fn len(&self) -> usize; fn len(&self) -> usize;

View File

@ -43,7 +43,10 @@
#![cfg(feature = "std")] #![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}; 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`]. /// A `MessageFileReader` reads a stream of [`SymExpr`] and their corresponding [`SymExprRef`]s from any [`Read`].
#[allow(missing_debug_implementations)]
pub struct MessageFileReader<R: Read> { pub struct MessageFileReader<R: Read> {
reader: R, reader: R,
deserializer_config: DefaultOptions, deserializer_config: DefaultOptions,
current_id: usize, current_id: usize,
} }
impl<R: Read> Debug for MessageFileReader<R> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "MessageFileReader {{ current_id: {} }}", self.current_id)
}
}
impl<R: Read> MessageFileReader<R> { impl<R: Read> MessageFileReader<R> {
/// Construct from the given reader. /// Construct from the given reader.
pub fn from_reader(reader: R) -> Self { pub fn from_reader(reader: R) -> Self {
@ -204,7 +212,6 @@ impl<R: Read> MessageFileReader<R> {
/// A `MessageFileWriter` writes a stream of [`SymExpr`] to any [`Write`]. For each written expression, it returns /// 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. /// a [`SymExprRef`] which should be used to refer back to it.
#[allow(missing_debug_implementations)]
pub struct MessageFileWriter<W: Write> { pub struct MessageFileWriter<W: Write> {
id_counter: usize, id_counter: usize,
writer: W, writer: W,
@ -212,6 +219,18 @@ pub struct MessageFileWriter<W: Write> {
serialization_options: DefaultOptions, serialization_options: DefaultOptions,
} }
impl<W> Debug for MessageFileWriter<W>
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<W: Write + Seek> MessageFileWriter<W> { impl<W: Write + Seek> MessageFileWriter<W> {
/// Create a `MessageFileWriter` from the given [`Write`]. /// Create a `MessageFileWriter` from the given [`Write`].
pub fn from_writer(mut writer: W) -> io::Result<Self> { pub fn from_writer(mut writer: W) -> io::Result<Self> {

View File

@ -25,7 +25,7 @@ use crate::{
}; };
/// A [`MapObserver`] observes the static map, as oftentimes used for afl-like coverage information /// A [`MapObserver`] observes the static map, as oftentimes used for afl-like coverage information
pub trait MapObserver<T>: HasLen + Named + Serialize + serde::de::DeserializeOwned pub trait MapObserver<T>: HasLen + Named + Serialize + serde::de::DeserializeOwned + Debug
where where
T: PrimInt + Default + Copy + Debug, T: PrimInt + Default + Copy + Debug,
{ {

View File

@ -9,7 +9,7 @@ pub use cmp::*;
pub mod concolic; pub mod concolic;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use core::time::Duration; use core::{fmt::Debug, time::Duration};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -22,7 +22,7 @@ use crate::{
/// Observers observe different information about the target. /// Observers observe different information about the target.
/// They can then be used by various sorts of feedback. /// They can then be used by various sorts of feedback.
pub trait Observer<I, S>: Named { pub trait Observer<I, S>: Named + Debug {
/// The testcase finished execution, calculate any changes. /// The testcase finished execution, calculate any changes.
/// Reserved for future use. /// Reserved for future use.
#[inline] #[inline]
@ -44,7 +44,7 @@ pub trait Observer<I, S>: Named {
} }
/// A haskell-style tuple of observers /// A haskell-style tuple of observers
pub trait ObserversTuple<I, S>: MatchName { pub trait ObserversTuple<I, S>: MatchName + Debug {
/// This is called right before the next execution. /// This is called right before the next execution.
fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error>; fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error>;

View File

@ -1,6 +1,6 @@
//! The tracing stage can trace the target and enrich a testcase with metadata, for example for `CmpLog`. //! 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::{ use crate::{
corpus::Corpus, corpus::Corpus,
@ -119,7 +119,7 @@ where
E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>, E: Executor<EM, I, S, Z> + HasObservers<I, OT, S>,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
SOT: ObserversTuple<I, S>, SOT: ObserversTuple<I, S>,
S: HasClientPerfMonitor + HasExecutions + HasCorpus<C, I>, S: HasClientPerfMonitor + HasExecutions + HasCorpus<C, I> + Debug,
{ {
#[inline] #[inline]
fn perform( fn perform(

View File

@ -104,7 +104,9 @@ fn main() {
&mut clang_constants_file, &mut clang_constants_file,
"// These constants are autogenerated by build.rs "// These constants are autogenerated by build.rs
/// The path to the `clang` executable
pub const CLANG_PATH: &str = {:?}; pub const CLANG_PATH: &str = {:?};
/// The path to the `clang++` executable
pub const CLANGXX_PATH: &str = {:?}; pub const CLANGXX_PATH: &str = {:?};
/// The size of the edges map /// The size of the edges map
@ -165,7 +167,9 @@ fn main() {
&mut clang_constants_file, &mut clang_constants_file,
"// These constants are autogenerated by build.rs "// These constants are autogenerated by build.rs
/// The path to the `clang` executable
pub const CLANG_PATH: &str = \"clang\"; pub const CLANG_PATH: &str = \"clang\";
/// The path to the `clang++` executable
pub const CLANGXX_PATH: &str = \"clang++\"; pub const CLANGXX_PATH: &str = \"clang++\";
" "
) )

View File

@ -22,14 +22,19 @@ fn dll_extension<'a>() -> &'a str {
include!(concat!(env!("OUT_DIR"), "/clang_constants.rs")); include!(concat!(env!("OUT_DIR"), "/clang_constants.rs"));
/// The supported LLVM passes
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LLVMPasses { pub enum LLVMPasses {
//CmpLogIns, //CmpLogIns,
/// The CmpLog pass
CmpLogRtn, CmpLogRtn,
/// The AFL coverage pass
AFLCoverage, AFLCoverage,
} }
impl LLVMPasses { impl LLVMPasses {
/// Gets the path of the LLVM pass
#[must_use] #[must_use]
pub fn path(&self) -> PathBuf { pub fn path(&self) -> PathBuf {
match self { match self {
@ -43,6 +48,7 @@ impl LLVMPasses {
/// Wrap Clang /// Wrap Clang
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
#[derive(Debug)]
pub struct ClangWrapper { pub struct ClangWrapper {
is_silent: bool, is_silent: bool,
optimize: bool, optimize: bool,
@ -269,11 +275,13 @@ impl ClangWrapper {
} }
} }
/// Sets the wrapped `cc` compiler
pub fn wrapped_cc(&mut self, cc: String) -> &'_ mut Self { pub fn wrapped_cc(&mut self, cc: String) -> &'_ mut Self {
self.wrapped_cc = cc; self.wrapped_cc = cc;
self self
} }
/// Sets the wrapped `cxx` compiler
pub fn wrapped_cxx(&mut self, cxx: String) -> &'_ mut Self { pub fn wrapped_cxx(&mut self, cxx: String) -> &'_ mut Self {
self.wrapped_cxx = cxx; self.wrapped_cxx = cxx;
self self
@ -291,7 +299,7 @@ impl ClangWrapper {
self self
} }
// Add LLVM pass /// Add LLVM pass
pub fn add_pass(&mut self, pass: LLVMPasses) -> &'_ mut Self { pub fn add_pass(&mut self, pass: LLVMPasses) -> &'_ mut Self {
self.passes.push(pass); self.passes.push(pass);
self self

View File

@ -1,5 +1,48 @@
//! Compiler Wrapper from `LibAFL` //! 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}; use std::{convert::Into, path::Path, process::Command, string::String, vec::Vec};
pub mod clang; pub mod clang;

View File

@ -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 proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
/// Derive macro to implement `SerdeAny`, to use a type in a `SerdeAnyMap`
#[proc_macro_derive(SerdeAny)] #[proc_macro_derive(SerdeAny)]
pub fn libafl_serdeany_derive(input: TokenStream) -> TokenStream { pub fn libafl_serdeany_derive(input: TokenStream) -> TokenStream {
let name = parse_macro_input!(input as DeriveInput).ident; let name = parse_macro_input!(input as DeriveInput).ident;

View File

@ -6,17 +6,28 @@ use nix::{
}; };
use backtrace::Backtrace; use backtrace::Backtrace;
#[cfg(unix)] #[cfg(any(
target_os = "linux",
all(target_arch = "aarch64", target_os = "android")
))]
use libc::{sysconf, _SC_PAGESIZE}; use libc::{sysconf, _SC_PAGESIZE};
use rangemap::RangeSet; use rangemap::RangeSet;
use serde::{Deserialize, Serialize}; 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::{ use crate::{
asan::errors::{AsanError, AsanErrors}, asan::errors::{AsanError, AsanErrors},
FridaOptions, FridaOptions,
}; };
/// An allocator wrapper with binary-only address sanitization
#[derive(Debug)]
#[allow(missing_docs)]
pub struct Allocator { pub struct Allocator {
#[allow(dead_code)] #[allow(dead_code)]
options: FridaOptions, options: FridaOptions,
@ -44,7 +55,9 @@ macro_rules! map_to_shadow {
}; };
} }
/// Metadata for an allocation
#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[allow(missing_docs)]
pub struct AllocationMetadata { pub struct AllocationMetadata {
pub address: usize, pub address: usize,
pub size: usize, pub size: usize,
@ -56,6 +69,21 @@ pub struct AllocationMetadata {
} }
impl Allocator { 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] #[must_use]
pub fn new(options: FridaOptions) -> Self { pub fn new(options: FridaOptions) -> Self {
let ret = unsafe { sysconf(_SC_PAGESIZE) }; let ret = unsafe { sysconf(_SC_PAGESIZE) };
@ -118,11 +146,6 @@ impl Allocator {
shadow_bit = try_shadow_bit; 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); assert!(shadow_bit != 0);
// attempt to pre-map the entire shadow-memory space // attempt to pre-map the entire shadow-memory space
@ -188,6 +211,7 @@ impl Allocator {
None None
} }
/// Allocate a new allocation of the given size.
#[must_use] #[must_use]
#[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_safety_doc)]
pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void { pub unsafe fn alloc(&mut self, size: usize, _alignment: usize) -> *mut c_void {
@ -272,6 +296,7 @@ impl Allocator {
address address
} }
/// Releases the allocation at the given address.
#[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_safety_doc)]
pub unsafe fn release(&mut self, ptr: *mut c_void) { pub unsafe fn release(&mut self, ptr: *mut c_void) {
let mut metadata = if let Some(metadata) = self.allocations.get_mut(&(ptr as usize)) { 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); Self::poison(shadow_mapping_start, metadata.size);
} }
/// Finds the metadata for the allocation at the given address.
pub fn find_metadata( pub fn find_metadata(
&mut self, &mut self,
ptr: usize, ptr: usize,
@ -328,6 +354,7 @@ impl Allocator {
closest closest
} }
/// Resets the allocator contents
pub fn reset(&mut self) { pub fn reset(&mut self) {
let mut tmp_allocations = Vec::new(); let mut tmp_allocations = Vec::new();
for (address, mut allocation) in self.allocations.drain() { for (address, mut allocation) in self.allocations.drain() {
@ -358,6 +385,7 @@ impl Allocator {
self.total_allocation_size = 0; 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 { pub fn get_usable_size(&self, ptr: *mut c_void) -> usize {
match self.allocations.get(&(ptr as usize)) { match self.allocations.get(&(ptr as usize)) {
Some(metadata) => metadata.size, Some(metadata) => metadata.size,
@ -388,6 +416,7 @@ impl Allocator {
} }
} }
/// Poisonn an area in memory
pub fn poison(start: usize, size: usize) { pub fn poison(start: usize, size: usize) {
// println!("poisoning {:x} for {:x}", start, size / 8 + 1); // println!("poisoning {:x} for {:x}", start, size / 8 + 1);
unsafe { unsafe {
@ -448,17 +477,21 @@ impl Allocator {
(shadow_mapping_start, (end - start) / 8) (shadow_mapping_start, (end - start) / 8)
} }
/// Maps the address to a shadow address
#[inline]
#[must_use] #[must_use]
pub fn map_to_shadow(&self, start: usize) -> usize { pub fn map_to_shadow(&self, start: usize) -> usize {
map_to_shadow!(self, start) map_to_shadow!(self, start)
} }
/// Checks if the currennt address is one of ours
#[inline] #[inline]
pub fn is_managed(&self, ptr: *mut c_void) -> bool { pub fn is_managed(&self, ptr: *mut c_void) -> bool {
//self.allocations.contains_key(&(ptr as usize)) //self.allocations.contains_key(&(ptr as usize))
self.base_mapping_addr <= ptr as usize && (ptr as usize) < self.current_mapping_addr 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) { pub fn check_for_leaks(&self) {
for metadata in self.allocations.values() { for metadata in self.allocations.values() {
if !metadata.freed { if !metadata.freed {

View File

@ -7,6 +7,7 @@ this helps finding mem errors early.
*/ */
use backtrace::Backtrace; use backtrace::Backtrace;
use core::fmt::{self, Debug, Formatter};
use frida_gum::{ModuleDetails, NativePointer, RangeDetails}; use frida_gum::{ModuleDetails, NativePointer, RangeDetails};
use hashbrown::HashMap; use hashbrown::HashMap;
use nix::sys::mman::{mmap, MapFlags, ProtFlags}; use nix::sys::mman::{mmap, MapFlags, ProtFlags};
@ -69,10 +70,12 @@ const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON;
#[cfg(not(target_vendor = "apple"))] #[cfg(not(target_vendor = "apple"))]
const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANONYMOUS; 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")] #[cfg(target_arch = "x86_64")]
pub const ASAN_SAVE_REGISTER_COUNT: usize = 19; 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")] #[cfg(target_arch = "x86_64")]
pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [ pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [
"rax", "rax",
@ -96,6 +99,7 @@ pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [
"actual rip", "actual rip",
]; ];
/// The count of registers that need to be saved by the asan runtime
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
pub const ASAN_SAVE_REGISTER_COUNT: usize = 32; pub const ASAN_SAVE_REGISTER_COUNT: usize = 32;
@ -128,6 +132,17 @@ pub struct AsanRuntime {
shadow_check_func: Option<extern "C" fn(*const c_void, usize) -> bool>, shadow_check_func: Option<extern "C" fn(*const c_void, usize) -> 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", &"<ModuleMap>")
.field("suppressed_addresses", &self.suppressed_addresses)
.finish_non_exhaustive()
}
}
impl AsanRuntime { impl AsanRuntime {
/// Create a new `AsanRuntime` /// Create a new `AsanRuntime`
#[must_use] #[must_use]
@ -261,15 +276,18 @@ impl AsanRuntime {
self.allocator.reset(); self.allocator.reset();
} }
/// Gets the allocator
#[must_use] #[must_use]
pub fn allocator(&self) -> &Allocator { pub fn allocator(&self) -> &Allocator {
&self.allocator &self.allocator
} }
/// Gets the allocator, mut
pub fn allocator_mut(&mut self) -> &mut Allocator { pub fn allocator_mut(&mut self) -> &mut Allocator {
&mut self.allocator &mut self.allocator
} }
/// The function that checks the shadow byte
#[must_use] #[must_use]
pub fn shadow_check_func(&self) -> &Option<extern "C" fn(*const c_void, usize) -> bool> { pub fn shadow_check_func(&self) -> &Option<extern "C" fn(*const c_void, usize) -> bool> {
&self.shadow_check_func &self.shadow_check_func
@ -332,7 +350,7 @@ impl AsanRuntime {
.map_shadow_for_region(tls_start, tls_end, true); .map_shadow_for_region(tls_start, tls_end, true);
println!( println!(
"registering thread with stack {:x}:{:x} and tls {:x}:{:x}", "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) (start, end)
} }
/// Gets the current instruction pointer
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[must_use] #[must_use]
#[inline] #[inline]
@ -423,6 +442,7 @@ impl AsanRuntime {
Interceptor::current_invocation().cpu_context().pc() as usize Interceptor::current_invocation().cpu_context().pc() as usize
} }
/// Gets the current instruction pointer
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[must_use] #[must_use]
#[inline] #[inline]
@ -447,7 +467,7 @@ impl AsanRuntime {
unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type { unsafe extern "C" fn [<replacement_ $name>]($($param: $param_type),*) -> $return_type {
let mut invocation = Interceptor::current_invocation(); let mut invocation = Interceptor::current_invocation();
let this = &mut *(invocation.replacement_data().unwrap().0 as *mut AsanRuntime); 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() { if !this.suppressed_addresses.contains(&real_address) && this.module_map.as_ref().unwrap().find(real_address as u64).is_some() {
this.[<hook_ $name>]($($param),*) this.[<hook_ $name>]($($param),*)
} else { } else {
@ -2118,6 +2138,7 @@ impl AsanRuntime {
Err(()) Err(())
} }
/// Checks if the current instruction is interesting for address sanitization.
#[cfg(all(target_arch = "x86_64", unix))] #[cfg(all(target_arch = "x86_64", unix))]
#[inline] #[inline]
#[allow(clippy::unused_self)] #[allow(clippy::unused_self)]
@ -2182,6 +2203,7 @@ impl AsanRuntime {
Err(()) Err(())
} }
/// Emits a asan shadow byte check.
#[inline] #[inline]
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]

View File

@ -1,3 +1,4 @@
//! Errors that can be caught by the `libafl_frida` address sanitizer.
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use crate::asan::asan_rt::ASAN_SAVE_REGISTER_NAMES; use crate::asan::asan_rt::ASAN_SAVE_REGISTER_NAMES;
use backtrace::Backtrace; use backtrace::Backtrace;
@ -548,7 +549,7 @@ impl AsanErrors {
pub static mut ASAN_ERRORS: Option<AsanErrors> = None; pub static mut ASAN_ERRORS: Option<AsanErrors> = None;
/// An observer for frida address sanitizer `AsanError`s for a frida executor run /// 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)] #[allow(clippy::unsafe_derive_deserialize)]
pub struct AsanErrorsObserver { pub struct AsanErrorsObserver {
errors: OwnedPtr<Option<AsanErrors>>, errors: OwnedPtr<Option<AsanErrors>>,

View File

@ -1,3 +1,4 @@
//! The allocator hooks for address sanitizer.
use crate::{ use crate::{
alloc::Allocator, alloc::Allocator,
asan::{ asan::{
@ -1013,6 +1014,7 @@ impl AsanRuntime {
unsafe { atoi(s) } unsafe { atoi(s) }
} }
/// Hooks `atol`
#[inline] #[inline]
pub fn hook_atol(&mut self, s: *const c_char) -> i32 { pub fn hook_atol(&mut self, s: *const c_char) -> i32 {
extern "C" { extern "C" {
@ -1031,6 +1033,7 @@ impl AsanRuntime {
unsafe { atol(s) } unsafe { atol(s) }
} }
/// Hooks `atoll`
#[inline] #[inline]
pub fn hook_atoll(&mut self, s: *const c_char) -> i64 { pub fn hook_atoll(&mut self, s: *const c_char) -> i64 {
extern "C" { extern "C" {
@ -1049,6 +1052,7 @@ impl AsanRuntime {
unsafe { atoll(s) } unsafe { atoll(s) }
} }
/// Hooks `wcslen`
#[inline] #[inline]
pub fn hook_wcslen(&mut self, s: *const wchar_t) -> usize { pub fn hook_wcslen(&mut self, s: *const wchar_t) -> usize {
extern "C" { extern "C" {
@ -1067,6 +1071,7 @@ impl AsanRuntime {
size size
} }
/// Hooks `wcscpy`
#[inline] #[inline]
pub fn hook_wcscpy(&mut self, dest: *mut wchar_t, src: *const wchar_t) -> *mut wchar_t { pub fn hook_wcscpy(&mut self, dest: *mut wchar_t, src: *const wchar_t) -> *mut wchar_t {
extern "C" { extern "C" {
@ -1098,6 +1103,7 @@ impl AsanRuntime {
unsafe { wcscpy(dest, src) } unsafe { wcscpy(dest, src) }
} }
/// Hooks `wcscmp`
#[inline] #[inline]
pub fn hook_wcscmp(&mut self, s1: *const wchar_t, s2: *const wchar_t) -> i32 { pub fn hook_wcscmp(&mut self, s1: *const wchar_t, s2: *const wchar_t) -> i32 {
extern "C" { extern "C" {

View File

@ -1,3 +1,5 @@
//! Address sanitization using [`frida`](https://frida.re/)
pub mod asan_rt; pub mod asan_rt;
pub mod errors; pub mod errors;
#[allow(missing_docs)]
pub mod hook_funcs; pub mod hook_funcs;

View File

@ -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 dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
use libafl_targets;
use libafl_targets::CMPLOG_MAP_W; use libafl_targets::CMPLOG_MAP_W;
use std::ffi::c_void; use std::ffi::c_void;
extern crate libafl_targets;
extern "C" { extern "C" {
/// Tracks cmplog instructions
pub fn __libafl_targets_cmplog_instructions(k: u64, shape: u8, arg1: u64, arg2: u64); 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"))] #[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
use crate::helper::FridaInstrumentationHelper; use crate::helper::FridaInstrumentationHelper;
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
/// Speciial CmpLog Cases for `aarch64`
#[derive(Debug)]
pub enum SpecialCmpLogCase { pub enum SpecialCmpLogCase {
/// Test bit and branch if zero
Tbz, Tbz,
/// Test bit and branch if not zero
Tbnz, Tbnz,
} }
@ -27,21 +38,31 @@ use capstone::{
Capstone, Insn, 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 { pub struct CmpLogRuntime {
ops_save_register_and_blr_to_populate: Option<Box<[u8]>>, ops_save_register_and_blr_to_populate: Option<Box<[u8]>>,
ops_handle_tbz_masking: Option<Box<[u8]>>, ops_handle_tbz_masking: Option<Box<[u8]>>,
ops_handle_tbnz_masking: Option<Box<[u8]>>, ops_handle_tbnz_masking: Option<Box<[u8]>>,
} }
#[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 { impl CmpLogRuntime {
/// Create a new [`CmpLogRuntime`]
#[must_use] #[must_use]
pub fn new() -> CmpLogRuntime { pub fn new() -> CmpLogRuntime {
Self { Self {
@ -179,6 +200,9 @@ impl CmpLogRuntime {
.into_boxed_slice(), .into_boxed_slice(),
); );
} }
/// Initialize this `CmpLog` runtime.
/// This will generate the instrumentation blobs for the current arch.
pub fn init(&mut self) { pub fn init(&mut self) {
self.generate_instrumentation_blobs(); self.generate_instrumentation_blobs();
} }
@ -204,9 +228,9 @@ impl CmpLogRuntime {
self.ops_handle_tbnz_masking.as_ref().unwrap() 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"))] #[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
#[inline] #[inline]
/// Emit the instrumentation code which is responsible for opernads value extraction and cmplog map population
pub fn emit_comparison_handling( pub fn emit_comparison_handling(
&self, &self,
_address: u64, _address: u64,

View File

@ -1,3 +1,4 @@
//! Functionality regarding binary-only coverage collection.
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi}; use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
use std::ffi::c_void; use std::ffi::c_void;
@ -11,6 +12,8 @@ use frida_gum::{instruction_writer::InstructionWriter, stalker::StalkerOutput};
/// (Default) map size for frida coverage reporting /// (Default) map size for frida coverage reporting
pub const MAP_SIZE: usize = 64 * 1024; pub const MAP_SIZE: usize = 64 * 1024;
/// Frida binary-only coverage
#[derive(Debug)]
pub struct CoverageRuntime { pub struct CoverageRuntime {
map: [u8; MAP_SIZE], map: [u8; MAP_SIZE],
previous_pc: u64, previous_pc: u64,
@ -25,6 +28,7 @@ impl Default for CoverageRuntime {
} }
impl CoverageRuntime { impl CoverageRuntime {
/// Create a new coverage runtime
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@ -35,13 +39,17 @@ impl CoverageRuntime {
} }
} }
/// Initialize the coverage runtime
pub fn init(&mut self) { pub fn init(&mut self) {
self.generate_maybe_log_blob(); self.generate_maybe_log_blob();
} }
/// Retrieve the coverage map pointer
pub fn map_ptr_mut(&mut self) -> *mut u8 { pub fn map_ptr_mut(&mut self) -> *mut u8 {
self.map.as_mut_ptr() self.map.as_mut_ptr()
} }
/// Retrieve the `maybe_log` code blob, that will write coverage into the map
#[must_use] #[must_use]
pub fn blob_maybe_log(&self) -> &[u8] { pub fn blob_maybe_log(&self) -> &[u8] {
self.blob_maybe_log.as_ref().unwrap() 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()); 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] #[inline]
pub fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) { pub fn emit_coverage_mapping(&mut self, address: u64, output: &StalkerOutput) {
let writer = output.writer(); let writer = output.writer();

View File

@ -9,7 +9,7 @@ use rangemap::RangeMap;
use std::hash::Hasher; use std::hash::Hasher;
/// Generates `DrCov` traces /// Generates `DrCov` traces
#[derive(Clone, Debug)] #[derive(Debug, Clone)]
pub struct DrCovRuntime { pub struct DrCovRuntime {
/// The basic blocks of this execution /// The basic blocks of this execution
pub drcov_basic_blocks: Vec<DrCovBasicBlock>, pub drcov_basic_blocks: Vec<DrCovBasicBlock>,

View File

@ -1,13 +1,11 @@
use crate::helper::FridaHelper; use crate::helper::FridaHelper;
use std::{ffi::c_void, marker::PhantomData}; use core::fmt::{self, Debug, Formatter};
use frida_gum::{ use frida_gum::{
stalker::{NoneEventSink, Stalker}, stalker::{NoneEventSink, Stalker},
Gum, NativePointer, Gum, MemoryRange, NativePointer,
}; };
use std::{ffi::c_void, marker::PhantomData};
use frida_gum::MemoryRange;
use libafl::{ use libafl::{
executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, executors::{Executor, ExitKind, HasObservers, InProcessExecutor},
@ -22,6 +20,7 @@ use crate::asan::errors::ASAN_ERRORS;
#[cfg(windows)] #[cfg(windows)]
use libafl::executors::inprocess::{HasInProcessHandlers, InProcessHandlers}; 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> pub struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where where
FH: FridaHelper<'b>, FH: FridaHelper<'b>,
@ -38,6 +37,22 @@ where
_phantom: PhantomData<&'b u8>, _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<I, S>,
{
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<EM, I, S, Z> impl<'a, 'b, 'c, EM, FH, H, I, OT, S, Z> Executor<EM, I, S, Z>
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where where
@ -107,6 +122,7 @@ where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>, OT: ObserversTuple<I, S>,
{ {
/// Creates a new [`FridaInProcessExecutor`]
pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT, S>, helper: &'c mut FH) -> Self { pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT, S>, helper: &'c mut FH) -> Self {
let mut stalker = Stalker::new(gum); let mut stalker = Stalker::new(gum);
// Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that // Include the current module (the fuzzer) in stalked ranges. We clone the ranges so that

View File

@ -2,53 +2,39 @@ use libafl::inputs::{HasTargetBytes, Input};
use libafl::Error; use libafl::Error;
use libafl_targets::drcov::DrCovBasicBlock; 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")] #[cfg(target_arch = "aarch64")]
use capstone::{ use capstone::{
arch::{self, arm64::Arm64OperandType, ArchOperand::Arm64Operand, BuildsCapstone}, arch::{self, arm64::Arm64OperandType, ArchOperand::Arm64Operand, BuildsCapstone},
Capstone, Insn, Capstone, Insn,
}; };
#[cfg(all(target_arch = "x86_64", unix))] #[cfg(all(target_arch = "x86_64", unix))]
use capstone::{ use capstone::{
arch::{self, BuildsCapstone}, arch::{self, BuildsCapstone},
Capstone, RegId, Capstone, RegId,
}; };
use core::fmt::{self, Debug, Formatter};
#[cfg(target_arch = "aarch64")]
use num_traits::cast::FromPrimitive;
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
use frida_gum::instruction_writer::Aarch64Register; use frida_gum::instruction_writer::Aarch64Register;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
use frida_gum::instruction_writer::X86Register; use frida_gum::instruction_writer::X86Register;
use frida_gum::{
instruction_writer::InstructionWriter, stalker::Transformer, ModuleDetails, ModuleMap,
};
#[cfg(unix)] #[cfg(unix)]
use frida_gum::CpuContext; use frida_gum::CpuContext;
use frida_gum::{
use frida_gum::{Gum, Module, PageProtection}; instruction_writer::InstructionWriter, stalker::Transformer, Gum, Module, ModuleDetails,
ModuleMap, PageProtection,
use rangemap::RangeMap; };
#[cfg(unix)] #[cfg(unix)]
use nix::sys::mman::{mmap, MapFlags, ProtFlags}; use nix::sys::mman::{mmap, MapFlags, ProtFlags};
#[cfg(target_arch = "aarch64")]
#[cfg(unix)] use num_traits::cast::FromPrimitive;
use crate::{asan::asan_rt::AsanRuntime, FridaOptions}; use rangemap::RangeMap;
#[cfg(windows)]
use crate::FridaOptions;
use crate::drcov_rt::DrCovRuntime;
use crate::coverage_rt::CoverageRuntime;
#[cfg(feature = "cmplog")]
use crate::cmplog_rt::CmpLogRuntime;
#[cfg(any(target_vendor = "apple"))] #[cfg(any(target_vendor = "apple"))]
const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON; 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; const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANONYMOUS;
/// An helper that feeds `FridaInProcessExecutor` with user-supplied instrumentation /// An helper that feeds `FridaInProcessExecutor` with user-supplied instrumentation
pub trait FridaHelper<'a> { pub trait FridaHelper<'a>: Debug {
/// Access to the stalker `Transformer` /// Access to the stalker `Transformer`
fn transformer(&self) -> &Transformer<'a>; fn transformer(&self) -> &Transformer<'a>;
@ -76,8 +62,10 @@ pub trait FridaHelper<'a> {
/// pointer to the frida coverage map /// pointer to the frida coverage map
fn map_ptr_mut(&mut self) -> *mut u8; fn map_ptr_mut(&mut self) -> *mut u8;
/// Returns the mapped ranges of the target
fn ranges(&self) -> &RangeMap<usize, (u16, String)>; fn ranges(&self) -> &RangeMap<usize, (u16, String)>;
/// Returns the mapped ranges of the target, mutable
fn ranges_mut(&mut self) -> &mut RangeMap<usize, (u16, String)>; fn ranges_mut(&mut self) -> &mut RangeMap<usize, (u16, String)>;
} }
@ -98,6 +86,23 @@ pub struct FridaInstrumentationHelper<'a> {
options: &'a FridaOptions, 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", &"<ModuleMap>")
.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> { impl<'a> FridaHelper<'a> for FridaInstrumentationHelper<'a> {
fn transformer(&self) -> &Transformer<'a> { fn transformer(&self) -> &Transformer<'a> {
self.transformer.as_ref().unwrap() self.transformer.as_ref().unwrap()
@ -166,7 +171,7 @@ pub fn get_module_size(module_name: &str) -> usize {
let mut code_size = 0; let mut code_size = 0;
let code_size_ref = &mut code_size; let code_size_ref = &mut code_size;
Module::enumerate_ranges(module_name, PageProtection::ReadExecute, move |details| { 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 true
}); });
@ -467,8 +472,9 @@ impl<'a> FridaInstrumentationHelper<'a> {
Aarch64Register::from_u32(regint as u32).unwrap() Aarch64Register::from_u32(regint as u32).unwrap()
} }
// frida registers: https://docs.rs/frida-gum/0.4.0/frida_gum/instruction_writer/enum.X86Register.html /// The writer registers
// capstone registers: https://docs.rs/capstone-sys/0.14.0/capstone_sys/x86_reg/index.html /// 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>
#[cfg(all(target_arch = "x86_64", unix))] #[cfg(all(target_arch = "x86_64", unix))]
#[must_use] #[must_use]
#[inline] #[inline]

View File

@ -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. 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 /// The frida-asan allocator
#[cfg(unix)] #[cfg(unix)]
pub mod alloc; pub mod alloc;

View File

@ -162,6 +162,7 @@ pub fn init_with_asan(args: &mut Vec<String>, env: &mut [(String, String)]) -> E
Emulator::new(args, env) Emulator::new(args, env)
} }
#[derive(Debug)]
// TODO intrumentation filter // TODO intrumentation filter
pub struct QemuAsanHelper { pub struct QemuAsanHelper {
enabled: bool, enabled: bool,

View File

@ -11,7 +11,7 @@ use crate::{
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
}; };
#[derive(Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct QemuCmpsMapMetadata { pub struct QemuCmpsMapMetadata {
pub map: HashMap<u64, u64>, pub map: HashMap<u64, u64>,
pub current_id: u64, pub current_id: u64,
@ -29,6 +29,7 @@ impl QemuCmpsMapMetadata {
libafl::impl_serdeany!(QemuCmpsMapMetadata); libafl::impl_serdeany!(QemuCmpsMapMetadata);
#[derive(Debug)]
pub struct QemuCmpLogHelper { pub struct QemuCmpLogHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationFilter,
} }

View File

@ -10,7 +10,7 @@ use crate::{
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
}; };
#[derive(Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
pub struct QemuEdgesMapMetadata { pub struct QemuEdgesMapMetadata {
pub map: HashMap<(u64, u64), u64>, pub map: HashMap<(u64, u64), u64>,
pub current_id: u64, pub current_id: u64,
@ -28,6 +28,7 @@ impl QemuEdgesMapMetadata {
libafl::impl_serdeany!(QemuEdgesMapMetadata); libafl::impl_serdeany!(QemuEdgesMapMetadata);
#[derive(Debug)]
pub struct QemuEdgeCoverageHelper { pub struct QemuEdgeCoverageHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationFilter,
} }

View File

@ -293,6 +293,7 @@ impl Drop for GuestMaps {
static mut EMULATOR_IS_INITIALIZED: bool = false; static mut EMULATOR_IS_INITIALIZED: bool = false;
#[derive(Debug)]
pub struct Emulator { pub struct Emulator {
_private: (), _private: (),
} }

View File

@ -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::{ use libafl::{
corpus::Corpus, corpus::Corpus,
@ -445,6 +451,22 @@ where
inner: InProcessExecutor<'a, H, I, OT, S>, 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<I, S>,
QT: QemuHelperTuple<I, S>,
{
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> impl<'a, H, I, OT, QT, S> QemuExecutor<'a, H, I, OT, QT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,

View File

@ -1,12 +1,13 @@
use core::{fmt::Debug, ops::Range};
use libafl::{ use libafl::{
bolts::tuples::MatchFirstType, executors::ExitKind, inputs::Input, observers::ObserversTuple, bolts::tuples::MatchFirstType, executors::ExitKind, inputs::Input, observers::ObserversTuple,
}; };
use std::ops::Range;
use crate::{emu::Emulator, executor::QemuExecutor}; use crate::{emu::Emulator, executor::QemuExecutor};
/// A helper for `libafl_qemu`.
// TODO remove 'static when specialization will be stable // TODO remove 'static when specialization will be stable
pub trait QemuHelper<I, S>: 'static pub trait QemuHelper<I, S>: 'static + Debug
where where
I: Input, I: Input,
{ {
@ -23,7 +24,7 @@ where
fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {} fn post_exec(&mut self, _emulator: &Emulator, _input: &I) {}
} }
pub trait QemuHelperTuple<I, S>: MatchFirstType pub trait QemuHelperTuple<I, S>: MatchFirstType + Debug
where where
I: Input, I: Input,
{ {
@ -82,6 +83,7 @@ where
} }
} }
#[derive(Debug)]
pub enum QemuInstrumentationFilter { pub enum QemuInstrumentationFilter {
AllowList(Vec<Range<u64>>), AllowList(Vec<Range<u64>>),
DenyList(Vec<Range<u64>>), DenyList(Vec<Range<u64>>),

View File

@ -10,12 +10,14 @@ use crate::{
pub const SNAPSHOT_PAGE_SIZE: usize = 4096; pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
#[derive(Debug)]
pub struct SnapshotPageInfo { pub struct SnapshotPageInfo {
pub addr: u64, pub addr: u64,
pub dirty: bool, pub dirty: bool,
pub data: [u8; SNAPSHOT_PAGE_SIZE], 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 // TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html
pub struct QemuSnapshotHelper { pub struct QemuSnapshotHelper {
pub access_cache: [u64; 4], pub access_cache: [u64; 4],

View File

@ -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 std::{fs, net::SocketAddr, path::PathBuf, time::Duration};
use typed_builder::TypedBuilder;
use libafl::{ use libafl::{
bolts::{ bolts::{
@ -32,9 +33,11 @@ use libafl::{
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; 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; 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> { pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> {
/// Laucher configuration (default is random) /// Laucher configuration (default is random)
#[builder(default = None, setter(strip_option))] #[builder(default = None, setter(strip_option))]
@ -74,6 +77,7 @@ pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> {
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> {
/// Runs the fuzzer.
#[allow(clippy::too_many_lines, clippy::similar_names)] #[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn run(&mut self) { pub fn run(&mut self) {
let conf = match self.configuration.as_ref() { 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")] #[cfg(feature = "python")]
pub mod pybind { pub mod pybind {
use crate::forkserver; use crate::forkserver;
@ -257,6 +262,7 @@ pub mod pybind {
use pyo3::prelude::*; use pyo3::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
/// Python bindings for the `LibAFL` forkserver sugar
#[pyclass(unsendable)] #[pyclass(unsendable)]
struct ForkserverBytesCoverageSugar { struct ForkserverBytesCoverageSugar {
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
@ -267,6 +273,7 @@ pub mod pybind {
#[pymethods] #[pymethods]
impl ForkserverBytesCoverageSugar { impl ForkserverBytesCoverageSugar {
/// Create a new [`ForkserverBytesCoverageSugar`]
#[new] #[new]
fn new( fn new(
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
@ -282,6 +289,7 @@ pub mod pybind {
} }
} }
/// Run the fuzzer
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub fn run(&self, program: String, arguments: Vec<String>) { pub fn run(&self, program: String, arguments: Vec<String>) {
forkserver::ForkserverBytesCoverageSugar::<{ forkserver::DEFAULT_MAP_SIZE }>::builder() 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<()> { pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<ForkserverBytesCoverageSugar>()?; m.add_class::<ForkserverBytesCoverageSugar>()?;
Ok(()) Ok(())

View File

@ -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 std::{fs, net::SocketAddr, path::PathBuf, time::Duration};
use typed_builder::TypedBuilder;
use libafl::{ use libafl::{
bolts::{ bolts::{
@ -35,6 +38,8 @@ use libafl_targets::{CmpLogObserver, CMPLOG_MAP, EDGES_MAP, MAX_EDGES_NUM};
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
/// In-Memory fuzzing made easy.
/// Use this sugar for scaling `libfuzzer`-style fuzzers.
#[derive(TypedBuilder)] #[derive(TypedBuilder)]
pub struct InMemoryBytesCoverageSugar<'a, H> pub struct InMemoryBytesCoverageSugar<'a, H>
where where
@ -56,6 +61,7 @@ where
/// Flag if use CmpLog /// Flag if use CmpLog
#[builder(default = false)] #[builder(default = false)]
use_cmplog: bool, use_cmplog: bool,
/// The port used for communication between this fuzzer node and other fuzzer nodes
#[builder(default = 1337_u16)] #[builder(default = 1337_u16)]
broker_port: u16, broker_port: u16,
/// The list of cores to run on /// The list of cores to run on
@ -69,11 +75,39 @@ where
harness: Option<H>, harness: Option<H>,
} }
impl<H> 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() {
&"<harness_fn>"
} else {
&"None"
},
)
.finish()
}
}
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
impl<'a, H> InMemoryBytesCoverageSugar<'a, H> impl<'a, H> InMemoryBytesCoverageSugar<'a, H>
where where
H: FnMut(&[u8]), H: FnMut(&[u8]),
{ {
/// Run the fuzzer
#[allow(clippy::too_many_lines, clippy::similar_names)] #[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn run(&mut self) { pub fn run(&mut self) {
let conf = match self.configuration.as_ref() { let conf = match self.configuration.as_ref() {
@ -270,6 +304,7 @@ where
} }
} }
/// Python bindings for this sugar
#[cfg(feature = "python")] #[cfg(feature = "python")]
pub mod pybind { pub mod pybind {
use crate::inmemory; use crate::inmemory;
@ -278,6 +313,8 @@ pub mod pybind {
use pyo3::types::PyBytes; use pyo3::types::PyBytes;
use std::path::PathBuf; use std::path::PathBuf;
/// In-Memory fuzzing made easy.
/// Use this sugar for scaling `libfuzzer`-style fuzzers.
#[pyclass(unsendable)] #[pyclass(unsendable)]
struct InMemoryBytesCoverageSugar { struct InMemoryBytesCoverageSugar {
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
@ -288,6 +325,7 @@ pub mod pybind {
#[pymethods] #[pymethods]
impl InMemoryBytesCoverageSugar { impl InMemoryBytesCoverageSugar {
/// Create a new [`InMemoryBytesCoverageSugar`]
#[new] #[new]
fn new( fn new(
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
@ -303,6 +341,7 @@ pub mod pybind {
} }
} }
/// Run the fuzzer
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub fn run(&self, harness: PyObject) { pub fn run(&self, harness: PyObject) {
inmemory::InMemoryBytesCoverageSugar::builder() inmemory::InMemoryBytesCoverageSugar::builder()
@ -323,6 +362,7 @@ pub mod pybind {
} }
} }
/// Register the module
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<InMemoryBytesCoverageSugar>()?; m.add_class::<InMemoryBytesCoverageSugar>()?;
Ok(()) Ok(())

View File

@ -1,5 +1,48 @@
//! Sugar API to simplify the life of the naive user of `LibAFL` //! 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 mod inmemory;
pub use inmemory::InMemoryBytesCoverageSugar; pub use inmemory::InMemoryBytesCoverageSugar;
@ -13,12 +56,16 @@ pub mod forkserver;
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub use forkserver::ForkserverBytesCoverageSugar; pub use forkserver::ForkserverBytesCoverageSugar;
/// Default timeout for a run
pub const DEFAULT_TIMEOUT_SECS: u64 = 1200; 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; pub const CORPUS_CACHE_SIZE: usize = 4096;
#[cfg(feature = "python")] #[cfg(feature = "python")]
use pyo3::prelude::*; use pyo3::prelude::*;
/// The sugar python module
#[cfg(feature = "python")] #[cfg(feature = "python")]
#[pymodule] #[pymodule]
#[pyo3(name = "libafl_sugar")] #[pyo3(name = "libafl_sugar")]

View File

@ -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 std::{fs, net::SocketAddr, path::PathBuf, time::Duration};
use typed_builder::TypedBuilder;
use libafl::{ use libafl::{
bolts::{ bolts::{
@ -36,6 +38,8 @@ use libafl_targets::CmpLogObserver;
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS}; use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
/// Sugar to create a `libfuzzer`-style fuzzer that uses
/// `QEMU`-based binary-only instrumentation
#[derive(TypedBuilder)] #[derive(TypedBuilder)]
pub struct QemuBytesCoverageSugar<'a, H> pub struct QemuBytesCoverageSugar<'a, H>
where where
@ -57,6 +61,8 @@ where
/// Flag if use CmpLog /// Flag if use CmpLog
#[builder(default = false)] #[builder(default = false)]
use_cmplog: bool, 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)] #[builder(default = 1337_u16)]
broker_port: u16, broker_port: u16,
/// The list of cores to run on /// The list of cores to run on
@ -70,10 +76,38 @@ where
harness: Option<H>, harness: Option<H>,
} }
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() {
&"<harness_fn>"
} else {
&"None"
},
)
.finish()
}
}
impl<'a, H> QemuBytesCoverageSugar<'a, H> impl<'a, H> QemuBytesCoverageSugar<'a, H>
where where
H: FnMut(&[u8]), H: FnMut(&[u8]),
{ {
/// Run the fuzzer
#[allow(clippy::too_many_lines, clippy::similar_names)] #[allow(clippy::too_many_lines, clippy::similar_names)]
pub fn run(&mut self, emulator: &Emulator) { pub fn run(&mut self, emulator: &Emulator) {
let conf = match self.configuration.as_ref() { let conf = match self.configuration.as_ref() {
@ -330,6 +364,7 @@ where
} }
} }
/// python bindings for this sugar
#[cfg(feature = "python")] #[cfg(feature = "python")]
pub mod pybind { pub mod pybind {
use crate::qemu; use crate::qemu;
@ -349,6 +384,7 @@ pub mod pybind {
#[pymethods] #[pymethods]
impl QemuBytesCoverageSugar { impl QemuBytesCoverageSugar {
/// Create a new [`QemuBytesCoverageSugar`]
#[new] #[new]
fn new( fn new(
input_dirs: Vec<PathBuf>, input_dirs: Vec<PathBuf>,
@ -364,6 +400,7 @@ pub mod pybind {
} }
} }
/// Run the fuzzer
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]
pub fn run(&self, emulator: &Emulator, harness: PyObject) { pub fn run(&self, emulator: &Emulator, harness: PyObject) {
qemu::QemuBytesCoverageSugar::builder() qemu::QemuBytesCoverageSugar::builder()
@ -384,6 +421,7 @@ pub mod pybind {
} }
} }
/// Register this class
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<QemuBytesCoverageSugar>()?; m.add_class::<QemuBytesCoverageSugar>()?;
Ok(()) Ok(())

View File

@ -1,5 +1,8 @@
//! `CmpLog` logs and reports back values touched during fuzzing. //! `CmpLog` logs and reports back values touched during fuzzing.
//! The values will then be used in subsequent mutations. //! The values will then be used in subsequent mutations.
//!
use core::fmt::{self, Debug, Formatter};
use libafl::{ use libafl::{
bolts::{ownedref::OwnedRefMut, tuples::Named}, 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 /// The size of a logged routine argument in bytes
pub const CMPLOG_RTN_LEN: usize = 32; 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::<CmpLogInstruction>()) pub const CMPLOG_MAP_RTN_H: usize = (CMPLOG_MAP_H * core::mem::size_of::<CmpLogInstruction>())
/ core::mem::size_of::<CmpLogRoutine>(); / core::mem::size_of::<CmpLogRoutine>();
@ -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) // void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2)
extern "C" { extern "C" {
/// Logs an instruction for feedback during fuzzing
pub fn __libafl_targets_cmplog_instructions(k: usize, shape: u8, arg1: u64, arg2: u64); 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)] #[derive(Default, Debug, Clone, Copy)]
pub struct CmpLogRoutine([u8; CMPLOG_RTN_LEN], [u8; CMPLOG_RTN_LEN]); pub struct CmpLogRoutine([u8; CMPLOG_RTN_LEN], [u8; CMPLOG_RTN_LEN]);
/// Union of cmplog operands and routines
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub union CmpLogVals { pub union CmpLogVals {
@ -55,9 +61,15 @@ pub union CmpLogVals {
routines: [[CmpLogRoutine; CMPLOG_MAP_RTN_H]; CMPLOG_MAP_W], 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. /// A struct containing the `CmpLog` metadata for a `LibAFL` run.
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct CmpLogMap { pub struct CmpLogMap {
headers: [CmpLogHeader; CMPLOG_MAP_W], headers: [CmpLogHeader; CMPLOG_MAP_W],
vals: CmpLogVals, vals: CmpLogVals,
@ -109,8 +121,8 @@ impl CmpMap for CmpLogMap {
self.vals.operands[idx][execution].1 as u32, self.vals.operands[idx][execution].1 as u32,
)), )),
8 => CmpValues::U64(( 8 => CmpValues::U64((
self.vals.operands[idx][execution].0 as u64, self.vals.operands[idx][execution].0,
self.vals.operands[idx][execution].1 as u64, self.vals.operands[idx][execution].1,
)), )),
other => panic!("Invalid CmpLog shape {}", other), 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; pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
/// A [`CmpObserver`] observer for `CmpLog` /// A [`CmpObserver`] observer for `CmpLog`
#[derive(Debug)]
pub struct CmpLogObserver<'a> { pub struct CmpLogObserver<'a> {
map: OwnedRefMut<'a, CmpLogMap>, map: OwnedRefMut<'a, CmpLogMap>,
size: Option<OwnedRefMut<'a, usize>>, size: Option<OwnedRefMut<'a, usize>>,

View File

@ -12,22 +12,26 @@ pub use __afl_area_ptr_local as EDGES_MAP;
pub static mut MAX_EDGES_NUM: usize = 0; pub static mut MAX_EDGES_NUM: usize = 0;
extern "C" { extern "C" {
/// The area pointer points to the edges map.
pub static mut __afl_area_ptr: *mut u8; pub static mut __afl_area_ptr: *mut u8;
} }
pub use __afl_area_ptr as EDGES_MAP_PTR; pub use __afl_area_ptr as EDGES_MAP_PTR;
/// The size of the map for edges.
#[no_mangle] #[no_mangle]
pub static mut __afl_map_size: usize = EDGES_MAP_SIZE; pub static mut __afl_map_size: usize = EDGES_MAP_SIZE;
pub use __afl_map_size as EDGES_MAP_PTR_SIZE; pub use __afl_map_size as EDGES_MAP_PTR_SIZE;
/// Gets the edges map from the `EDGES_MAP_PTR` raw pointer.
#[must_use] #[must_use]
pub fn edges_map_from_ptr<'a>() -> &'a mut [u8] { pub fn edges_map_from_ptr<'a>() -> &'a mut [u8] {
unsafe { 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) from_raw_parts_mut(EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE)
} }
} }
/// Gets the current maximum number of edges tracked.
#[must_use] #[must_use]
pub fn edges_max_num() -> usize { pub fn edges_max_num() -> usize {
unsafe { unsafe {

View File

@ -13,7 +13,9 @@ use std::{
/// A basic block struct /// A basic block struct
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DrCovBasicBlock { pub struct DrCovBasicBlock {
/// Start of this basic block
pub start: usize, pub start: usize,
/// End of this basic block
pub end: usize, pub end: usize,
} }
@ -26,6 +28,7 @@ struct DrCovBasicBlockEntry {
} }
/// A writer for `DrCov` files /// A writer for `DrCov` files
#[derive(Debug)]
pub struct DrCovWriter<'a> { pub struct DrCovWriter<'a> {
module_mapping: &'a RangeMap<usize, (u16, String)>, module_mapping: &'a RangeMap<usize, (u16, String)>,
} }

View File

@ -1,5 +1,50 @@
//! `libafl_targets` contains runtime code, injected in the target itself during compilation. //! `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] #[macro_use]
extern crate alloc; extern crate alloc;

View File

@ -2,6 +2,8 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use core::slice::from_raw_parts_mut; 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(); pub static mut COUNTERS_MAPS: Vec<&'static mut [u8]> = Vec::new();
/// Initialize the sancov `8-bit-counters` - usually called by `llvm`. /// Initialize the sancov `8-bit-counters` - usually called by `llvm`.

View File

@ -1,15 +1,25 @@
//! Sanitizer Coverage comparison functions
extern "C" { extern "C" {
/// Trace an 8 bit `cmp`
pub fn __sanitizer_cov_trace_cmp1(v0: u8, v1: u8); 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); 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); 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); 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); 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); 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); 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); 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); pub fn __sanitizer_cov_trace_switch(val: u64, cases: *const u64);
} }

View File

@ -39,7 +39,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
} }
#[cfg(feature = "sancov_pcguard_hitcounts")] #[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; *EDGES_MAP.get_unchecked_mut(pos) = val;
} }
} }