From 8a7c06bc987e1ceb8a35de1c15d1f25cbd530d8b Mon Sep 17 00:00:00 2001 From: "devrata.puri" Date: Sun, 5 Jan 2025 14:35:54 +0530 Subject: [PATCH 1/6] Enhanced TuiMonitor display to include Current Testcase Index and user stats --- libafl/src/monitors/tui/mod.rs | 22 +++++++++++++++------- libafl/src/stages/afl_stats.rs | 23 ++++++++++++++++++++++- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/libafl/src/monitors/tui/mod.rs b/libafl/src/monitors/tui/mod.rs index 0e3d93f232..64801e060a 100644 --- a/libafl/src/monitors/tui/mod.rs +++ b/libafl/src/monitors/tui/mod.rs @@ -430,14 +430,14 @@ impl Monitor for TuiMonitor { #[expect(clippy::cast_sign_loss)] fn display(&mut self, event_msg: &str, sender_id: ClientId) { let cur_time = current_time(); - + { // TODO implement floating-point support for TimedStat let execsec = self.execs_per_sec() as u64; let totalexec = self.total_execs(); let run_time = cur_time - self.start_time; let total_process_timing = self.process_timing(); - + let mut ctx = self.context.write().unwrap(); ctx.total_process_timing = total_process_timing; ctx.corpus_size_timed.add(run_time, self.corpus_size()); @@ -452,11 +452,11 @@ impl Monitor for TuiMonitor { ctx.total_corpus_count = self.corpus_size(); ctx.total_item_geometry = self.item_geometry(); } - + self.client_stats_insert(sender_id); let client = self.client_stats_mut_for(sender_id); let exec_sec = client.execs_per_sec_pretty(cur_time); - + let sender = format!("#{}", sender_id.0); let pad = if event_msg.len() + sender.len() < 13 { " ".repeat(13 - event_msg.len() - sender.len()) @@ -468,13 +468,21 @@ impl Monitor for TuiMonitor { "[{}] corpus: {}, objectives: {}, executions: {}, exec/sec: {}", head, client.corpus_size, client.objective_size, client.executions, exec_sec ); + + // Display "Current Testcase Index" if available + if let Some(stat) = client.get_user_stats("Current Testcase Index") { + write!(fmt, ", Testcase Index: {}", stat.value()).unwrap(); + } else { + write!(fmt, ", Testcase Index: N/A").unwrap(); + } + for (key, val) in &client.user_monitor { write!(fmt, ", {key}: {val}").unwrap(); } for (key, val) in &self.aggregator.aggregated { write!(fmt, ", {key}: {val}").unwrap(); } - + { let client = &self.client_stats()[sender_id.0 as usize]; let mut ctx = self.context.write().unwrap(); @@ -487,7 +495,7 @@ impl Monitor for TuiMonitor { } ctx.client_logs.push_back(fmt); } - + #[cfg(feature = "introspection")] { // Print the client performance monitor. Skip the Client IDs that have never sent anything. @@ -502,7 +510,7 @@ impl Monitor for TuiMonitor { } } } - + fn aggregate(&mut self, name: &str) { self.aggregator.aggregate(name, &self.client_stats); } diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 78f78bc739..4fa11d29be 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -1,4 +1,8 @@ //! Stage to compute and report AFL++ stats +use crate::events::Event; +use crate::monitors::UserStats; +use crate::monitors::{AggregatorOps, UserStatsValue}; +use crate::state::UsesState; use alloc::{string::String, vec::Vec}; use core::{marker::PhantomData, time::Duration}; use std::{ @@ -238,7 +242,7 @@ pub struct AFLPlotData<'a> { impl Stage for AflStatsStage where E: HasObservers, - EM: EventFirer, + EM: EventFirer + UsesState, Z: HasScheduler<::Input, S>, S: HasImported + HasCorpus @@ -267,7 +271,24 @@ where "state is not currently processing a corpus index", )); }; + // Clone or copy the required data from `state` + let corpus_idx_value = corpus_idx.0; // Extract `usize` value from `CorpusId` + + // Fire the UpdateUserStats event with the corpus index + _manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("Current Testcase Index"), + value: UserStats::new( + UserStatsValue::Number(corpus_idx_value as u64), + AggregatorOps::Sum, + ), + phantom: PhantomData, + }, + )?; + let testcase = state.corpus().get(corpus_idx)?.borrow(); + // NOTE: scheduled_count represents the amount of fuzz runs a // testcase has had. Since this stage is kept at the very end of stage list, // the entry would have been fuzzed already (and should contain IsFavoredMetadata) but would have a scheduled count of zero From 0de8dcd201bfc8f45fff4b3fcd3dae407e2cf727 Mon Sep 17 00:00:00 2001 From: "devrata.puri" Date: Sun, 26 Jan 2025 20:49:26 +0530 Subject: [PATCH 2/6] added if condition in builder --- libafl/src/stages/afl_stats.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 47c601f365..d97680f1f2 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -2,7 +2,7 @@ use crate::events::Event; use crate::monitors::UserStats; use crate::monitors::{AggregatorOps, UserStatsValue}; -use crate::state::UsesState; + use alloc::{string::String, vec::Vec}; use core::{marker::PhantomData, time::Duration}; use std::{ @@ -28,9 +28,8 @@ use serde::{Deserialize, Serialize}; use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; use crate::{ corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, - events::{Event, EventFirer}, + events::EventFirer, executors::HasObservers, - monitors::{AggregatorOps, UserStats, UserStatsValue}, mutators::Tokens, observers::MapObserver, schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, @@ -119,6 +118,7 @@ pub struct AflStatsStage { /// The core we are bound to core_id: CoreId, phantom_data: PhantomData<(E, EM, I, O, S, Z)>, + update_user_stats_enabled: bool, } /// AFL++'s `fuzzer_stats` @@ -244,7 +244,7 @@ impl Stage for AflStatsStage + Named, E: HasObservers, - EM: EventFirer + UsesState, + EM: EventFirer , Z: HasScheduler, S: HasImported + HasCorpus @@ -276,7 +276,8 @@ where let corpus_idx_value = corpus_idx.0; // Extract `usize` value from `CorpusId` // Fire the UpdateUserStats event with the corpus index - _manager.fire( + if self.update_user_stats_enabled { + manager.fire( state, Event::UpdateUserStats { name: Cow::Borrowed("Current Testcase Index"), @@ -287,7 +288,7 @@ where phantom: PhantomData, }, )?; - + } let testcase = state.corpus().get(corpus_idx)?.borrow(); // NOTE: scheduled_count represents the amount of fuzz runs a @@ -679,6 +680,7 @@ pub struct AflStatsStageBuilder { version: String, target_mode: String, phantom_data: PhantomData<(E, EM, I, O, S, Z)>, + update_user_stats_enabled: bool, } impl AflStatsStageBuilder @@ -703,6 +705,7 @@ where version: String::default(), target_mode: String::default(), phantom_data: PhantomData, + update_user_stats_enabled: false, } } @@ -838,6 +841,7 @@ where dict_count: self.dict_count, core_id: self.core_id.unwrap_or(CoreId(0)), autotokens_enabled: self.uses_autotokens, + update_user_stats_enabled: self.update_user_stats_enabled, // Set field phantom_data: PhantomData, }) } From 4a004dffaf51ff7398e1d7defbbafb89c0aed75f Mon Sep 17 00:00:00 2001 From: "devrata.puri" Date: Mon, 27 Jan 2025 19:55:14 +0530 Subject: [PATCH 3/6] review point --- libafl/src/stages/afl_stats.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index d97680f1f2..426732c0b9 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -118,7 +118,7 @@ pub struct AflStatsStage { /// The core we are bound to core_id: CoreId, phantom_data: PhantomData<(E, EM, I, O, S, Z)>, - update_user_stats_enabled: bool, + report_current_corpus_idx: bool, } /// AFL++'s `fuzzer_stats` @@ -276,7 +276,7 @@ where let corpus_idx_value = corpus_idx.0; // Extract `usize` value from `CorpusId` // Fire the UpdateUserStats event with the corpus index - if self.update_user_stats_enabled { + if self.report_current_corpus_idx { manager.fire( state, Event::UpdateUserStats { @@ -680,7 +680,7 @@ pub struct AflStatsStageBuilder { version: String, target_mode: String, phantom_data: PhantomData<(E, EM, I, O, S, Z)>, - update_user_stats_enabled: bool, + report_current_corpus_idx: bool, } impl AflStatsStageBuilder @@ -705,7 +705,7 @@ where version: String::default(), target_mode: String::default(), phantom_data: PhantomData, - update_user_stats_enabled: false, + report_current_corpus_idx: false, } } @@ -841,7 +841,7 @@ where dict_count: self.dict_count, core_id: self.core_id.unwrap_or(CoreId(0)), autotokens_enabled: self.uses_autotokens, - update_user_stats_enabled: self.update_user_stats_enabled, // Set field + report_current_corpus_idx: self.report_current_corpus_idx, // Set field phantom_data: PhantomData, }) } From 4d1abfceee91e290abe81cf403f5295de249d3b1 Mon Sep 17 00:00:00 2001 From: "devrata.puri" Date: Wed, 12 Feb 2025 19:01:50 +0530 Subject: [PATCH 4/6] have runned cargo +nightly fmt --- libafl/src/monitors/tui/mod.rs | 18 ++++++++--------- libafl/src/stages/afl_stats.rs | 35 ++++++++++++++++------------------ libafl_targets/src/drcov.rs | 2 +- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/libafl/src/monitors/tui/mod.rs b/libafl/src/monitors/tui/mod.rs index 64801e060a..aeb769f3d5 100644 --- a/libafl/src/monitors/tui/mod.rs +++ b/libafl/src/monitors/tui/mod.rs @@ -430,14 +430,14 @@ impl Monitor for TuiMonitor { #[expect(clippy::cast_sign_loss)] fn display(&mut self, event_msg: &str, sender_id: ClientId) { let cur_time = current_time(); - + { // TODO implement floating-point support for TimedStat let execsec = self.execs_per_sec() as u64; let totalexec = self.total_execs(); let run_time = cur_time - self.start_time; let total_process_timing = self.process_timing(); - + let mut ctx = self.context.write().unwrap(); ctx.total_process_timing = total_process_timing; ctx.corpus_size_timed.add(run_time, self.corpus_size()); @@ -452,11 +452,11 @@ impl Monitor for TuiMonitor { ctx.total_corpus_count = self.corpus_size(); ctx.total_item_geometry = self.item_geometry(); } - + self.client_stats_insert(sender_id); let client = self.client_stats_mut_for(sender_id); let exec_sec = client.execs_per_sec_pretty(cur_time); - + let sender = format!("#{}", sender_id.0); let pad = if event_msg.len() + sender.len() < 13 { " ".repeat(13 - event_msg.len() - sender.len()) @@ -468,21 +468,21 @@ impl Monitor for TuiMonitor { "[{}] corpus: {}, objectives: {}, executions: {}, exec/sec: {}", head, client.corpus_size, client.objective_size, client.executions, exec_sec ); - + // Display "Current Testcase Index" if available if let Some(stat) = client.get_user_stats("Current Testcase Index") { write!(fmt, ", Testcase Index: {}", stat.value()).unwrap(); } else { write!(fmt, ", Testcase Index: N/A").unwrap(); } - + for (key, val) in &client.user_monitor { write!(fmt, ", {key}: {val}").unwrap(); } for (key, val) in &self.aggregator.aggregated { write!(fmt, ", {key}: {val}").unwrap(); } - + { let client = &self.client_stats()[sender_id.0 as usize]; let mut ctx = self.context.write().unwrap(); @@ -495,7 +495,7 @@ impl Monitor for TuiMonitor { } ctx.client_logs.push_back(fmt); } - + #[cfg(feature = "introspection")] { // Print the client performance monitor. Skip the Client IDs that have never sent anything. @@ -510,7 +510,7 @@ impl Monitor for TuiMonitor { } } } - + fn aggregate(&mut self, name: &str) { self.aggregator.aggregate(name, &self.client_stats); } diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 426732c0b9..539a9ac012 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -1,8 +1,4 @@ //! Stage to compute and report AFL++ stats -use crate::events::Event; -use crate::monitors::UserStats; -use crate::monitors::{AggregatorOps, UserStatsValue}; - use alloc::{string::String, vec::Vec}; use core::{marker::PhantomData, time::Duration}; use std::{ @@ -28,8 +24,9 @@ use serde::{Deserialize, Serialize}; use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME}; use crate::{ corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase}, - events::EventFirer, + events::{Event, EventFirer}, executors::HasObservers, + monitors::{AggregatorOps, UserStats, UserStatsValue}, mutators::Tokens, observers::MapObserver, schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles}, @@ -244,7 +241,7 @@ impl Stage for AflStatsStage + Named, E: HasObservers, - EM: EventFirer , + EM: EventFirer, Z: HasScheduler, S: HasImported + HasCorpus @@ -277,20 +274,20 @@ where // Fire the UpdateUserStats event with the corpus index if self.report_current_corpus_idx { - manager.fire( - state, - Event::UpdateUserStats { - name: Cow::Borrowed("Current Testcase Index"), - value: UserStats::new( - UserStatsValue::Number(corpus_idx_value as u64), - AggregatorOps::Sum, - ), - phantom: PhantomData, - }, - )?; - } + manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("Current Testcase Index"), + value: UserStats::new( + UserStatsValue::Number(corpus_idx_value as u64), + AggregatorOps::Sum, + ), + phantom: PhantomData, + }, + )?; + } let testcase = state.corpus().get(corpus_idx)?.borrow(); - + // NOTE: scheduled_count represents the amount of fuzz runs a // testcase has had. Since this stage is kept at the very end of stage list, // the entry would have been fuzzed already (and should contain IsFavoredMetadata) but would have a scheduled count of zero diff --git a/libafl_targets/src/drcov.rs b/libafl_targets/src/drcov.rs index 28056ad14d..8b9fa30221 100644 --- a/libafl_targets/src/drcov.rs +++ b/libafl_targets/src/drcov.rs @@ -255,7 +255,7 @@ fn parse_path(s: &str) -> PathBuf { let s = s.trim(); // If first and last character is a quote, let's remove them - let s = if s.starts_with('\"') && s.ends_with('\"'){ + let s = if s.starts_with('\"') && s.ends_with('\"') { &s[1..s.len() - 1] } else { s From adb68981970f42dcd5890396e378e88923262d42 Mon Sep 17 00:00:00 2001 From: "devrata.puri" Date: Wed, 12 Feb 2025 19:21:52 +0530 Subject: [PATCH 5/6] added last_sent_corpus_idx check --- libafl/src/stages/afl_stats.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 539a9ac012..d439cd312c 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -116,6 +116,7 @@ pub struct AflStatsStage { core_id: CoreId, phantom_data: PhantomData<(E, EM, I, O, S, Z)>, report_current_corpus_idx: bool, + last_sent_corpus_idx: Option, } /// AFL++'s `fuzzer_stats` @@ -274,17 +275,21 @@ where // Fire the UpdateUserStats event with the corpus index if self.report_current_corpus_idx { - manager.fire( - state, - Event::UpdateUserStats { - name: Cow::Borrowed("Current Testcase Index"), - value: UserStats::new( - UserStatsValue::Number(corpus_idx_value as u64), - AggregatorOps::Sum, - ), - phantom: PhantomData, - }, - )?; + if self.last_sent_corpus_idx != Some(corpus_idx_value) { + manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("Current Testcase Index"), + value: UserStats::new( + UserStatsValue::Number(corpus_idx_value as u64), + AggregatorOps::Sum, + ), + phantom: PhantomData, + }, + )?; + // Update the last_sent_corpus_idx to the current value + self.last_sent_corpus_idx = Some(corpus_idx_value); + } } let testcase = state.corpus().get(corpus_idx)?.borrow(); @@ -840,6 +845,7 @@ where autotokens_enabled: self.uses_autotokens, report_current_corpus_idx: self.report_current_corpus_idx, // Set field phantom_data: PhantomData, + last_sent_corpus_idx: None, }) } } From a833dd536fbd85b77e0ab20332ec3df75b7ff347 Mon Sep 17 00:00:00 2001 From: "devrata.puri" Date: Sat, 1 Mar 2025 11:59:22 +0530 Subject: [PATCH 6/6] Review points --- libafl/src/monitors/tui/mod.rs | 5 ----- libafl/src/stages/afl_stats.rs | 30 ++++++++++++++---------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/libafl/src/monitors/tui/mod.rs b/libafl/src/monitors/tui/mod.rs index 87b15384c5..0a32930b8d 100644 --- a/libafl/src/monitors/tui/mod.rs +++ b/libafl/src/monitors/tui/mod.rs @@ -393,11 +393,6 @@ impl Monitor for TuiMonitor { exec_sec ); - // Display "Current Testcase Index" if available - if let Some(stat) = client.get_user_stats("Current Testcase Index") { - write!(fmt, ", Testcase Index: {}", stat.value()).unwrap(); - } - for (key, val) in client.user_stats() { write!(fmt, ", {key}: {val}").unwrap(); } diff --git a/libafl/src/stages/afl_stats.rs b/libafl/src/stages/afl_stats.rs index 9ceff055e4..4e14d42569 100644 --- a/libafl/src/stages/afl_stats.rs +++ b/libafl/src/stages/afl_stats.rs @@ -274,22 +274,20 @@ where let corpus_idx_value = corpus_idx.0; // Extract `usize` value from `CorpusId` // Fire the UpdateUserStats event with the corpus index - if self.report_current_corpus_idx { - if self.last_sent_corpus_idx != Some(corpus_idx_value) { - manager.fire( - state, - Event::UpdateUserStats { - name: Cow::Borrowed("Current Testcase Index"), - value: UserStats::new( - UserStatsValue::Number(corpus_idx_value as u64), - AggregatorOps::Sum, - ), - phantom: PhantomData, - }, - )?; - // Update the last_sent_corpus_idx to the current value - self.last_sent_corpus_idx = Some(corpus_idx_value); - } + if self.report_current_corpus_idx && self.last_sent_corpus_idx != Some(corpus_idx_value) { + manager.fire( + state, + Event::UpdateUserStats { + name: Cow::Borrowed("Current Testcase Index"), + value: UserStats::new( + UserStatsValue::Number(corpus_idx_value as u64), + AggregatorOps::Sum, + ), + phantom: PhantomData, + }, + )?; + // Update the last_sent_corpus_idx to the current value + self.last_sent_corpus_idx = Some(corpus_idx_value); } let testcase = state.corpus().get(corpus_idx)?.borrow();