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:
parent
efc804fe7d
commit
af3d321213
@ -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 {
|
||||||
//...
|
//...
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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",
|
||||||
|
@ -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/" }
|
||||||
|
@ -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/" }
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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?
|
||||||
|
@ -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
|
||||||
|
@ -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>>>,
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
@ -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>,
|
||||||
{
|
{
|
||||||
|
@ -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,
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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>,
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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>(
|
||||||
|
@ -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>,
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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> {
|
||||||
|
@ -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,
|
||||||
{
|
{
|
||||||
|
@ -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>;
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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++\";
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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)]
|
||||||
|
@ -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>>,
|
||||||
|
@ -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" {
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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>,
|
||||||
|
@ -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
|
||||||
|
@ -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]
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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: (),
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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>>),
|
||||||
|
@ -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],
|
||||||
|
@ -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(())
|
||||||
|
@ -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(())
|
||||||
|
@ -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")]
|
||||||
|
@ -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(())
|
||||||
|
@ -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>>,
|
||||||
|
@ -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 {
|
||||||
|
@ -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)>,
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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`.
|
||||||
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user