diff --git a/README.md b/README.md index c9b452463a7..a61ba68c6ea 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,6 @@ Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/ git clone https://github.com/AFLplusplus/LibAFL ``` -If you want to get the latest and greatest features, -``` -git checkout dev -``` - Build the library using ``` @@ -65,7 +60,6 @@ cargo doc cd docs && mdbook serve ``` - We collect all example fuzzers in [`./fuzzers`](./fuzzers/). Be sure to read their documentation (and source), this is *the natural way to get started!* diff --git a/clippy.sh b/clippy.sh index 01b7093e3ca..67a8c4709d6 100755 --- a/clippy.sh +++ b/clippy.sh @@ -3,16 +3,12 @@ cargo clean -p libafl RUST_BACKTRACE=full cargo clippy --all --all-features --tests -- \ -D clippy::pedantic \ - -W clippy::cast_sign_loss \ -W clippy::similar-names \ - -W clippy::cast_ptr_alignment \ - -W clippy::cast_possible_wrap \ -W clippy::unused_self \ -W clippy::too_many_lines \ -W clippy::option_if_let_else \ -W clippy::must-use-candidate \ -W clippy::if-not-else \ - -W clippy::doc-markdown \ -A clippy::type_repetition_in_bounds \ -A clippy::missing-errors-doc \ -A clippy::cast-possible-truncation \ diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index e6787664694..8c96bc5a4dd 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -20,7 +20,8 @@ which = { version = "4.0.2" } num_cpus = "1.0" [dependencies] -libafl = { path = "../../libafl/" } +libafl = { path = "../../libafl/", features = ["default", "introspection"] } +# libafl = { path = "../../libafl/", features = ["default"] } libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } diff --git a/fuzzers/libfuzzer_libpng/Makefile b/fuzzers/libfuzzer_libpng/Makefile new file mode 100644 index 00000000000..7d054885874 --- /dev/null +++ b/fuzzers/libfuzzer_libpng/Makefile @@ -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 & diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index 922667e8fe3..6484bf62e67 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -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. 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. 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 -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 diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 4036d210738..35a1bc0bc71 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -63,7 +63,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> } }, }; - + // Create an observation channel using the coverage map let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] }; let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges)); diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 52c8c4005be..6c6a6bf9017 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl" -version = "0.2.0" +version = "0.2.1" authors = ["Andrea Fioraldi ", "Dominik Maier "] description = "Slot your own fuzzers together and extend their features using Rust" documentation = "https://docs.rs/libafl" @@ -40,10 +40,11 @@ default = ["std", "anymap_debug", "derive", "llmp_compression"] std = [] # print, sharedmap, ... support anymap_debug = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint. 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_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]] 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 static_assertions = "1.1.0" 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 compression = { version = "0.1.5" } num_enum = "0.5.1" diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 95389a1d565..b7b7585aa86 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -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`. +For broker2broker communication, all messages are forwarded via network sockets. + */ use alloc::{string::String, vec::Vec}; diff --git a/libafl/src/cpu.rs b/libafl/src/cpu.rs new file mode 100644 index 00000000000..7575c2585af --- /dev/null +++ b/libafl/src/cpu.rs @@ -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() +} diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index c77e338954f..8147140b765 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -230,7 +230,7 @@ where let client = stats.client_stats_mut_for(sender_id); client.update_corpus_size(*corpus_size as u64); client.update_executions(*executions as u64, *time); - stats.display(event.name().to_string() + " #" + &sender_id.to_string()); + // stats.display(event.name().to_string() + " #" + &sender_id.to_string()); Ok(BrokerEventResult::Forward) } Event::UpdateStats { @@ -241,7 +241,35 @@ where // TODO: The stats buffer should be added on client add. let client = stats.client_stats_mut_for(sender_id); client.update_executions(*executions as u64, *time); - stats.display(event.name().to_string() + " #" + &sender_id.to_string()); + if sender_id == 1 { + 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) } Event::Objective { objective_size } => { diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index 883036c6656..dfabe1162db 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -17,6 +17,9 @@ use crate::{ Error, }; +#[cfg(feature = "introspection")] +use crate::stats::ClientPerfStats; + /// The log event severity #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum LogSeverity { @@ -98,6 +101,20 @@ where /// [`PhantomData`] phantom: PhantomData, }, + /// 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, + + phantom: PhantomData, + }, /// A new objective was found Objective { /// Objective corpus size @@ -138,6 +155,13 @@ where executions: _, phantom: _, } => "Stats", + #[cfg(feature = "introspection")] + Event::UpdatePerfStats { + time: _, + executions: _, + introspection_stats: _, + phantom: _, + } => "PerfStats", Event::Objective { objective_size: _ } => "Objective", Event::Log { severity_level: _, diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index db26a388654..6e702dde73e 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -106,6 +106,19 @@ where stats.display(event.name().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. + 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 } => { stats .client_stats_mut_for(0) diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index c5e2e8c0909..41633436ae2 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -16,6 +16,9 @@ use crate::{ Error, }; +#[cfg(feature = "introspection")] +use crate::stats::NUM_FEEDBACKS; + use core::{marker::PhantomData, time::Duration}; /// Feedbacks evaluate the observers. @@ -35,6 +38,35 @@ where where OT: ObserversTuple; + #[cfg(feature = "introspection")] + fn is_interesting_with_perf( + &mut self, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + 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 #[inline] fn append_metadata(&mut self, _testcase: &mut Testcase) -> Result<(), Error> { @@ -84,6 +116,36 @@ where Ok(a && b) } + #[cfg(feature = "introspection")] + fn is_interesting_with_perf( + &mut self, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + 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] fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { self.first.append_metadata(testcase)?; @@ -162,6 +224,36 @@ where Ok(a || b) } + #[cfg(feature = "introspection")] + fn is_interesting_with_perf( + &mut self, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + feedback_stats: &mut [u64; NUM_FEEDBACKS], + feedback_index: usize, + ) -> Result + 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] fn append_metadata(&mut self, testcase: &mut Testcase) -> Result<(), Error> { self.first.append_metadata(testcase)?; diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 8a5d6457855..22224df57ca 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -7,7 +7,7 @@ use crate::{ inputs::Input, observers::ObserversTuple, stages::StagesTuple, - state::HasExecutions, + state::{HasClientPerfStats, HasExecutions}, utils::current_time, Error, }; @@ -15,8 +15,8 @@ use crate::{ use alloc::string::ToString; use core::{marker::PhantomData, time::Duration}; -/// Send a stats update all 6 (or more) seconds -const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(6 * 1000); +/// Send a stats update all 3 (or more) seconds +const STATS_TIMEOUT_DEFAULT: Duration = Duration::from_millis(3 * 1000); /// Holds a set of stages pub trait HasStages @@ -122,7 +122,7 @@ pub trait Fuzzer { /// 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) - /// 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( state: &mut S, manager: &mut EM, @@ -184,7 +184,7 @@ where impl Fuzzer for StdFuzzer where CS: CorpusScheduler, - S: HasExecutions, + S: HasExecutions + HasClientPerfStats, ST: StagesTuple, EM: EventManager, E: Executor + HasObservers, @@ -200,7 +200,8 @@ where ) -> Result { let cur = current_time(); if cur - last > stats_timeout { - //println!("Fire {:?} {:?} {:?}", cur, last, stats_timeout); + // Default no introspection implmentation + #[cfg(not(feature = "introspection"))] manager.fire( state, Event::UpdateStats { @@ -209,6 +210,27 @@ where 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) } else { if cur.as_millis() % 1000 == 0 {} @@ -223,12 +245,36 @@ where manager: &mut EM, scheduler: &CS, ) -> Result { + // 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)?; + // 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() .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)?; + + // Mark the elapsed time for the manager + #[cfg(feature = "introspection")] + state.introspection_stats_mut().mark_manager_time(); + Ok(idx) } } diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index cbdd166de58..72a90befe8d 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -25,6 +25,7 @@ pub use libafl_derive::*; pub mod bolts; pub mod corpus; +pub mod cpu; pub mod events; pub mod executors; pub mod feedbacks; diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index 943ad62c5ed..e7a19378df1 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -13,7 +13,8 @@ pub use mutational::{MutationalStage, StdMutationalStage}; //pub use power::PowerMutationalStage; 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. @@ -78,6 +79,7 @@ where EM: EventManager, E: Executor, I: Input, + S: HasClientPerfStats, { fn perform_all( &mut self, @@ -87,8 +89,11 @@ where scheduler: &CS, corpus_idx: usize, ) -> Result<(), Error> { + // Perform the current stage self.0 .perform(state, executor, manager, scheduler, corpus_idx)?; + + // Execute the remaining stages self.1 .perform_all(state, executor, manager, scheduler, corpus_idx) } diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index ed9ce2f7a4a..66029821dc8 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -5,14 +5,19 @@ use crate::{ events::EventManager, executors::{Executor, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks}, inputs::Input, + mark_feature_time, mutators::Mutator, observers::ObserversTuple, stages::Stage, - state::{Evaluator, HasCorpus, HasRand}, + start_timer, + state::{Evaluator, HasClientPerfStats, HasCorpus, HasRand}, utils::Rand, Error, }; +#[cfg(feature = "introspection")] +use crate::stats::PerfFeature; + // TODO multi mutators stage /// A Mutational stage is the stage in a fuzzing run that mutates inputs. @@ -22,7 +27,7 @@ pub trait MutationalStage: Stage where M: Mutator, I: Input, - S: HasCorpus + Evaluator, + S: HasCorpus + Evaluator + HasClientPerfStats, C: Corpus, EM: EventManager, E: Executor + HasObservers + HasExecHooks + HasObserversHooks, @@ -49,17 +54,27 @@ where corpus_idx: usize, ) -> Result<(), Error> { let num = self.iterations(state); + for i in 0..num { + start_timer!(state); let mut input_mut = state .corpus() .get(corpus_idx)? .borrow_mut() .load_input()? .clone(); + mark_feature_time!(state, PerfFeature::GetInputFromCorpus); + + start_timer!(state); 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)?; + start_timer!(state); self.mutator_mut().post_exec(state, i as i32, corpus_idx)?; + mark_feature_time!(state, PerfFeature::MutatePostExec); } Ok(()) } @@ -93,7 +108,7 @@ impl MutationalStage where M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasRand, + S: HasCorpus + Evaluator + HasRand + HasClientPerfStats, C: Corpus, EM: EventManager, E: Executor + HasObservers + HasExecHooks + HasObserversHooks, @@ -124,7 +139,7 @@ impl Stage where M: Mutator, I: Input, - S: HasCorpus + Evaluator + HasRand, + S: HasCorpus + Evaluator + HasRand + HasClientPerfStats, C: Corpus, EM: EventManager, E: Executor + HasObservers + HasExecHooks + HasObserversHooks, diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 43d51c209f4..6357f184fe0 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -18,11 +18,17 @@ use crate::{ feedbacks::Feedback, generators::Generator, inputs::Input, + mark_feature_time, observers::ObserversTuple, + start_timer, + stats::ClientPerfStats, utils::Rand, Error, }; +#[cfg(feature = "introspection")] +use crate::stats::PerfFeature; + #[cfg(feature = "std")] use crate::inputs::bytes::BytesInput; @@ -72,6 +78,15 @@ where 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 pub trait HasMetadata { /// A map, storing all metadata @@ -223,6 +238,10 @@ where /// MaxSize testcase size for mutators that appreciate it max_size: usize, + /// Performance statistics for this fuzzer + #[cfg(feature = "introspection")] + introspection_stats: ClientPerfStats, + phantom: PhantomData, } @@ -646,23 +665,65 @@ where C: Corpus, EM: EventManager, { + start_timer!(self); executor.pre_exec_observers(self, event_mgr, input)?; + mark_feature_time!(self, PerfFeature::PreExecObservers); + start_timer!(self); executor.pre_exec(self, event_mgr, input)?; + mark_feature_time!(self, PerfFeature::PreExec); + + start_timer!(self); let exit_kind = executor.run_target(input)?; + mark_feature_time!(self, PerfFeature::TargetExecution); + + start_timer!(self); executor.post_exec(self, event_mgr, input)?; + mark_feature_time!(self, PerfFeature::PostExec); *self.executions_mut() += 1; + + start_timer!(self); executor.post_exec_observers(self, event_mgr, input)?; + mark_feature_time!(self, PerfFeature::PostExecObservers); let observers = executor.observers(); + #[cfg(not(feature = "introspection"))] let is_interesting = self .feedback_mut() .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 .objective_mut() .is_interesting(&input, observers, &exit_kind)?; + + mark_feature_time!(self, PerfFeature::GetObjectivesInterestingAll); + Ok((is_interesting, is_solution)) } @@ -718,7 +779,47 @@ where solutions, objective, max_size: DEFAULT_MAX_SIZE, + #[cfg(feature = "introspection")] + introspection_stats: ClientPerfStats::new(), phantom: PhantomData, } } } + +#[cfg(feature = "introspection")] +impl HasClientPerfStats for State +where + C: Corpus, + I: Input, + R: Rand, + F: Feedback, + SC: Corpus, + OF: Feedback, +{ + 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 HasClientPerfStats for State +where + C: Corpus, + I: Input, + R: Rand, + F: Feedback, + SC: Corpus, + OF: Feedback, +{ + fn introspection_stats(&self) -> &ClientPerfStats { + unimplemented!() + } + + fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats { + unimplemented!() + } +} diff --git a/libafl/src/stats/mod.rs b/libafl/src/stats/mod.rs index 9e642def65d..5c84c043ae1 100644 --- a/libafl/src/stats/mod.rs +++ b/libafl/src/stats/mod.rs @@ -1,8 +1,15 @@ //! 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 core::{time, time::Duration}; +#[cfg(feature = "introspection")] +use alloc::string::ToString; +#[cfg(feature = "introspection")] +use core::convert::TryInto; + use crate::utils::current_time; const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds @@ -23,6 +30,10 @@ pub struct ClientStats { pub last_window_time: time::Duration, /// The last executions per sec pub last_execs_per_sec: f32, + + /// Client performance statistics + #[cfg(feature = "introspection")] + pub introspection_stats: ClientPerfStats, } 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 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. @@ -180,6 +197,19 @@ where self.execs_per_sec() ); (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, +} + +/// 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 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 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() + } +} diff --git a/libafl_cc/Cargo.toml b/libafl_cc/Cargo.toml index 88cd09a9482..0e82b0fe1a3 100644 --- a/libafl_cc/Cargo.toml +++ b/libafl_cc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_cc" -version = "0.1.0" +version = "0.2.1" authors = ["Andrea Fioraldi "] description = "Commodity library to wrap compilers and link LibAFL" documentation = "https://docs.rs/libafl_cc" diff --git a/libafl_derive/Cargo.toml b/libafl_derive/Cargo.toml index d95d012ef62..d38de29c327 100644 --- a/libafl_derive/Cargo.toml +++ b/libafl_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_derive" -version = "0.1.0" +version = "0.2.1" authors = ["Andrea Fioraldi "] description = "Derive proc-macro crate for LibAFL" documentation = "https://docs.rs/libafl_derive" diff --git a/libafl_frida/Cargo.toml b/libafl_frida/Cargo.toml index 8aa72e75781..c0bfc0b3496 100644 --- a/libafl_frida/Cargo.toml +++ b/libafl_frida/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_frida" -version = "0.2.0" +version = "0.2.1" authors = ["s1341 "] description = "Frida backend library for LibAFL" documentation = "https://docs.rs/libafl_frida" @@ -14,8 +14,8 @@ edition = "2018" cc = { version = "1.0", features = ["parallel"] } [dependencies] -libafl = { path = "../libafl", version = "0.2.0", features = ["std", "libafl_derive"] } -libafl_targets = { path = "../libafl_targets", version = "0.1.0" } +libafl = { path = "../libafl", version = "0.2.1", features = ["std", "libafl_derive"] } +libafl_targets = { path = "../libafl_targets", version = "0.2.1" } nix = "0.20.0" libc = "0.2.92" hashbrown = "0.11" diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index 810df875a97..aa4b6bdb6d8 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libafl_targets" -version = "0.1.0" +version = "0.2.1" authors = ["Andrea Fioraldi "] description = "Common code for target instrumentation that can be used combined with LibAFL" documentation = "https://docs.rs/libafl_targets"