Introspection (#97)

* Rework to put `ClientPerfStats` in `State` and pass that along. Still need to work on getting granular information from `Feedback` and `Observer`

* Add perf_stats feature to libafl/Cargo.toml

* Update feedbacks to have with_perf

* Remove unneeeded print statement

* cargo fmt all the things

* use local llvmint vs cpu specific asm for reading cycle counter

* Remove debug testing code

* Stats timeout to 3 seconds

* Inline smallish functions for ClientPerfStats

* Remove .libs/llvmint and have the correct conditional compilation of link_llvm_intrinsics on the perf_stats feature

* pub(crate) the NUM_FEEDBACK and NUM_STAGES consts

* Tcp Broker to Broker Communication (#66)

* initial b2b implementation

* no_std and clippy fixes

* b2b testcase added

* more correct testcases

* fixed b2b

* typo

* fixed unused warning

* clippy fixes

* fallback to systemtime on non-x86

* make clippy more strict

* small fixes

* bump 0.2.1

* readme

Co-authored-by: ctfhacker <cld251@gmail.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
Andrea Fioraldi 2021-05-07 10:04:07 +02:00 committed by GitHub
parent 33e918f256
commit ba61b39baa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 934 additions and 37 deletions

View File

@ -42,11 +42,6 @@ Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/
git clone https://github.com/AFLplusplus/LibAFL git clone https://github.com/AFLplusplus/LibAFL
``` ```
If you want to get the latest and greatest features,
```
git checkout dev
```
Build the library using Build the library using
``` ```
@ -65,7 +60,6 @@ cargo doc
cd docs && mdbook serve cd docs && mdbook serve
``` ```
We collect all example fuzzers in [`./fuzzers`](./fuzzers/). We collect all example fuzzers in [`./fuzzers`](./fuzzers/).
Be sure to read their documentation (and source), this is *the natural way to get started!* Be sure to read their documentation (and source), this is *the natural way to get started!*

View File

@ -3,16 +3,12 @@
cargo clean -p libafl cargo clean -p libafl
RUST_BACKTRACE=full cargo clippy --all --all-features --tests -- \ RUST_BACKTRACE=full cargo clippy --all --all-features --tests -- \
-D clippy::pedantic \ -D clippy::pedantic \
-W clippy::cast_sign_loss \
-W clippy::similar-names \ -W clippy::similar-names \
-W clippy::cast_ptr_alignment \
-W clippy::cast_possible_wrap \
-W clippy::unused_self \ -W clippy::unused_self \
-W clippy::too_many_lines \ -W clippy::too_many_lines \
-W clippy::option_if_let_else \ -W clippy::option_if_let_else \
-W clippy::must-use-candidate \ -W clippy::must-use-candidate \
-W clippy::if-not-else \ -W clippy::if-not-else \
-W clippy::doc-markdown \
-A clippy::type_repetition_in_bounds \ -A clippy::type_repetition_in_bounds \
-A clippy::missing-errors-doc \ -A clippy::missing-errors-doc \
-A clippy::cast-possible-truncation \ -A clippy::cast-possible-truncation \

View File

@ -20,7 +20,8 @@ which = { version = "4.0.2" }
num_cpus = "1.0" num_cpus = "1.0"
[dependencies] [dependencies]
libafl = { path = "../../libafl/" } libafl = { path = "../../libafl/", features = ["default", "introspection"] }
# libafl = { path = "../../libafl/", features = ["default"] }
libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] } libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] }
# TODO Include it only when building cc # TODO Include it only when building cc
libafl_cc = { path = "../../libafl_cc/" } libafl_cc = { path = "../../libafl_cc/" }

View File

@ -0,0 +1,63 @@
PWD=`pwd`
FUZZER_NAME="fuzzer_libpng"
all:
# Build the libpng libfuzzer library
cargo build --release
# Build the libpng harness
$(PWD)/target/release/libafl_cxx \
$(PWD)/harness.cc \
$(PWD)/libpng-1.6.37/.libs/libpng16.a \
-I$(PWD)/libpng-1.6.37/ \
-o $(FUZZER_NAME) \
-lm -lz
run: all
./$(FUZZER_NAME) &
sleep 0.2
./$(FUZZER_NAME) >/dev/null 2>/dev/null &
short_test: all
timeout 11s ./$(FUZZER_NAME) &
sleep 0.2
timeout 10s taskset -c 0 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 10s taskset -c 1 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 10s taskset -c 2 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 10s taskset -c 3 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
test: all
timeout 60s ./$(FUZZER_NAME) &
sleep 0.2
timeout 59s taskset 0x00000001 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 59s taskset 0x00000002 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 59s taskset 0x00000004 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 59s taskset 0x00000008 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000010 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000020 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000040 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000080 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000100 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000200 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000400 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000800 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00001000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00002000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00004000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00008000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00010000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00020000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00040000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00080000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00100000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00200000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00400000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00800000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x01000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x02000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x04000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x08000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x10000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x20000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x40000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x80000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &

View File

@ -6,6 +6,8 @@ In contrast to other fuzzer examples, this setup uses `fuzz_loop_for`, to occasi
While this costs performance, it can be useful for targets with memory leaks or other instabilities. While this costs performance, it can be useful for targets with memory leaks or other instabilities.
If your target is really instable, however, consider exchanging the `InProcessExecutor` for a `ForkserverExecutor` instead. If your target is really instable, however, consider exchanging the `InProcessExecutor` for a `ForkserverExecutor` instead.
It also uses the `introspection` feature, printing fuzzer stats during execution.
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example. To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
It has been tested on Linux. It has been tested on Linux.
@ -51,7 +53,7 @@ This allows you to run multiple different builds of the same fuzzer alongside, f
## Run ## Run
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus. The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently, you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus.
``` ```
./fuzzer_libpng ./fuzzer_libpng

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl" name = "libafl"
version = "0.2.0" version = "0.2.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
description = "Slot your own fuzzers together and extend their features using Rust" description = "Slot your own fuzzers together and extend their features using Rust"
documentation = "https://docs.rs/libafl" documentation = "https://docs.rs/libafl"
@ -40,10 +40,11 @@ default = ["std", "anymap_debug", "derive", "llmp_compression"]
std = [] # print, sharedmap, ... support std = [] # print, sharedmap, ... support
anymap_debug = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint. 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.
llmp_small_maps = [] # reduces initial map size for llmp
llmp_debug = ["backtrace"] # Enables debug output for LLMP
llmp_compression = [] # llmp compression using GZip
llmp_bind_public = [] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default. llmp_bind_public = [] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default.
llmp_compression = [] # llmp compression using GZip
llmp_debug = ["backtrace"] # Enables debug output for LLMP
llmp_small_maps = [] # reduces initial map size for llmp
introspection = [] # Include performance statistics of the fuzzing pipeline
[[example]] [[example]]
name = "llmp_test" name = "llmp_test"
@ -60,7 +61,7 @@ erased-serde = "0.3.12"
postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat
static_assertions = "1.1.0" static_assertions = "1.1.0"
ctor = "0.1.20" ctor = "0.1.20"
libafl_derive = { version = "0.1.0", optional = true, path = "../libafl_derive" } libafl_derive = { optional = true, path = "../libafl_derive", version = "0.2.1" }
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"] } # an easy way to debug print SerdeAnyMap
compression = { version = "0.1.5" } compression = { version = "0.1.5" }
num_enum = "0.5.1" num_enum = "0.5.1"

View File

@ -55,6 +55,8 @@ For broker2broker communication, all messages are forwarded via network sockets.
Check out the `llmp_test` example in ./examples, or build it with `cargo run --example llmp_test`. Check out the `llmp_test` example in ./examples, or build it with `cargo run --example llmp_test`.
For broker2broker communication, all messages are forwarded via network sockets.
*/ */
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};

31
libafl/src/cpu.rs Normal file
View File

@ -0,0 +1,31 @@
//! Architecture agnostic processor features
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
use crate::utils::current_nanos;
// TODO: Add more architectures, using C code, see
// https://github.com/google/benchmark/blob/master/src/cycleclock.h
// Or using llvm intrinsics (if they ever should become available in stable rust?)
/// Read a timestamp for measurements.
///
/// This function is a wrapper around different ways to get a timestamp, fast
/// In this way, an experiment only has to
/// change this implementation rather than every instead of [`cpu::read_time_counter`]
/// It is using [`rdtsc`] on `x86_64` and `x86`.
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
#[must_use]
pub fn read_time_counter() -> u64 {
unsafe { core::arch::x86_64::_rdtsc() }
}
/// Read a timestamp for measurements.
///
/// This function is a wrapper around different ways to get a timestamp, fast
/// In this way, an experiment only has to
/// change this implementation rather than every instead of [`cpu::read_time_counter`]
/// On unsupported architectures, it's falling back to normal system time, in millis.
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
pub fn read_time_counter() -> u64 {
current_nanos()
}

View File

@ -230,7 +230,7 @@ where
let client = stats.client_stats_mut_for(sender_id); let client = stats.client_stats_mut_for(sender_id);
client.update_corpus_size(*corpus_size as u64); client.update_corpus_size(*corpus_size as u64);
client.update_executions(*executions as u64, *time); client.update_executions(*executions as u64, *time);
stats.display(event.name().to_string() + " #" + &sender_id.to_string()); // stats.display(event.name().to_string() + " #" + &sender_id.to_string());
Ok(BrokerEventResult::Forward) Ok(BrokerEventResult::Forward)
} }
Event::UpdateStats { Event::UpdateStats {
@ -241,7 +241,35 @@ where
// TODO: The stats buffer should be added on client add. // TODO: The stats buffer should be added on client add.
let client = stats.client_stats_mut_for(sender_id); let client = stats.client_stats_mut_for(sender_id);
client.update_executions(*executions as u64, *time); client.update_executions(*executions as u64, *time);
if sender_id == 1 {
stats.display(event.name().to_string() + " #" + &sender_id.to_string()); stats.display(event.name().to_string() + " #" + &sender_id.to_string());
}
Ok(BrokerEventResult::Handled)
}
#[cfg(feature = "introspection")]
Event::UpdatePerfStats {
time,
executions,
introspection_stats,
phantom: _,
} => {
// TODO: The stats buffer should be added on client add.
// Get the client for the sender ID
let client = stats.client_stats_mut_for(sender_id);
// Update the normal stats for this client
client.update_executions(*executions as u64, *time);
// Update the performance stats for this client
client.update_introspection_stats(**introspection_stats);
// Display the stats via `.display` only on core #1
if sender_id == 1 {
stats.display(event.name().to_string() + " #" + &sender_id.to_string());
}
// Correctly handled the event
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
Event::Objective { objective_size } => { Event::Objective { objective_size } => {

View File

@ -17,6 +17,9 @@ use crate::{
Error, Error,
}; };
#[cfg(feature = "introspection")]
use crate::stats::ClientPerfStats;
/// The log event severity /// The log event severity
#[derive(Serialize, Deserialize, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum LogSeverity { pub enum LogSeverity {
@ -98,6 +101,20 @@ where
/// [`PhantomData`] /// [`PhantomData`]
phantom: PhantomData<I>, phantom: PhantomData<I>,
}, },
/// New stats with performance stats.
#[cfg(feature = "introspection")]
UpdatePerfStats {
/// The time of generation of the event
time: Duration,
/// The executions of this client
executions: usize,
/// Current performance statistics
introspection_stats: Box<ClientPerfStats>,
phantom: PhantomData<I>,
},
/// A new objective was found /// A new objective was found
Objective { Objective {
/// Objective corpus size /// Objective corpus size
@ -138,6 +155,13 @@ where
executions: _, executions: _,
phantom: _, phantom: _,
} => "Stats", } => "Stats",
#[cfg(feature = "introspection")]
Event::UpdatePerfStats {
time: _,
executions: _,
introspection_stats: _,
phantom: _,
} => "PerfStats",
Event::Objective { objective_size: _ } => "Objective", Event::Objective { objective_size: _ } => "Objective",
Event::Log { Event::Log {
severity_level: _, severity_level: _,

View File

@ -106,6 +106,19 @@ where
stats.display(event.name().to_string()); stats.display(event.name().to_string());
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
#[cfg(feature = "introspection")]
Event::UpdatePerfStats {
time,
executions,
introspection_stats,
phantom: _,
} => {
// TODO: The stats buffer should be added on client add.
stats.client_stats_mut()[0].update_executions(*executions as u64, *time);
stats.client_stats_mut()[0].update_introspection_stats(**introspection_stats);
stats.display(event.name().to_string());
Ok(BrokerEventResult::Handled)
}
Event::Objective { objective_size } => { Event::Objective { objective_size } => {
stats stats
.client_stats_mut_for(0) .client_stats_mut_for(0)

View File

@ -16,6 +16,9 @@ use crate::{
Error, Error,
}; };
#[cfg(feature = "introspection")]
use crate::stats::NUM_FEEDBACKS;
use core::{marker::PhantomData, time::Duration}; use core::{marker::PhantomData, time::Duration};
/// Feedbacks evaluate the observers. /// Feedbacks evaluate the observers.
@ -35,6 +38,35 @@ where
where where
OT: ObserversTuple; OT: ObserversTuple;
#[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
// Start a timer for this feedback
let start_time = crate::cpu::read_time_counter();
// Execute this feedback
let ret = self.is_interesting(input, observers, &exit_kind);
// Get the elapsed time for checking this feedback
let elapsed = crate::cpu::read_time_counter() - start_time;
// TODO: A more meaningful way to get perf for each feedback
// Add this stat to the feedback metrics
feedback_stats[feedback_index] = elapsed;
ret
}
/// Append to the testcase the generated metadata in case of a new corpus item /// Append to the testcase the generated metadata in case of a new corpus item
#[inline] #[inline]
fn append_metadata(&mut self, _testcase: &mut Testcase<I>) -> Result<(), Error> { fn append_metadata(&mut self, _testcase: &mut Testcase<I>) -> Result<(), Error> {
@ -84,6 +116,36 @@ where
Ok(a && b) Ok(a && b)
} }
#[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
// Execute this feedback
let a = self.first.is_interesting_with_perf(
input,
observers,
&exit_kind,
feedback_stats,
feedback_index,
)?;
let b = self.second.is_interesting_with_perf(
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)?;
Ok(a && b)
}
#[inline] #[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> { fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(testcase)?; self.first.append_metadata(testcase)?;
@ -162,6 +224,36 @@ where
Ok(a || b) Ok(a || b)
} }
#[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
// Execute this feedback
let a = self.first.is_interesting_with_perf(
input,
observers,
&exit_kind,
feedback_stats,
feedback_index,
)?;
let b = self.second.is_interesting_with_perf(
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)?;
Ok(a || b)
}
#[inline] #[inline]
fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> { fn append_metadata(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(testcase)?; self.first.append_metadata(testcase)?;

View File

@ -7,7 +7,7 @@ use crate::{
inputs::Input, inputs::Input,
observers::ObserversTuple, observers::ObserversTuple,
stages::StagesTuple, stages::StagesTuple,
state::HasExecutions, state::{HasClientPerfStats, HasExecutions},
utils::current_time, utils::current_time,
Error, Error,
}; };
@ -15,8 +15,8 @@ use crate::{
use alloc::string::ToString; use alloc::string::ToString;
use core::{marker::PhantomData, time::Duration}; use core::{marker::PhantomData, time::Duration};
/// Send a stats update all 6 (or more) seconds /// Send a stats update all 3 (or more) seconds
const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000); const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000);
/// Holds a set of stages /// Holds a set of stages
pub trait HasStages<CS, E, EM, I, S, ST> pub trait HasStages<CS, E, EM, I, S, ST>
@ -122,7 +122,7 @@ pub trait Fuzzer<E, EM, S, CS> {
/// Given the last time, if `stats_timeout` seconds passed, send off an info/stats/heartbeat message to the broker. /// Given the last time, if `stats_timeout` seconds passed, send off an info/stats/heartbeat message to the broker.
/// Returns the new `last` time (so the old one, unless `stats_timeout` time has passed and stats have been sent) /// Returns the new `last` time (so the old one, unless `stats_timeout` time has passed and stats have been sent)
/// Will return an Error, if the stats could not be sent. /// Will return an [`crate::Error`], if the stats could not be sent.
fn maybe_report_stats( fn maybe_report_stats(
state: &mut S, state: &mut S,
manager: &mut EM, manager: &mut EM,
@ -184,7 +184,7 @@ where
impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S, CS> for StdFuzzer<CS, ST, E, EM, I, OT, S> impl<CS, ST, E, EM, I, OT, S> Fuzzer<E, EM, S, CS> for StdFuzzer<CS, ST, E, EM, I, OT, S>
where where
CS: CorpusScheduler<I, S>, CS: CorpusScheduler<I, S>,
S: HasExecutions, S: HasExecutions + HasClientPerfStats,
ST: StagesTuple<CS, E, EM, I, S>, ST: StagesTuple<CS, E, EM, I, S>,
EM: EventManager<I, S>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT>, E: Executor<I> + HasObservers<OT>,
@ -200,7 +200,8 @@ where
) -> Result<Duration, Error> { ) -> Result<Duration, Error> {
let cur = current_time(); let cur = current_time();
if cur - last > stats_timeout { if cur - last > stats_timeout {
//println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout); // Default no introspection implmentation
#[cfg(not(feature = "introspection"))]
manager.fire( manager.fire(
state, state,
Event::UpdateStats { Event::UpdateStats {
@ -209,6 +210,27 @@ where
phantom: PhantomData, phantom: PhantomData,
}, },
)?; )?;
// If performance stats are requested, fire the `UpdatePerfStats` event
#[cfg(feature = "introspection")]
{
state
.introspection_stats_mut()
.set_current_time(crate::cpu::read_time_counter());
// Send the current stats over to the manager. This `.clone` shouldn't be
// costly as `ClientPerfStats` impls `Copy` since it only contains `u64`s
manager.fire(
state,
Event::UpdatePerfStats {
executions: *state.executions(),
time: cur,
introspection_stats: Box::new(*state.introspection_stats()),
phantom: PhantomData,
},
)?;
}
Ok(cur) Ok(cur)
} else { } else {
if cur.as_millis() % 1000 == 0 {} if cur.as_millis() % 1000 == 0 {}
@ -223,12 +245,36 @@ where
manager: &mut EM, manager: &mut EM,
scheduler: &CS, scheduler: &CS,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
// Init timer for scheduler
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
// Get the next index from the scheduler
let idx = scheduler.next(state)?; let idx = scheduler.next(state)?;
// Mark the elapsed time for the scheduler
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_scheduler_time();
// Mark the elapsed time for the scheduler
#[cfg(feature = "introspection")]
state.introspection_stats_mut().reset_stage_index();
// Execute all stages
self.stages_mut() self.stages_mut()
.perform_all(state, executor, manager, scheduler, idx)?; .perform_all(state, executor, manager, scheduler, idx)?;
// Init timer for manager
#[cfg(feature = "introspection")]
state.introspection_stats_mut().start_timer();
// Execute the manager
manager.process(state, executor, scheduler)?; manager.process(state, executor, scheduler)?;
// Mark the elapsed time for the manager
#[cfg(feature = "introspection")]
state.introspection_stats_mut().mark_manager_time();
Ok(idx) Ok(idx)
} }
} }

View File

@ -25,6 +25,7 @@ pub use libafl_derive::*;
pub mod bolts; pub mod bolts;
pub mod corpus; pub mod corpus;
pub mod cpu;
pub mod events; pub mod events;
pub mod executors; pub mod executors;
pub mod feedbacks; pub mod feedbacks;

View File

@ -13,7 +13,8 @@ pub use mutational::{MutationalStage, StdMutationalStage};
//pub use power::PowerMutationalStage; //pub use power::PowerMutationalStage;
use crate::{ use crate::{
bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input, Error, bolts::tuples::TupleList, events::EventManager, executors::Executor, inputs::Input,
state::HasClientPerfStats, Error,
}; };
/// A stage is one step in the fuzzing process. /// A stage is one step in the fuzzing process.
@ -78,6 +79,7 @@ where
EM: EventManager<I, S>, EM: EventManager<I, S>,
E: Executor<I>, E: Executor<I>,
I: Input, I: Input,
S: HasClientPerfStats,
{ {
fn perform_all( fn perform_all(
&mut self, &mut self,
@ -87,8 +89,11 @@ where
scheduler: &CS, scheduler: &CS,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Perform the current stage
self.0 self.0
.perform(state, executor, manager, scheduler, corpus_idx)?; .perform(state, executor, manager, scheduler, corpus_idx)?;
// Execute the remaining stages
self.1 self.1
.perform_all(state, executor, manager, scheduler, corpus_idx) .perform_all(state, executor, manager, scheduler, corpus_idx)
} }

View File

@ -5,14 +5,19 @@ use crate::{
events::EventManager, events::EventManager,
executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks}, executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks},
inputs::Input, inputs::Input,
mark_feature_time,
mutators::Mutator, mutators::Mutator,
observers::ObserversTuple, observers::ObserversTuple,
stages::Stage, stages::Stage,
state::{Evaluator, HasCorpus, HasRand}, start_timer,
state::{Evaluator, HasClientPerfStats, HasCorpus, HasRand},
utils::Rand, utils::Rand,
Error, Error,
}; };
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
// TODO multi mutators stage // TODO multi mutators stage
/// A Mutational stage is the stage in a fuzzing run that mutates inputs. /// A Mutational stage is the stage in a fuzzing run that mutates inputs.
@ -22,7 +27,7 @@ pub trait MutationalStage<C, CS, E, EM, I, M, OT, S>: Stage<CS, E, EM, I, S>
where where
M: Mutator<I, S>, M: Mutator<I, S>,
I: Input, I: Input,
S: HasCorpus<C, I> + Evaluator<I>, S: HasCorpus<C, I> + Evaluator<I> + HasClientPerfStats,
C: Corpus<I>, C: Corpus<I>,
EM: EventManager<I, S>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>, E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
@ -49,17 +54,27 @@ where
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let num = self.iterations(state); let num = self.iterations(state);
for i in 0..num { for i in 0..num {
start_timer!(state);
let mut input_mut = state let mut input_mut = state
.corpus() .corpus()
.get(corpus_idx)? .get(corpus_idx)?
.borrow_mut() .borrow_mut()
.load_input()? .load_input()?
.clone(); .clone();
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state);
self.mutator_mut().mutate(state, &mut input_mut, i as i32)?; self.mutator_mut().mutate(state, &mut input_mut, i as i32)?;
mark_feature_time!(state, PerfFeature::Mutate);
// Time is measured directly the `evaluate_input` function
let (_, corpus_idx) = state.evaluate_input(input_mut, executor, manager, scheduler)?; let (_, corpus_idx) = state.evaluate_input(input_mut, executor, manager, scheduler)?;
start_timer!(state);
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
mark_feature_time!(state, PerfFeature::MutatePostExec);
} }
Ok(()) Ok(())
} }
@ -93,7 +108,7 @@ impl<C, CS, E, EM, I, M, OT, R, S> MutationalStage<C, CS, E, EM, I, M, OT, S>
where where
M: Mutator<I, S>, M: Mutator<I, S>,
I: Input, I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>, S: HasCorpus<C, I> + Evaluator<I> + HasRand<R> + HasClientPerfStats,
C: Corpus<I>, C: Corpus<I>,
EM: EventManager<I, S>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>, E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,
@ -124,7 +139,7 @@ impl<C, CS, E, EM, I, M, OT, R, S> Stage<CS, E, EM, I, S>
where where
M: Mutator<I, S>, M: Mutator<I, S>,
I: Input, I: Input,
S: HasCorpus<C, I> + Evaluator<I> + HasRand<R>, S: HasCorpus<C, I> + Evaluator<I> + HasRand<R> + HasClientPerfStats,
C: Corpus<I>, C: Corpus<I>,
EM: EventManager<I, S>, EM: EventManager<I, S>,
E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>, E: Executor<I> + HasObservers<OT> + HasExecHooks<EM, I, S> + HasObserversHooks<EM, I, OT, S>,

View File

@ -18,11 +18,17 @@ use crate::{
feedbacks::Feedback, feedbacks::Feedback,
generators::Generator, generators::Generator,
inputs::Input, inputs::Input,
mark_feature_time,
observers::ObserversTuple, observers::ObserversTuple,
start_timer,
stats::ClientPerfStats,
utils::Rand, utils::Rand,
Error, Error,
}; };
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use crate::inputs::bytes::BytesInput; use crate::inputs::bytes::BytesInput;
@ -72,6 +78,15 @@ where
fn rand_mut(&mut self) -> &mut R; fn rand_mut(&mut self) -> &mut R;
} }
/// Trait for offering a [`ClientPerfStats`]
pub trait HasClientPerfStats {
/// [`ClientPerfStats`] itself
fn introspection_stats(&self) -> &ClientPerfStats;
/// Mutatable ref to [`ClientPerfStats`]
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats;
}
/// Trait for elements offering metadata /// Trait for elements offering metadata
pub trait HasMetadata { pub trait HasMetadata {
/// A map, storing all metadata /// A map, storing all metadata
@ -223,6 +238,10 @@ where
/// MaxSize testcase size for mutators that appreciate it /// MaxSize testcase size for mutators that appreciate it
max_size: usize, max_size: usize,
/// Performance statistics for this fuzzer
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats,
phantom: PhantomData<I>, phantom: PhantomData<I>,
} }
@ -646,23 +665,65 @@ where
C: Corpus<I>, C: Corpus<I>,
EM: EventManager<I, Self>, EM: EventManager<I, Self>,
{ {
start_timer!(self);
executor.pre_exec_observers(self, event_mgr, input)?; executor.pre_exec_observers(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PreExecObservers);
start_timer!(self);
executor.pre_exec(self, event_mgr, input)?; executor.pre_exec(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PreExec);
start_timer!(self);
let exit_kind = executor.run_target(input)?; let exit_kind = executor.run_target(input)?;
mark_feature_time!(self, PerfFeature::TargetExecution);
start_timer!(self);
executor.post_exec(self, event_mgr, input)?; executor.post_exec(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PostExec);
*self.executions_mut() += 1; *self.executions_mut() += 1;
start_timer!(self);
executor.post_exec_observers(self, event_mgr, input)?; executor.post_exec_observers(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PostExecObservers);
let observers = executor.observers(); let observers = executor.observers();
#[cfg(not(feature = "introspection"))]
let is_interesting = self let is_interesting = self
.feedback_mut() .feedback_mut()
.is_interesting(&input, observers, &exit_kind)?; .is_interesting(&input, observers, &exit_kind)?;
#[cfg(feature = "introspection")]
let is_interesting = {
// Init temporary feedback stats here. We can't use the typical pattern above
// since we need a `mut self` for `feedbacks_mut`, so we can't also hand a
// new `mut self` to `is_interesting_with_perf`. We use this stack
// variable to get the stats and then update the feedbacks directly
let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS];
let feedback_index = 0;
let is_interesting = self.feedback_mut().is_interesting_with_perf(
&input,
observers,
&exit_kind,
&mut feedback_stats,
feedback_index,
)?;
// Update the feedback stats
self.introspection_stats_mut()
.update_feedbacks(feedback_stats);
// Return the total fitness
is_interesting
};
start_timer!(self);
let is_solution = self let is_solution = self
.objective_mut() .objective_mut()
.is_interesting(&input, observers, &exit_kind)?; .is_interesting(&input, observers, &exit_kind)?;
mark_feature_time!(self, PerfFeature::GetObjectivesInterestingAll);
Ok((is_interesting, is_solution)) Ok((is_interesting, is_solution))
} }
@ -718,7 +779,47 @@ where
solutions, solutions,
objective, objective,
max_size: DEFAULT_MAX_SIZE, max_size: DEFAULT_MAX_SIZE,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats::new(),
phantom: PhantomData, phantom: PhantomData,
} }
} }
} }
#[cfg(feature = "introspection")]
impl<C, F, I, OF, R, SC> HasClientPerfStats for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
&self.introspection_stats
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
&mut self.introspection_stats
}
}
#[cfg(not(feature = "introspection"))]
impl<C, F, I, OF, R, SC> HasClientPerfStats for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
unimplemented!()
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
unimplemented!()
}
}

View File

@ -1,8 +1,15 @@
//! Keep stats, and dispaly them to the user. Usually used in a broker, or main node, of some sort. //! Keep stats, and dispaly them to the user. Usually used in a broker, or main node, of some sort.
use serde::{Deserialize, Serialize};
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use core::{time, time::Duration}; use core::{time, time::Duration};
#[cfg(feature = "introspection")]
use alloc::string::ToString;
#[cfg(feature = "introspection")]
use core::convert::TryInto;
use crate::utils::current_time; use crate::utils::current_time;
const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds
@ -23,6 +30,10 @@ pub struct ClientStats {
pub last_window_time: time::Duration, pub last_window_time: time::Duration,
/// The last executions per sec /// The last executions per sec
pub last_execs_per_sec: f32, pub last_execs_per_sec: f32,
/// Client performance statistics
#[cfg(feature = "introspection")]
pub introspection_stats: ClientPerfStats,
} }
impl ClientStats { impl ClientStats {
@ -78,6 +89,12 @@ impl ClientStats {
self.last_execs_per_sec * (1.0 - 1.0 / 16.0) + cur_avg * (1.0 / 16.0); self.last_execs_per_sec * (1.0 - 1.0 / 16.0) + cur_avg * (1.0 / 16.0);
self.last_execs_per_sec as u64 self.last_execs_per_sec as u64
} }
/// Update the current [`ClientPerfStats`] with the given [`ClientPerfStats`]
#[cfg(feature = "introspection")]
pub fn update_introspection_stats(&mut self, introspection_stats: ClientPerfStats) {
self.introspection_stats = introspection_stats;
}
} }
/// The stats trait keeps track of all the client's stats, and offers methods to dispaly them. /// The stats trait keeps track of all the client's stats, and offers methods to dispaly them.
@ -180,6 +197,19 @@ where
self.execs_per_sec() self.execs_per_sec()
); );
(self.print_fn)(fmt); (self.print_fn)(fmt);
// Only print perf stats if the feature is enabled
#[cfg(feature = "introspection")]
{
// Print the client performance stats. Skip the Client 0 which is the broker
for (i, client) in self.client_stats.iter().skip(1).enumerate() {
let fmt = format!("Client {:03}: {}", i + 1, client.introspection_stats);
(self.print_fn)(fmt);
}
// Separate the spacing just a bit
(self.print_fn)("\n".to_string());
}
} }
} }
@ -207,3 +237,455 @@ where
} }
} }
} }
#[macro_export]
macro_rules! start_timer {
($state:expr) => {{
// Start the timer
#[cfg(feature = "introspection")]
$state.introspection_stats_mut().start_timer();
}};
}
#[macro_export]
macro_rules! mark_feature_time {
($state:expr, $feature:expr) => {{
// Mark the elapsed time for the given feature
#[cfg(feature = "introspection")]
$state.introspection_stats_mut().mark_feature_time($feature);
}};
}
#[macro_export]
macro_rules! mark_feedback_time {
($state:expr) => {{
// Mark the elapsed time for the given feature
#[cfg(feature = "introspection")]
$state.introspection_stats_mut().mark_feedback_time();
}};
}
/// Number of stages in the fuzzer
pub(crate) const NUM_STAGES: usize = 8;
/// Number of feedback mechanisms to measure for performance
pub(crate) const NUM_FEEDBACKS: usize = 16;
/// Client performance statistics
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub struct ClientPerfStats {
/// Starting counter (in clock cycles from [`cpu::read_time_counter`])
start_time: u64,
/// Current counter in the fuzzer (in clock cycles from [`cpu::read_time_counter`]
current_time: u64,
/// Clock cycles spent in the scheduler
scheduler: u64,
/// Clock cycles spent in the manager
manager: u64,
/// Current stage index to write the next stage benchmark time
curr_stage: u8,
/// Current feedback index to write the next feedback benchmark time
curr_feedback: u8,
/// Flag to dictate this stage is in use. Used during printing to not print the empty
/// stages if they are not in use.
stages_used: [bool; NUM_STAGES],
// TODO(andrea) use an hashmap and indetify feaures using a &'static str
/// Clock cycles spent in the the various features of each stage
stages: [[u64; PerfFeature::Count as usize]; NUM_STAGES],
/// Clock cycles spent in each feedback mechanism of the fuzzer.
feedbacks: [u64; NUM_FEEDBACKS],
/// Current time set by `start_timer`
timer_start: Option<u64>,
}
/// Various features that are measured for performance
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
#[repr(u8)]
pub enum PerfFeature {
/// Getting an input from the corpus
GetInputFromCorpus = 0,
/// Mutating the input
Mutate = 1,
/// Post-Exec Mutator callback
MutatePostExec = 2,
/// Actual time spent executing the target
TargetExecution = 3,
/// Time spent in the [`pre_exec`](crate::executors::Executor::pre_exec) callback
PreExec = 4,
/// Time spent in the [`post_exec`](crate::executors::Executor::post_exec) callback
PostExec = 5,
/// Time spent in the [`pre_exec_observers`](crate::executors::Executor::pre_exec_observers) callback
PreExecObservers = 6,
/// Time spent in the [`post_exec_observers`](crate::executors::Executor::post_exec_observers) callback
PostExecObservers = 7,
/// Time spent getting the feedback from [`is_interesting`] from all feedbacks
GetFeedbackInterestingAll = 8,
/// Time spent getting the feedback from [`is_interesting`] from all objectives
GetObjectivesInterestingAll = 9,
/// Used as a counter to know how many elements are in [`PerfFeature`]. Must be the
/// last value in the enum.
Count, // !! No more values here since Count is last! !!
// !! No more values here since Count is last! !!
}
// TryFromPrimitive requires `std` so these are implemented manually
impl From<PerfFeature> for usize {
fn from(val: PerfFeature) -> usize {
match val {
PerfFeature::GetInputFromCorpus => PerfFeature::GetInputFromCorpus as usize,
PerfFeature::Mutate => PerfFeature::Mutate as usize,
PerfFeature::MutatePostExec => PerfFeature::MutatePostExec as usize,
PerfFeature::TargetExecution => PerfFeature::TargetExecution as usize,
PerfFeature::PreExec => PerfFeature::PreExec as usize,
PerfFeature::PostExec => PerfFeature::PostExec as usize,
PerfFeature::PreExecObservers => PerfFeature::PreExecObservers as usize,
PerfFeature::PostExecObservers => PerfFeature::PostExecObservers as usize,
PerfFeature::GetFeedbackInterestingAll => {
PerfFeature::GetFeedbackInterestingAll as usize
}
PerfFeature::GetObjectivesInterestingAll => {
PerfFeature::GetObjectivesInterestingAll as usize
}
PerfFeature::Count => PerfFeature::Count as usize,
}
}
}
// TryFromPrimitive requires `std` so these are implemented manually
impl From<usize> for PerfFeature {
fn from(val: usize) -> PerfFeature {
match val {
0 => PerfFeature::GetInputFromCorpus,
1 => PerfFeature::Mutate,
2 => PerfFeature::MutatePostExec,
3 => PerfFeature::TargetExecution,
4 => PerfFeature::PreExec,
5 => PerfFeature::PostExec,
6 => PerfFeature::PreExecObservers,
7 => PerfFeature::PostExecObservers,
8 => PerfFeature::GetFeedbackInterestingAll,
9 => PerfFeature::GetObjectivesInterestingAll,
_ => panic!("Unknown PerfFeature: {}", val),
}
}
}
/// Number of features we can measure for performance
#[cfg(feature = "introspection")]
const NUM_PERF_FEATURES: usize = PerfFeature::Count as usize;
#[cfg(feature = "introspection")]
impl ClientPerfStats {
/// Create a blank [`ClientPerfStats`] with the `start_time` and `current_time` with
/// the current clock counter
#[must_use]
pub fn new() -> Self {
let start_time = crate::cpu::read_time_counter();
Self {
start_time,
current_time: start_time,
scheduler: 0,
manager: 0,
curr_stage: 0,
curr_feedback: 0,
stages: [[0; NUM_PERF_FEATURES]; NUM_STAGES],
stages_used: [false; NUM_STAGES],
feedbacks: [0; NUM_FEEDBACKS],
timer_start: None,
}
}
/// Set the current time with the given time
#[inline]
pub fn set_current_time(&mut self, time: u64) {
self.current_time = time;
}
/// Start a timer with the current time counter
#[inline]
pub fn start_timer(&mut self) {
self.timer_start = Some(crate::cpu::read_time_counter());
}
/// Update the current [`ClientPerfStats`] with the given [`ClientPerfStats`]
pub fn update(&mut self, stats: ClientPerfStats) {
self.set_current_time(stats.current_time);
self.update_scheduler(stats.scheduler);
self.update_manager(stats.manager);
self.update_stages(stats.stages);
self.update_feedbacks(stats.feedbacks);
}
/// Gets the elapsed time since the internal timer started. Resets the timer when
/// finished execution.
#[inline]
fn mark_time(&mut self) -> u64 {
match self.timer_start {
None => {
// Warning message if marking time without starting the timer first
#[cfg(feature = "std")]
eprint!("Attempted to `mark_time` without starting timer first.");
// Return 0 for no time marked
0
}
Some(timer_start) => {
// Calculate the elapsed time
let elapsed = crate::cpu::read_time_counter() - timer_start;
// Reset the timer
self.timer_start = None;
// Return the elapsed time
elapsed
}
}
}
/// Update the time spent in the scheduler with the elapsed time that we have seen
#[inline]
pub fn mark_scheduler_time(&mut self) {
// Get the current elapsed time
let elapsed = self.mark_time();
// Add the time to the scheduler stat
self.update_scheduler(elapsed)
}
/// Update the time spent in the scheduler with the elapsed time that we have seen
#[inline]
pub fn mark_manager_time(&mut self) {
// Get the current elapsed time
let elapsed = self.mark_time();
// Add the time the manager stat
self.update_manager(elapsed);
}
/// Update the time spent in the given [`PerfFeature`] with the elapsed time that we have seen
#[inline]
pub fn mark_feature_time(&mut self, feature: PerfFeature) {
// Get the current elapsed time
let elapsed = self.mark_time();
// Add the time the the given feature
self.update_feature(feature, elapsed);
}
/// Update the time spent in the current [`Feedback`] with the elapsed time that we
/// have seen
pub fn mark_feedback_time(&mut self) {
// Sanity check that these stats have enough room for these benchmarks
assert!(
(self.curr_feedback as usize) < NUM_FEEDBACKS,
"Current fuzzer has more
stages than the `ClientPerfStats` supports ({}). Please update the
NUM_FEEDBACKS const value in src/stats/mod.rs and rebuild",
NUM_FEEDBACKS
);
// Get the current elapsed time
let elapsed = self.mark_time();
// Get a `usize` for the index
let index: usize = self.curr_feedback.try_into().unwrap();
// Update the current feedback's time with the given time
self.feedbacks[index] = self.feedbacks[index]
.checked_add(elapsed)
.expect("mark_feedback_time overflow");
// Increment the feedback index to the next feedback
self.curr_feedback += 1;
}
/// Add the given `time` to the `scheduler` stats
#[inline]
pub fn update_scheduler(&mut self, time: u64) {
self.scheduler = self
.scheduler
.checked_add(time)
.expect("update_scheduler overflow");
}
/// Add the given `time` to the `manager` stats
#[inline]
pub fn update_manager(&mut self, time: u64) {
self.manager = self
.manager
.checked_add(time)
.expect("update_manager overflow");
}
/// Update the total stage counter and increment the stage counter for the next stage
#[inline]
pub fn finish_stage(&mut self) {
// Increment the stage to the next index. The check is only done if this were to
// be used past the length of the `self.stages` buffer
self.curr_stage += 1;
}
/// Reset the stage index counter to zero
#[inline]
pub fn reset_stage_index(&mut self) {
self.curr_stage = 0;
}
/// Reset the feedback index counter to zero
#[inline]
pub fn reset_feedback_index(&mut self) {
self.curr_feedback = 0;
}
/// Update the time spent in the feedbacks
pub fn update_feedbacks(&mut self, feedbacks: [u64; NUM_FEEDBACKS]) {
for (feedback_index, feedback) in feedbacks.iter().enumerate() {
self.feedbacks[feedback_index] = self.feedbacks[feedback_index]
.checked_add(*feedback)
.expect("update_feedback overflow");
}
}
/// Update the time spent in the stages
pub fn update_stages(&mut self, stages: [[u64; NUM_PERF_FEATURES]; NUM_STAGES]) {
for (stage_index, features) in stages.iter().enumerate() {
for (feature_index, feature) in features.iter().enumerate() {
self.stages[stage_index][feature_index] = self.stages[stage_index][feature_index]
.checked_add(*feature)
.expect("Stage overflow");
}
}
}
/// Update the given [`PerfFeature`] with the given `time`
pub fn update_feature(&mut self, feature: PerfFeature, time: u64) {
// Sanity check that these stats have enough room for these benchmarks
assert!(
(self.curr_stage as usize) < NUM_STAGES,
"Current fuzzer has more stages
than the `ClientPerfStats` supports ({}). Please update the NUM_STAGES
const value in src/stats/mod.rs and rebuild",
NUM_STAGES
);
// Get the current stage index as `usize`
let stage_index: usize = self.curr_stage.try_into().unwrap();
// Get the index of the given feature
let feature_index: usize = feature.try_into().unwrap();
// Update the given feature
self.stages[stage_index][feature_index] = self.stages[stage_index][feature_index]
.checked_add(time)
.expect("Stage overflow");
// Set that the current stage is being used
self.stages_used[stage_index] = true;
}
}
#[cfg(feature = "introspection")]
impl core::fmt::Display for ClientPerfStats {
#[allow(clippy::cast_precision_loss)]
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
// Calculate the elapsed time from the stats
let elapsed: f64 = (self.current_time - self.start_time) as f64;
// Calculate the percentages for each benchmark
let scheduler_percent = self.scheduler as f64 / elapsed;
let manager_percent = self.manager as f64 / elapsed;
// Calculate the remaining percentage that has not been benchmarked
let mut other_percent = 1.0;
other_percent -= scheduler_percent;
other_percent -= manager_percent;
// Create the formatted string
writeln!(
f,
"Scheduler: {:4.2} | Manager: {:4.2} | Stages:",
scheduler_percent, manager_percent
)?;
// Calculate each stage
for (stage_index, features) in self.stages.iter().enumerate() {
// Make sure this stage is actually used before dumping its information
if !self.stages_used[stage_index as usize] {
continue;
}
// Write the stage header
writeln!(f, " Stage {}:", stage_index)?;
for (feature_index, feature) in features.iter().enumerate() {
// Calculate this current stage's percentage
let feature_percent = *feature as f64 / elapsed;
// Ignore this feature if it isn't used
if feature_percent == 0.0 {
continue;
}
// Update the other percent by removing this current percent
other_percent -= feature_percent;
// Get the actual feature from the feature index for printing its name
let feature: PerfFeature = feature_index.into();
// Write the percentage for this feature
writeln!(f, " {:6.4}: {:?}", feature_percent, feature)?;
}
for (feedback_index, feedback) in self.feedbacks.iter().enumerate() {
// Calculate this current stage's percentage
let feedback_percent = *feedback as f64 / elapsed;
// Ignore this feedback if it isn't used
if feedback_percent == 0.0 {
continue;
}
// Update the other percent by removing this current percent
other_percent -= feedback_percent;
// Write the percentage for this feedback
writeln!(
f,
" {:6.4}: Feedback index {}",
feedback_percent, feedback_index
)?;
}
}
write!(f, " Not Measured: {:4.2}", other_percent)?;
Ok(())
}
}
#[cfg(feature = "introspection")]
impl Default for ClientPerfStats {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_cc" name = "libafl_cc"
version = "0.1.0" version = "0.2.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "Commodity library to wrap compilers and link LibAFL" description = "Commodity library to wrap compilers and link LibAFL"
documentation = "https://docs.rs/libafl_cc" documentation = "https://docs.rs/libafl_cc"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_derive" name = "libafl_derive"
version = "0.1.0" version = "0.2.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "Derive proc-macro crate for LibAFL" description = "Derive proc-macro crate for LibAFL"
documentation = "https://docs.rs/libafl_derive" documentation = "https://docs.rs/libafl_derive"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_frida" name = "libafl_frida"
version = "0.2.0" version = "0.2.1"
authors = ["s1341 <github@shmarya.net>"] authors = ["s1341 <github@shmarya.net>"]
description = "Frida backend library for LibAFL" description = "Frida backend library for LibAFL"
documentation = "https://docs.rs/libafl_frida" documentation = "https://docs.rs/libafl_frida"
@ -14,8 +14,8 @@ edition = "2018"
cc = { version = "1.0", features = ["parallel"] } cc = { version = "1.0", features = ["parallel"] }
[dependencies] [dependencies]
libafl = { path = "../libafl", version = "0.2.0", features = ["std", "libafl_derive"] } libafl = { path = "../libafl", version = "0.2.1", features = ["std", "libafl_derive"] }
libafl_targets = { path = "../libafl_targets", version = "0.1.0" } libafl_targets = { path = "../libafl_targets", version = "0.2.1" }
nix = "0.20.0" nix = "0.20.0"
libc = "0.2.92" libc = "0.2.92"
hashbrown = "0.11" hashbrown = "0.11"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_targets" name = "libafl_targets"
version = "0.1.0" version = "0.2.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "Common code for target instrumentation that can be used combined with LibAFL" description = "Common code for target instrumentation that can be used combined with LibAFL"
documentation = "https://docs.rs/libafl_targets" documentation = "https://docs.rs/libafl_targets"