Skip to content

Commit 04aecd9

Browse files
ToSeventoseventokatokadomenukk
authored
Add AFL-style metrics(pending,pend_fav, own_finds,imported) (#1351)
* add the metrics(pending,own_finds,imported) * add the pend_fav metrics * push * Add the feature that AFLStats is computed and reported in AFLStatsStage * fix some cicd errors * AFLStats migrates to stage/stats.rs * fix the cicd error * fix some bugs and resolve the conflicts * fix some typos --------- Co-authored-by: toseven <[email protected]> Co-authored-by: toka <[email protected]> Co-authored-by: Dominik Maier <[email protected]>
1 parent c791a23 commit 04aecd9

File tree

5 files changed

+202
-7
lines changed

5 files changed

+202
-7
lines changed

libafl/src/fuzzer/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use crate::{
2424
stages::StagesTuple,
2525
start_timer,
2626
state::{
27-
HasClientPerfMonitor, HasCorpus, HasExecutions, HasLastReportTime, HasMetadata,
28-
HasSolutions, UsesState,
27+
HasClientPerfMonitor, HasCorpus, HasExecutions, HasImported, HasLastReportTime,
28+
HasMetadata, HasSolutions, UsesState,
2929
},
3030
Error,
3131
};
@@ -333,7 +333,8 @@ where
333333
F: Feedback<CS::State>,
334334
OF: Feedback<CS::State>,
335335
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
336-
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions + HasCorpus,
336+
CS::State:
337+
HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions + HasCorpus + HasImported,
337338
{
338339
/// Evaluate if a set of observation channels has an interesting state
339340
fn process_execution<EM>(
@@ -415,6 +416,9 @@ where
415416
forward_id: None,
416417
},
417418
)?;
419+
} else {
420+
// This testcase is from the other fuzzers.
421+
*state.imported_mut() += 1;
418422
}
419423
Ok((res, Some(idx)))
420424
}
@@ -450,7 +454,7 @@ where
450454
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
451455
F: Feedback<CS::State>,
452456
OF: Feedback<CS::State>,
453-
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions,
457+
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions + HasImported,
454458
{
455459
/// Process one input, adding to the respective corpora if needed and firing the right events
456460
#[inline]
@@ -483,7 +487,7 @@ where
483487
F: Feedback<CS::State>,
484488
OF: Feedback<CS::State>,
485489
OT: ObserversTuple<CS::State> + Serialize + DeserializeOwned,
486-
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions,
490+
CS::State: HasCorpus + HasSolutions + HasClientPerfMonitor + HasExecutions + HasImported,
487491
{
488492
/// Process one input, adding to the respective corpora if needed and firing the right events
489493
#[inline]
@@ -591,6 +595,7 @@ where
591595
+ HasMetadata
592596
+ HasCorpus
593597
+ HasTestcase
598+
+ HasImported
594599
+ HasLastReportTime,
595600
ST: StagesTuple<E, EM, CS::State, Self>,
596601
{
@@ -633,11 +638,9 @@ where
633638
{
634639
let mut testcase = state.testcase_mut(idx)?;
635640
let scheduled_count = testcase.scheduled_count();
636-
637641
// increase scheduled count, this was fuzz_level in afl
638642
testcase.set_scheduled_count(scheduled_count + 1);
639643
}
640-
641644
Ok(idx)
642645
}
643646
}

libafl/src/stages/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ pub use power::{PowerMutationalStage, StdPowerMutationalStage};
2727
pub mod generalization;
2828
pub use generalization::GeneralizationStage;
2929

30+
pub mod stats;
31+
pub use stats::AflStatsStage;
32+
3033
pub mod owned;
3134
pub use owned::StagesOwnedList;
3235

libafl/src/stages/mutational.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ where
146146
post.post_exec(state, i as i32, corpus_idx)?;
147147
mark_feature_time!(state, PerfFeature::MutatePostExec);
148148
}
149+
149150
Ok(())
150151
}
151152
}

libafl/src/stages/stats.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//! Stage to compute/report AFL stats
2+
3+
#[cfg(feature = "std")]
4+
use alloc::string::ToString;
5+
use core::{marker::PhantomData, time::Duration};
6+
7+
use libafl_bolts::current_time;
8+
#[cfg(feature = "std")]
9+
use serde_json::json;
10+
11+
use crate::{
12+
corpus::{Corpus, CorpusId},
13+
events::EventFirer,
14+
schedulers::minimizer::IsFavoredMetadata,
15+
stages::Stage,
16+
state::{HasCorpus, HasImported, HasMetadata, UsesState},
17+
Error,
18+
};
19+
#[cfg(feature = "std")]
20+
use crate::{events::Event, monitors::UserStats};
21+
22+
/// The [`AflStatsStage`] is a simple stage that computes and reports some stats.
23+
#[derive(Debug, Clone)]
24+
pub struct AflStatsStage<E, EM, Z>
25+
where
26+
E: UsesState,
27+
EM: EventFirer<State = E::State>,
28+
Z: UsesState<State = E::State>,
29+
{
30+
// the number of testcases that have been fuzzed
31+
has_fuzzed_size: usize,
32+
// the number of "favored" testcases
33+
is_favored_size: usize,
34+
// the number of testcases found by itself
35+
own_finds_size: usize,
36+
// the number of testcases imported by other fuzzers
37+
imported_size: usize,
38+
// the last time that we report all stats
39+
last_report_time: Duration,
40+
// the interval that we report all stats
41+
stats_report_interval: Duration,
42+
43+
phantom: PhantomData<(E, EM, Z)>,
44+
}
45+
46+
impl<E, EM, Z> UsesState for AflStatsStage<E, EM, Z>
47+
where
48+
E: UsesState,
49+
EM: EventFirer<State = E::State>,
50+
Z: UsesState<State = E::State>,
51+
{
52+
type State = E::State;
53+
}
54+
55+
impl<E, EM, Z> Stage<E, EM, Z> for AflStatsStage<E, EM, Z>
56+
where
57+
E: UsesState,
58+
EM: EventFirer<State = E::State>,
59+
Z: UsesState<State = E::State>,
60+
E::State: HasImported + HasCorpus + HasMetadata,
61+
{
62+
fn perform(
63+
&mut self,
64+
_fuzzer: &mut Z,
65+
_executor: &mut E,
66+
state: &mut E::State,
67+
_manager: &mut EM,
68+
corpus_idx: CorpusId,
69+
) -> Result<(), Error> {
70+
// Report your stats every `STATS_REPORT_INTERVAL`
71+
// compute pending, pending_favored, imported, own_finds
72+
{
73+
let testcase = state.corpus().get(corpus_idx)?.borrow();
74+
if testcase.scheduled_count() == 0 {
75+
self.has_fuzzed_size += 1;
76+
if testcase.has_metadata::<IsFavoredMetadata>() {
77+
self.is_favored_size += 1;
78+
}
79+
} else {
80+
return Ok(());
81+
}
82+
}
83+
84+
let corpus_size = state.corpus().count();
85+
let pending_size = corpus_size - self.has_fuzzed_size;
86+
let pend_favored_size = corpus_size - self.is_favored_size;
87+
self.imported_size = *state.imported();
88+
self.own_finds_size = corpus_size - self.imported_size;
89+
90+
let cur = current_time();
91+
92+
if cur.checked_sub(self.last_report_time).unwrap_or_default() > self.stats_report_interval {
93+
#[cfg(feature = "std")]
94+
{
95+
let json = json!({
96+
"pending":pending_size,
97+
"pend_fav":pend_favored_size,
98+
"own_finds":self.own_finds_size,
99+
"imported":self.imported_size,
100+
});
101+
_manager.fire(
102+
state,
103+
Event::UpdateUserStats {
104+
name: "AflStats".to_string(),
105+
value: UserStats::String(json.to_string()),
106+
phantom: PhantomData,
107+
},
108+
)?;
109+
}
110+
#[cfg(not(feature = "std"))]
111+
log::info!(
112+
"pending: {}, pend_favored: {}, own_finds: {}, imported: {}",
113+
pending_size,
114+
pend_favored_size,
115+
self.own_finds_size,
116+
self.imported_size
117+
);
118+
self.last_report_time = cur;
119+
}
120+
121+
Ok(())
122+
}
123+
}
124+
125+
impl<E, EM, Z> AflStatsStage<E, EM, Z>
126+
where
127+
E: UsesState,
128+
EM: EventFirer<State = E::State>,
129+
Z: UsesState<State = E::State>,
130+
E::State: HasImported + HasCorpus + HasMetadata,
131+
{
132+
/// create a new instance of the [`AflStatsStage`]
133+
#[must_use]
134+
pub fn new(interval: Duration) -> Self {
135+
Self {
136+
stats_report_interval: interval,
137+
..Default::default()
138+
}
139+
}
140+
}
141+
142+
impl<E, EM, Z> Default for AflStatsStage<E, EM, Z>
143+
where
144+
E: UsesState,
145+
EM: EventFirer<State = E::State>,
146+
Z: UsesState<State = E::State>,
147+
E::State: HasImported + HasCorpus + HasMetadata,
148+
{
149+
/// the default instance of the [`AflStatsStage`]
150+
#[must_use]
151+
fn default() -> Self {
152+
Self {
153+
has_fuzzed_size: 0,
154+
is_favored_size: 0,
155+
own_finds_size: 0,
156+
imported_size: 0,
157+
last_report_time: current_time(),
158+
stats_report_interval: Duration::from_secs(15),
159+
phantom: PhantomData,
160+
}
161+
}
162+
}

libafl/src/state/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,15 @@ pub trait HasExecutions {
210210
fn executions_mut(&mut self) -> &mut usize;
211211
}
212212

213+
/// Trait for some stats of AFL
214+
pub trait HasImported {
215+
///the imported testcases counter
216+
fn imported(&self) -> &usize;
217+
218+
///the imported testcases counter (mutable)
219+
fn imported_mut(&mut self) -> &mut usize;
220+
}
221+
213222
/// Trait for the starting time
214223
pub trait HasStartTime {
215224
/// The starting time
@@ -244,6 +253,8 @@ pub struct StdState<I, C, R, SC> {
244253
executions: usize,
245254
/// At what time the fuzzing started
246255
start_time: Duration,
256+
/// the number of new paths that imported from other fuzzers
257+
imported: usize,
247258
/// The corpus
248259
corpus: C,
249260
// Solutions corpus
@@ -407,6 +418,20 @@ impl<I, C, R, SC> HasExecutions for StdState<I, C, R, SC> {
407418
}
408419
}
409420

421+
impl<I, C, R, SC> HasImported for StdState<I, C, R, SC> {
422+
/// Return the number of new paths that imported from other fuzzers
423+
#[inline]
424+
fn imported(&self) -> &usize {
425+
&self.imported
426+
}
427+
428+
/// Return the number of new paths that imported from other fuzzers
429+
#[inline]
430+
fn imported_mut(&mut self) -> &mut usize {
431+
&mut self.imported
432+
}
433+
}
434+
410435
impl<I, C, R, SC> HasLastReportTime for StdState<I, C, R, SC> {
411436
/// The last time we reported progress,if available/used.
412437
/// This information is used by fuzzer `maybe_report_progress`.
@@ -812,6 +837,7 @@ where
812837
let mut state = Self {
813838
rand,
814839
executions: 0,
840+
imported: 0,
815841
start_time: Duration::from_millis(0),
816842
metadata: SerdeAnyMap::default(),
817843
named_metadata: NamedSerdeAnyMap::default(),

0 commit comments

Comments
 (0)