From 323b8e23ee6f8f17c70a067b6695047c4c13b3c8 Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Wed, 8 Jun 2022 17:32:58 +0200 Subject: [PATCH] LIBAFL_DEBUG_OUTPUT in Launcher and OnDiskTOMLMonitor to create fuzzer_stats (#666) * LIBAFL_DEBUG_OUTPUT in launcher on unix * OnDiskTOMLMonitor * fix * clp * clippy * fix * fix * allow all Co-authored-by: tokatoka --- fuzzers/libfuzzer_libpng_launcher/.gitignore | 3 +- fuzzers/libfuzzer_libpng_launcher/src/lib.rs | 7 +- libafl/src/bolts/launcher.rs | 12 +- libafl/src/bolts/os/unix_shmem_server.rs | 5 +- libafl/src/feedbacks/map.rs | 12 +- libafl/src/monitors/disk.rs | 136 +++++++++++++++++++ libafl/src/monitors/mod.rs | 5 + libafl/src/observers/mod.rs | 3 +- libafl/src/stages/sync.rs | 6 +- libafl_cc/src/cfg.rs | 2 +- 10 files changed, 178 insertions(+), 13 deletions(-) create mode 100644 libafl/src/monitors/disk.rs diff --git a/fuzzers/libfuzzer_libpng_launcher/.gitignore b/fuzzers/libfuzzer_libpng_launcher/.gitignore index a977a2ca5b..95debb7c8d 100644 --- a/fuzzers/libfuzzer_libpng_launcher/.gitignore +++ b/fuzzers/libfuzzer_libpng_launcher/.gitignore @@ -1 +1,2 @@ -libpng-* \ No newline at end of file +fuzzer_stats.toml +libpng-* diff --git a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs index 1c0e82ccb1..13511a7834 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs @@ -27,7 +27,7 @@ use libafl::{ feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, - monitors::tui::TuiMonitor, + monitors::{MultiMonitor, OnDiskTOMLMonitor}, mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, mutators::token_mutations::Tokens, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, @@ -139,7 +139,10 @@ pub fn libafl_main() { let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - let monitor = TuiMonitor::new("Test fuzzer on libpng".into(), true); + let monitor = OnDiskTOMLMonitor::new( + "./fuzzer_stats.toml", + MultiMonitor::new(|s| println!("{}", s)), + ); let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| { // Create an observation channel using the coverage map diff --git a/libafl/src/bolts/launcher.rs b/libafl/src/bolts/launcher.rs index 8070ff1023..14505456bb 100644 --- a/libafl/src/bolts/launcher.rs +++ b/libafl/src/bolts/launcher.rs @@ -142,6 +142,9 @@ where .stdout_file .map(|filename| File::create(filename).unwrap()); + #[cfg(feature = "std")] + let debug_output = std::env::var("LIBAFL_DEBUG_OUTPUT").is_ok(); + // Spawn clients let mut index = 0_u64; for (id, bind_to) in core_ids.iter().enumerate().take(num_cores) { @@ -163,10 +166,13 @@ where std::thread::sleep(std::time::Duration::from_millis(index * 100)); #[cfg(feature = "std")] - if let Some(file) = stdout_file { - dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?; - dup2(file.as_raw_fd(), libc::STDERR_FILENO)?; + if !debug_output { + if let Some(file) = stdout_file { + dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?; + dup2(file.as_raw_fd(), libc::STDERR_FILENO)?; + } } + // Fuzzer client. keeps retrying the connection to broker till the broker starts let (state, mgr) = RestartingMgr::::builder() .shmem_provider(self.shmem_provider.clone()) diff --git a/libafl/src/bolts/os/unix_shmem_server.rs b/libafl/src/bolts/os/unix_shmem_server.rs index 6c9d325f9b..755dfc8857 100644 --- a/libafl/src/bolts/os/unix_shmem_server.rs +++ b/libafl/src/bolts/os/unix_shmem_server.rs @@ -205,7 +205,7 @@ where fn shmem_from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result { let parts = id.as_str().split(':').collect::>(); - let server_id_str = parts.get(0).unwrap(); + let server_id_str = parts.first().unwrap(); let (server_fd, client_fd) = self.send_receive(ServedShMemRequest::ExistingMap( ShMemDescription::from_string_and_size(server_id_str, size), ))?; @@ -431,7 +431,8 @@ where // It's either running at this point, or we won't be able to spawn it anyway. env::set_var(AFL_SHMEM_SERVICE_STARTED, "true"); - match *success { + let status = *success; + match status { ShMemServiceStatus::Starting => panic!("Unreachable"), ShMemServiceStatus::Started => { println!("Started ShMem Service"); diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 47328db9e7..78a0e2bf72 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -332,6 +332,8 @@ where name: String, /// Name identifier of the observer observer_name: String, + /// Name of the feedback as shown in the `UserStats` + stats_name: String, /// Phantom Data of Reducer phantom: PhantomData<(I, N, S, R, O, T)>, } @@ -416,7 +418,7 @@ where manager.fire( state, Event::UpdateUserStats { - name: self.name.to_string(), + name: self.stats_name.to_string(), value: UserStats::Ratio(filled, size as u64), phantom: PhantomData, }, @@ -480,6 +482,10 @@ where } } +fn create_stats_name(name: &str) -> String { + name.to_lowercase() +} + impl MapFeedback where T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug, @@ -497,6 +503,7 @@ where novelties: None, name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), observer_name: map_observer.name().to_string(), + stats_name: create_stats_name(map_observer.name()), phantom: PhantomData, } } @@ -509,6 +516,7 @@ where novelties: if track_novelties { Some(vec![]) } else { None }, name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), observer_name: map_observer.name().to_string(), + stats_name: create_stats_name(map_observer.name()), phantom: PhantomData, } } @@ -521,6 +529,7 @@ where novelties: None, name: name.to_string(), observer_name: observer_name.to_string(), + stats_name: create_stats_name(name), phantom: PhantomData, } } @@ -537,6 +546,7 @@ where indexes: if track_indexes { Some(vec![]) } else { None }, novelties: if track_novelties { Some(vec![]) } else { None }, observer_name: observer_name.to_string(), + stats_name: create_stats_name(name), name: name.to_string(), phantom: PhantomData, } diff --git a/libafl/src/monitors/disk.rs b/libafl/src/monitors/disk.rs new file mode 100644 index 0000000000..c993f43f5e --- /dev/null +++ b/libafl/src/monitors/disk.rs @@ -0,0 +1,136 @@ +//! Monitors that wrap a base one and log on disk + +use alloc::{string::String, vec::Vec}; +use core::time::Duration; + +use crate::{ + bolts::{current_time, format_duration_hms}, + monitors::{ClientStats, Monitor, NopMonitor}, +}; + +use std::{fs::File, io::Write, path::PathBuf}; + +/// Wrap a monitor and log the current state of the monitor into a TOML file. +#[derive(Debug, Clone)] +pub struct OnDiskTOMLMonitor +where + M: Monitor, +{ + base: M, + filename: PathBuf, + last_update: Duration, +} + +impl Monitor for OnDiskTOMLMonitor +where + M: Monitor, +{ + /// The client monitor, mutable + fn client_stats_mut(&mut self) -> &mut Vec { + self.base.client_stats_mut() + } + + /// The client monitor + fn client_stats(&self) -> &[ClientStats] { + self.base.client_stats() + } + + /// Time this fuzzing run stated + fn start_time(&mut self) -> Duration { + self.base.start_time() + } + + fn display(&mut self, event_msg: String, sender_id: u32) { + let cur_time = current_time(); + + if (cur_time - self.last_update).as_secs() >= 60 { + self.last_update = cur_time; + + let mut file = File::create(&self.filename).expect("Failed to open the TOML file"); + write!( + &mut file, + "# This TOML is generated using the OnDiskMonitor component of LibAFL + +[global] +run_time = \"{}\" +clients = {} +corpus = {} +objectives = {} +executions = {} +exec_sec = {} +", + format_duration_hms(&(cur_time - self.start_time())), + self.client_stats().len(), + self.corpus_size(), + self.objective_size(), + self.total_execs(), + self.execs_per_sec() + ) + .expect("Failed to write to the TOML file"); + + for (i, client) in self.client_stats_mut().iter_mut().skip(1).enumerate() { + let exec_sec = client.execs_per_sec(cur_time); + + write!( + &mut file, + " +[client_{}] +corpus = {} +objectives = {} +executions = {} +exec_sec = {} +", + i + 1, + client.corpus_size, + client.objective_size, + client.executions, + exec_sec + ) + .expect("Failed to write to the TOML file"); + + for (key, val) in &client.user_monitor { + let k: String = key + .chars() + .map(|c| if c.is_whitespace() { '_' } else { c }) + .filter(|c| c.is_alphanumeric() || *c == '_') + .collect(); + writeln!(&mut file, "{} = \"{}\"", k, val) + .expect("Failed to write to the TOML file"); + } + } + + drop(file); + } + + self.base.display(event_msg, sender_id); + } +} + +impl OnDiskTOMLMonitor +where + M: Monitor, +{ + /// Create new [`OnDiskTOMLMonitor`] + #[must_use] + pub fn new

(filename: P, base: M) -> Self + where + P: Into, + { + Self { + base, + filename: filename.into(), + last_update: current_time(), + } + } +} + +impl OnDiskTOMLMonitor { + /// Create new [`OnDiskTOMLMonitor`] without a base + #[must_use] + pub fn nop

(filename: P) -> Self + where + P: Into, + { + Self::new(filename, NopMonitor::new()) + } +} diff --git a/libafl/src/monitors/mod.rs b/libafl/src/monitors/mod.rs index 2161f2310e..53c56b64d0 100644 --- a/libafl/src/monitors/mod.rs +++ b/libafl/src/monitors/mod.rs @@ -7,6 +7,11 @@ pub use multi::MultiMonitor; #[allow(missing_docs)] pub mod tui; +#[cfg(feature = "std")] +pub mod disk; +#[cfg(feature = "std")] +pub use disk::OnDiskTOMLMonitor; + use alloc::{string::String, vec::Vec}; #[cfg(feature = "introspection")] diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 65e7baab85..5296bdcbf8 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -813,9 +813,10 @@ pub mod pybind { } #[pyo3(name = "match_name")] + #[allow(clippy::all)] fn pymatch_name(&self, name: &str) -> Option { for ob in &self.list { - if ob.name() == name { + if *ob.name() == *name { return Some(ob.clone()); } } diff --git a/libafl/src/stages/sync.rs b/libafl/src/stages/sync.rs index 5484403216..9fbfa7819e 100644 --- a/libafl/src/stages/sync.rs +++ b/libafl/src/stages/sync.rs @@ -155,8 +155,10 @@ where } } -impl - SyncFromDiskStage Result, E, EM, I, S, Z> +/// Function type when the callback in `SyncFromDiskStage` is not a lambda +pub type SyncFromDiskFunction = fn(&mut Z, &mut S, &Path) -> Result; + +impl SyncFromDiskStage, E, EM, I, S, Z> where I: Input, S: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata, diff --git a/libafl_cc/src/cfg.rs b/libafl_cc/src/cfg.rs index f6d3aa50e1..af7cd5537d 100644 --- a/libafl_cc/src/cfg.rs +++ b/libafl_cc/src/cfg.rs @@ -381,7 +381,7 @@ mod tests { edge = cfg.get_edge((41864 >> 1) ^ 26911).unwrap(); assert_eq!(edge.calling_func, "main"); assert_eq!(edge.successor_edges.len(), 2); - assert_eq!(*edge.successor_edges.get(0).unwrap(), (26911 >> 1) ^ 52706); + assert_eq!(*edge.successor_edges.first().unwrap(), (26911 >> 1) ^ 52706); assert!(cfg.get_edge(26911).is_none()); assert!(cfg.get_edge(41864).is_some());