Skip to content

Commit 4a8d49e

Browse files
ThomasTNOdergoegge
andauthored
Fixes in corpus minimizer (#3403)
* Fix removing file with ctr of the form "\u{3}000" * Ensure input is loaded, also ensure exec_time is set * Clippy * Revert "Fix removing file with ctr of the form "\u{3}000"" This reverts commit 4cb3507. * libafl: fix lockfile ctr serialization (#3319) All locations reading from the lockfile expect an integer as a string, but in `InMemoryOnDiskCorpus<I>::remove_testcase` the raw bytes of the integer are written to the file in little endian byte order. This may cause "ParseIntError { kind: InvalidDigit }" in e.g. `InMemoryOnDiskCorpus<I>::save_testcase` during parsing of the file's contents. Fix this by writing the integer to the lockfile as a string in `InMemoryOnDiskCorpus<I>::remove_testcase` as well. * Extract run target with timing * Expose run_target_with_timing * Simplify * HasExecutions * clippy * linters * fmt --------- Co-authored-by: Niklas Gögge <[email protected]>
1 parent 8dd5118 commit 4a8d49e

File tree

3 files changed

+86
-77
lines changed

3 files changed

+86
-77
lines changed

crates/libafl/src/corpus/minimizer.rs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! of your corpus.
33
44
use alloc::{borrow::Cow, string::ToString, vec::Vec};
5-
use core::{hash::Hash, marker::PhantomData};
5+
use core::{hash::Hash, marker::PhantomData, time::Duration};
66

77
use hashbrown::{HashMap, HashSet};
88
use libafl_bolts::{
@@ -16,11 +16,12 @@ use crate::{
1616
Error, HasMetadata, HasScheduler,
1717
corpus::Corpus,
1818
events::{Event, EventFirer, EventWithStats, LogSeverity},
19-
executors::{Executor, HasObservers},
19+
executors::{Executor, ExitKind, HasObservers},
2020
inputs::Input,
2121
monitors::stats::{AggregatorOps, UserStats, UserStatsValue},
2222
observers::{MapObserver, ObserversTuple},
2323
schedulers::{LenTimeMulTestcaseScore, RemovableScheduler, Scheduler, TestcaseScore},
24+
stages::run_target_with_timing,
2425
state::{HasCorpus, HasExecutions},
2526
};
2627

@@ -66,7 +67,7 @@ where
6667
&self,
6768
fuzzer: &mut Z,
6869
executor: &mut E,
69-
manager: &mut EM,
70+
mgr: &mut EM,
7071
state: &mut S,
7172
) -> Result<(), Error>
7273
where
@@ -88,7 +89,7 @@ where
8889

8990
let mut cur_id = state.corpus().first();
9091

91-
manager.log(
92+
mgr.log(
9293
state,
9394
LogSeverity::Info,
9495
"Executing each input...".to_string(),
@@ -97,31 +98,41 @@ where
9798
let total = state.corpus().count() as u64;
9899
let mut curr = 0;
99100
while let Some(id) = cur_id {
100-
let (weight, input) = {
101+
let (weight, executions) = {
102+
if state.corpus().get(id)?.borrow().scheduled_count() == 0 {
103+
// Execute the input; we cannot rely on the metadata already being present.
104+
105+
let input = state
106+
.corpus()
107+
.get(id)?
108+
.borrow_mut()
109+
.load_input(state.corpus())?
110+
.clone();
111+
112+
let (exit_kind, mut total_time, _) =
113+
run_target_with_timing(fuzzer, executor, state, mgr, &input, false)?;
114+
if exit_kind != ExitKind::Ok {
115+
total_time = Duration::from_secs(1);
116+
}
117+
state
118+
.corpus()
119+
.get(id)?
120+
.borrow_mut()
121+
.set_exec_time(total_time);
122+
}
123+
101124
let mut testcase = state.corpus().get(id)?.borrow_mut();
102-
let weight = TS::compute(state, &mut *testcase)?
103-
.to_u64()
104-
.expect("Weight must be computable.");
105-
let input = testcase
106-
.input()
107-
.as_ref()
108-
.expect("Input must be available.")
109-
.clone();
110-
(weight, input)
125+
(
126+
TS::compute(state, &mut *testcase)?
127+
.to_u64()
128+
.expect("Weight must be computable."),
129+
*state.executions(),
130+
)
111131
};
112132

113-
// Execute the input; we cannot rely on the metadata already being present.
114-
executor.observers_mut().pre_exec_all(state, &input)?;
115-
let kind = executor.run_target(fuzzer, state, manager, &input)?;
116-
executor
117-
.observers_mut()
118-
.post_exec_all(state, &input, &kind)?;
119-
120-
let executions = *state.executions();
121-
122133
curr += 1;
123134

124-
manager.fire(
135+
mgr.fire(
125136
state,
126137
EventWithStats::with_current_time(
127138
Event::UpdateUserStats {
@@ -159,7 +170,7 @@ where
159170
cur_id = state.corpus().next(id);
160171
}
161172

162-
manager.log(
173+
mgr.log(
163174
state,
164175
LogSeverity::Info,
165176
"Preparing Z3 assertions...".to_string(),
@@ -186,7 +197,7 @@ where
186197
opt.assert_soft(&!seed, *weight, None);
187198
}
188199

189-
manager.log(state, LogSeverity::Info, "Performing MaxSAT...".to_string())?;
200+
mgr.log(state, LogSeverity::Info, "Performing MaxSAT...".to_string())?;
190201
// Perform the optimization!
191202
opt.check(&[]);
192203

crates/libafl/src/stages/calibrate.rs

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use num_traits::Bounded;
1313
use serde::{Deserialize, Serialize};
1414

1515
use crate::{
16-
Error, HasMetadata, HasNamedMetadata,
16+
Error, HasMetadata, HasNamedMetadata, HasScheduler,
1717
corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata},
1818
events::{Event, EventFirer, EventWithStats, LogSeverity},
1919
executors::{Executor, ExitKind, HasObservers},
@@ -77,6 +77,45 @@ impl Default for UnstableEntriesMetadata {
7777
}
7878
}
7979

80+
/// Runs the target with pre and post execution hooks and returns the exit kind and duration.
81+
pub fn run_target_with_timing<E, EM, Z, S, OT, I>(
82+
fuzzer: &mut Z,
83+
executor: &mut E,
84+
state: &mut S,
85+
mgr: &mut EM,
86+
input: &I,
87+
had_errors: bool,
88+
) -> Result<(ExitKind, Duration, bool), Error>
89+
where
90+
OT: ObserversTuple<I, S>,
91+
E: Executor<EM, I, S, Z> + HasObservers<Observers = OT>,
92+
EM: EventFirer<I, S>,
93+
I: Input,
94+
S: HasExecutions,
95+
{
96+
executor.observers_mut().pre_exec_all(state, input)?;
97+
98+
let start = current_time();
99+
let exit_kind = executor.run_target(fuzzer, state, mgr, input)?;
100+
let mut has_errors = had_errors;
101+
if exit_kind != ExitKind::Ok && !had_errors {
102+
mgr.log(
103+
state,
104+
LogSeverity::Warn,
105+
"Corpus entry errored on execution!".into(),
106+
)?;
107+
108+
has_errors = true;
109+
}
110+
let duration = current_time() - start;
111+
112+
executor
113+
.observers_mut()
114+
.post_exec_all(state, input, &exit_kind)?;
115+
116+
Ok((exit_kind, duration, has_errors))
117+
}
118+
80119
/// The calibration stage will measure the average exec time and the target's stability for this input.
81120
#[derive(Debug, Clone)]
82121
pub struct CalibrationStage<C, I, O, OT, S> {
@@ -104,7 +143,7 @@ where
104143
+ HasExecutions
105144
+ HasCurrentTestcase<I>
106145
+ HasCurrentCorpusId,
107-
Z: Evaluator<E, EM, I, S>,
146+
Z: Evaluator<E, EM, I, S> + HasScheduler<I, S>,
108147
I: Input,
109148
{
110149
#[inline]
@@ -127,30 +166,11 @@ where
127166
}
128167

129168
let mut iter = self.stage_max;
169+
130170
// If we restarted after a timeout or crash, do less iterations.
131171
let input = state.current_input_cloned()?;
132-
133-
// Run once to get the initial calibration map
134-
executor.observers_mut().pre_exec_all(state, &input)?;
135-
136-
let mut start = current_time();
137-
138-
let exit_kind = executor.run_target(fuzzer, state, mgr, &input)?;
139-
let mut total_time = if exit_kind == ExitKind::Ok {
140-
current_time() - start
141-
} else {
142-
mgr.log(
143-
state,
144-
LogSeverity::Warn,
145-
"Corpus entry errored on execution!".into(),
146-
)?;
147-
// assume one second as default time
148-
Duration::from_secs(1)
149-
};
150-
151-
executor
152-
.observers_mut()
153-
.post_exec_all(state, &input, &exit_kind)?;
172+
let (_, mut total_time, _) =
173+
run_target_with_timing(fuzzer, executor, state, mgr, &input, false)?;
154174

155175
let observers = &executor.observers();
156176
let map_first = observers[&self.map_observer_handle].as_ref();
@@ -179,33 +199,11 @@ where
179199
let mut has_errors = false;
180200

181201
while i < iter {
182-
let input = state.current_input_cloned()?;
183-
184-
executor.observers_mut().pre_exec_all(state, &input)?;
185-
start = current_time();
186-
187-
let exit_kind = executor.run_target(fuzzer, state, mgr, &input)?;
188-
if exit_kind != ExitKind::Ok {
189-
if !has_errors {
190-
mgr.log(
191-
state,
192-
LogSeverity::Warn,
193-
"Corpus entry errored on execution!".into(),
194-
)?;
195-
196-
has_errors = true;
197-
}
198-
199-
if iter < CAL_STAGE_MAX {
200-
iter += 2;
201-
}
202-
}
203-
204-
total_time += current_time() - start;
202+
let (exit_kind, duration, has_errors_result) =
203+
run_target_with_timing(fuzzer, executor, state, mgr, &input, has_errors)?;
204+
has_errors = has_errors_result;
205205

206-
executor
207-
.observers_mut()
208-
.post_exec_all(state, &input, &exit_kind)?;
206+
total_time += duration;
209207

210208
if self.track_stability && exit_kind != ExitKind::Timeout {
211209
let map = &executor.observers()[&self.map_observer_handle]

crates/libafl/src/stages/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use core::{fmt, marker::PhantomData};
1414

1515
#[cfg(feature = "std")]
1616
pub use afl_stats::{AflStatsStage, CalibrationTime, FuzzTime, SyncTime};
17-
pub use calibrate::CalibrationStage;
17+
pub use calibrate::{CalibrationStage, run_target_with_timing};
1818
pub use colorization::*;
1919
#[cfg(all(feature = "std", unix))]
2020
pub use concolic::ConcolicTracingStage;

0 commit comments

Comments
 (0)