Skip to content

Commit eb06d4a

Browse files
authored
Default to use SIMD acceleration map feedbacks (#3157)
* Feature renam and clean urls * Fix features renaming * wip: working libafl_bolts simd * initial default SimdMapFeedback implementation * clippy * fix imports * clippy again * fmt * also generalize simplify_map * clippy again * fix no_std * fmt * fix import for no-std * fmt * fixes * Fix fuzzers * Fix cargo docs * better bounds * fmt * Fix fuzzer * Accidentally commit the file
1 parent dd0bcba commit eb06d4a

File tree

10 files changed

+558
-437
lines changed

10 files changed

+558
-437
lines changed

fuzzers/inprocess/libfuzzer_libmozjpeg/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use libafl::{
1111
events::{setup_restarting_mgr_std, EventConfig},
1212
executors::{inprocess::InProcessExecutor, ExitKind},
1313
feedback_or,
14-
feedbacks::{CrashFeedback, MaxMapFeedback},
14+
feedbacks::{CrashFeedback, DifferentIsNovel, MapFeedback, MaxMapFeedback},
1515
fuzzer::{Fuzzer, StdFuzzer},
1616
inputs::{BytesInput, HasTargetBytes},
1717
monitors::SimpleMonitor,
@@ -28,6 +28,7 @@ use libafl::{
2828
};
2929
use libafl_bolts::{
3030
rands::StdRand,
31+
simd::MaxReducer,
3132
tuples::{tuple_list, Merge},
3233
AsSlice,
3334
};
@@ -101,7 +102,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
101102
let mut feedback = feedback_or!(
102103
MaxMapFeedback::new(&edges_observer),
103104
MaxMapFeedback::new(&cmps_observer),
104-
MaxMapFeedback::new(&allocs_observer)
105+
MapFeedback::<_, DifferentIsNovel, _, MaxReducer>::new(&allocs_observer)
105106
);
106107

107108
// A feedback to choose if an input is a solution or not

fuzzers/inprocess/libfuzzer_libpng/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ pub extern "C" fn libafl_main() {
6262
#[cfg(not(test))]
6363
fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
6464
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
65-
66-
use libafl::feedbacks::simd::{SimdImplmentation, SimdMapFeedback};
6765
let monitor = MultiMonitor::new(|s| println!("{s}"));
6866

6967
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
@@ -95,7 +93,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
9593
// Create an observation channel to keep track of the execution time
9694
let time_observer = TimeObserver::new("time");
9795

98-
let map_feedback = SimdMapFeedback::new(MaxMapFeedback::new(&edges_observer));
96+
let map_feedback = MaxMapFeedback::new(&edges_observer);
9997
let calibration = CalibrationStage::new(&map_feedback);
10098

10199
// Feedback to rate the interestingness of an input

fuzzers/structure_aware/baby_fuzzer_multi/src/main.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use libafl::{
1111
events::SimpleEventManager,
1212
executors::{inprocess::InProcessExecutor, ExitKind},
1313
feedback_or_fast,
14-
feedbacks::{CrashFeedback, MaxMapFeedback, MinMapFeedback},
14+
feedbacks::{CrashFeedback, DifferentIsNovel, MapFeedback, MaxMapFeedback},
1515
fuzzer::{Fuzzer, StdFuzzer},
1616
inputs::{BytesInput, HasTargetBytes, MultipartInput},
1717
mutators::{havoc_mutations::havoc_mutations, scheduled::HavocScheduledMutator},
@@ -21,7 +21,9 @@ use libafl::{
2121
state::StdState,
2222
Evaluator,
2323
};
24-
use libafl_bolts::{nonnull_raw_mut, rands::StdRand, tuples::tuple_list, AsSlice};
24+
use libafl_bolts::{
25+
nonnull_raw_mut, rands::StdRand, simd::MinReducer, tuples::tuple_list, AsSlice,
26+
};
2527

2628
/// Coverage map with explicit assignments due to the lack of instrumentation
2729
static mut SIGNALS: [u8; 128] = [0; 128];
@@ -89,7 +91,7 @@ pub fn main() {
8991

9092
// Feedback to rate the interestingness of an input
9193
let signals_feedback = MaxMapFeedback::new(&signals_observer);
92-
let count_feedback = MinMapFeedback::new(&count_observer);
94+
let count_feedback = MapFeedback::<_, DifferentIsNovel, _, MinReducer>::new(&count_observer);
9395

9496
let mut feedback = feedback_or_fast!(count_feedback, signals_feedback);
9597

libafl/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ default = [
3939
"regex",
4040
"serdeany_autoreg",
4141
"libafl_bolts/xxh3",
42-
"stable_simd",
42+
"simd",
4343
]
4444
document-features = ["dep:document-features"]
4545

@@ -196,8 +196,8 @@ nautilus = [
196196
"regex",
197197
]
198198

199-
## Use the best SIMD implementation by our [benchmark](https://github.com/wtdcode/libafl_simd_bench)
200-
stable_simd = ["libafl_bolts/stable_simd"]
199+
## Use the best SIMD implementation by our benchmark
200+
simd = ["libafl_bolts/simd"]
201201

202202
[[example]]
203203
name = "tui_mock"

libafl/src/feedbacks/map.rs

Lines changed: 29 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@ use alloc::{borrow::Cow, vec::Vec};
44
use core::{
55
fmt::Debug,
66
marker::PhantomData,
7-
ops::{BitAnd, BitOr, Deref, DerefMut},
7+
ops::{Deref, DerefMut},
88
};
99

10-
#[rustversion::nightly]
11-
use libafl_bolts::simd::std_covmap_is_interesting;
10+
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
11+
use libafl_bolts::simd::vector::u8x16;
12+
#[cfg(not(feature = "simd"))]
13+
use libafl_bolts::simd::{MinReducer, OrReducer};
14+
#[cfg(feature = "simd")]
15+
use libafl_bolts::simd::{SimdMaxReducer, SimdMinReducer, SimdOrReducer, vector::u8x32};
1216
use libafl_bolts::{
13-
AsIter, AsSlice, HasRefCnt, Named,
17+
AsIter, HasRefCnt, Named,
18+
simd::{MaxReducer, NopReducer, Reducer},
1419
tuples::{Handle, Handled, MatchName, MatchNameRef},
1520
};
1621
use num_traits::PrimInt;
1722
use serde::{Deserialize, Serialize, de::DeserializeOwned};
1823

24+
#[cfg(feature = "simd")]
25+
use super::simd::SimdMapFeedback;
1926
#[cfg(feature = "track_hit_feedbacks")]
2027
use crate::feedbacks::premature_last_result_err;
2128
use crate::{
@@ -29,11 +36,27 @@ use crate::{
2936
state::HasExecutions,
3037
};
3138

39+
#[cfg(feature = "simd")]
40+
/// A [`SimdMapFeedback`] that implements the AFL algorithm using an [`SimdOrReducer`] combining the bits for the history map and the bit from (`HitcountsMapObserver`)[`crate::observers::HitcountsMapObserver`].
41+
pub type AflMapFeedback<C, O> = SimdMapFeedback<C, O, SimdOrReducer, u8x32>;
42+
#[cfg(not(feature = "simd"))]
3243
/// A [`MapFeedback`] that implements the AFL algorithm using an [`OrReducer`] combining the bits for the history map and the bit from (`HitcountsMapObserver`)[`crate::observers::HitcountsMapObserver`].
3344
pub type AflMapFeedback<C, O> = MapFeedback<C, DifferentIsNovel, O, OrReducer>;
3445

46+
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
47+
/// A [`SimdMapFeedback`] that strives to maximize the map contents.
48+
pub type MaxMapFeedback<C, O> = SimdMapFeedback<C, O, SimdMaxReducer, u8x16>;
49+
#[cfg(all(feature = "simd", not(target_arch = "x86_64")))]
50+
/// A [`SimdMapFeedback`] that strives to maximize the map contents.
51+
pub type MaxMapFeedback<C, O> = SimdMapFeedback<C, O, SimdMaxReducer, u8x32>;
52+
#[cfg(not(feature = "simd"))]
3553
/// A [`MapFeedback`] that strives to maximize the map contents.
3654
pub type MaxMapFeedback<C, O> = MapFeedback<C, DifferentIsNovel, O, MaxReducer>;
55+
56+
#[cfg(feature = "simd")]
57+
/// A [`SimdMapFeedback`] that strives to minimize the map contents.
58+
pub type MinMapFeedback<C, O> = SimdMapFeedback<C, O, SimdMinReducer, u8x32>;
59+
#[cfg(not(feature = "simd"))]
3760
/// A [`MapFeedback`] that strives to minimize the map contents.
3861
pub type MinMapFeedback<C, O> = MapFeedback<C, DifferentIsNovel, O, MinReducer>;
3962

@@ -47,79 +70,6 @@ pub type MaxMapPow2Feedback<C, O> = MapFeedback<C, NextPow2IsNovel, O, MaxReduce
4770
/// but only, if a value is either `T::one()` or `T::max_value()`.
4871
pub type MaxMapOneOrFilledFeedback<C, O> = MapFeedback<C, OneOrFilledIsNovel, O, MaxReducer>;
4972

50-
/// A `Reducer` function is used to aggregate values for the novelty search
51-
pub trait Reducer<T> {
52-
/// Reduce two values to one value, with the current [`Reducer`].
53-
fn reduce(first: T, second: T) -> T;
54-
}
55-
56-
/// A [`OrReducer`] reduces the values returning the bitwise OR with the old value
57-
#[derive(Clone, Debug)]
58-
pub struct OrReducer {}
59-
60-
impl<T> Reducer<T> for OrReducer
61-
where
62-
T: BitOr<Output = T>,
63-
{
64-
#[inline]
65-
fn reduce(history: T, new: T) -> T {
66-
history | new
67-
}
68-
}
69-
70-
/// A [`AndReducer`] reduces the values returning the bitwise AND with the old value
71-
#[derive(Clone, Debug)]
72-
pub struct AndReducer {}
73-
74-
impl<T> Reducer<T> for AndReducer
75-
where
76-
T: BitAnd<Output = T>,
77-
{
78-
#[inline]
79-
fn reduce(history: T, new: T) -> T {
80-
history & new
81-
}
82-
}
83-
84-
/// A [`NopReducer`] does nothing, and just "reduces" to the second/`new` value.
85-
#[derive(Clone, Debug)]
86-
pub struct NopReducer {}
87-
88-
impl<T> Reducer<T> for NopReducer {
89-
#[inline]
90-
fn reduce(_history: T, new: T) -> T {
91-
new
92-
}
93-
}
94-
95-
/// A [`MaxReducer`] reduces int values and returns their maximum.
96-
#[derive(Clone, Debug)]
97-
pub struct MaxReducer {}
98-
99-
impl<T> Reducer<T> for MaxReducer
100-
where
101-
T: PartialOrd,
102-
{
103-
#[inline]
104-
fn reduce(first: T, second: T) -> T {
105-
if first > second { first } else { second }
106-
}
107-
}
108-
109-
/// A [`MinReducer`] reduces int values and returns their minimum.
110-
#[derive(Clone, Debug)]
111-
pub struct MinReducer {}
112-
113-
impl<T> Reducer<T> for MinReducer
114-
where
115-
T: PartialOrd,
116-
{
117-
#[inline]
118-
fn reduce(first: T, second: T) -> T {
119-
if first < second { first } else { second }
120-
}
121-
}
122-
12373
/// A `IsNovel` function is used to discriminate if a reduced value is considered novel.
12474
pub trait IsNovel<T> {
12575
/// If a new value in the [`MapFeedback`] was found,
@@ -351,7 +301,7 @@ where
351301
#[derive(Clone, Debug)]
352302
pub struct MapFeedback<C, N, O, R> {
353303
/// New indexes observed in the last observation
354-
novelties: Option<Vec<usize>>,
304+
pub(crate) novelties: Option<Vec<usize>>,
355305
/// Name identifier of this instance
356306
name: Cow<'static, str>,
357307
/// Name identifier of the observer
@@ -360,7 +310,7 @@ pub struct MapFeedback<C, N, O, R> {
360310
stats_name: Cow<'static, str>,
361311
// The previous run's result of [`Self::is_interesting`]
362312
#[cfg(feature = "track_hit_feedbacks")]
363-
last_result: Option<bool>,
313+
pub(crate) last_result: Option<bool>,
364314
/// Phantom Data of Reducer
365315
#[expect(clippy::type_complexity)]
366316
phantom: PhantomData<fn() -> (N, O, R)>,
@@ -391,24 +341,6 @@ where
391341
R: Reducer<O::Entry>,
392342
S: HasNamedMetadata + HasExecutions,
393343
{
394-
#[rustversion::nightly]
395-
default fn is_interesting(
396-
&mut self,
397-
state: &mut S,
398-
_manager: &mut EM,
399-
_input: &I,
400-
observers: &OT,
401-
_exit_kind: &ExitKind,
402-
) -> Result<bool, Error> {
403-
let res = self.is_interesting_default(state, observers);
404-
#[cfg(feature = "track_hit_feedbacks")]
405-
{
406-
self.last_result = Some(res);
407-
}
408-
Ok(res)
409-
}
410-
411-
#[rustversion::not(nightly)]
412344
fn is_interesting(
413345
&mut self,
414346
state: &mut S,
@@ -528,28 +460,6 @@ where
528460
}
529461
}
530462

531-
/// Specialize for the common coverage map size, maximization of u8s
532-
#[rustversion::nightly]
533-
impl<C, O, EM, I, OT, S> Feedback<EM, I, OT, S> for MapFeedback<C, DifferentIsNovel, O, MaxReducer>
534-
where
535-
C: CanTrack + AsRef<O>,
536-
EM: EventFirer<I, S>,
537-
O: MapObserver<Entry = u8> + for<'a> AsSlice<'a, Entry = u8> + for<'a> AsIter<'a, Item = u8>,
538-
OT: MatchName,
539-
S: HasNamedMetadata + HasExecutions,
540-
{
541-
fn is_interesting(
542-
&mut self,
543-
state: &mut S,
544-
_manager: &mut EM,
545-
_input: &I,
546-
observers: &OT,
547-
_exit_kind: &ExitKind,
548-
) -> Result<bool, Error> {
549-
Ok(self.is_interesting_u8_simd_optimized(state, observers, std_covmap_is_interesting))
550-
}
551-
}
552-
553463
impl<C, N, O, R> Named for MapFeedback<C, N, O, R> {
554464
#[inline]
555465
fn name(&self) -> &Cow<'static, str> {
@@ -676,67 +586,6 @@ where
676586
}
677587
}
678588

679-
/// Specialize for the common coverage map size, maximization of u8s
680-
impl<C, O> MapFeedback<C, DifferentIsNovel, O, MaxReducer>
681-
where
682-
O: MapObserver<Entry = u8> + for<'a> AsSlice<'a, Entry = u8> + for<'a> AsIter<'a, Item = u8>,
683-
C: CanTrack + AsRef<O>,
684-
{
685-
#[allow(dead_code)] // this is true on stable wihout "stable_simd"
686-
pub(crate) fn is_interesting_u8_simd_optimized<S, OT, F>(
687-
&mut self,
688-
state: &mut S,
689-
observers: &OT,
690-
simd: F,
691-
) -> bool
692-
where
693-
S: HasNamedMetadata,
694-
OT: MatchName,
695-
F: FnOnce(&[u8], &[u8], bool) -> (bool, Vec<usize>),
696-
{
697-
// TODO Replace with match_name_type when stable
698-
let observer = observers.get(&self.map_ref).expect("MapObserver not found. This is likely because you entered the crash handler with the wrong executor/observer").as_ref();
699-
700-
let map_state = state
701-
.named_metadata_map_mut()
702-
.get_mut::<MapFeedbackMetadata<u8>>(&self.name)
703-
.unwrap();
704-
let size = observer.usable_count();
705-
let len = observer.len();
706-
if map_state.history_map.len() < len {
707-
map_state.history_map.resize(len, u8::default());
708-
}
709-
710-
let map = observer.as_slice();
711-
debug_assert!(map.len() >= size);
712-
713-
let history_map = map_state.history_map.as_slice();
714-
715-
// Non vector implementation for reference
716-
/*for (i, history) in history_map.iter_mut().enumerate() {
717-
let item = map[i];
718-
let reduced = MaxReducer::reduce(*history, item);
719-
if DifferentIsNovel::is_novel(*history, reduced) {
720-
*history = reduced;
721-
interesting = true;
722-
if self.novelties.is_some() {
723-
self.novelties.as_mut().unwrap().push(i);
724-
}
725-
}
726-
}*/
727-
728-
let (interesting, novelties) = simd(history_map, &map, self.novelties.is_some());
729-
if let Some(nov) = self.novelties.as_mut() {
730-
*nov = novelties;
731-
}
732-
#[cfg(feature = "track_hit_feedbacks")]
733-
{
734-
self.last_result = Some(interesting);
735-
}
736-
interesting
737-
}
738-
}
739-
740589
#[cfg(test)]
741590
mod tests {
742591
use crate::feedbacks::{AllIsNovel, IsNovel, NextPow2IsNovel};

libafl/src/feedbacks/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub mod map;
4646
pub mod nautilus;
4747
#[cfg(feature = "std")]
4848
pub mod new_hash_feedback;
49-
#[cfg(feature = "stable_simd")]
49+
#[cfg(feature = "simd")]
5050
pub mod simd;
5151
#[cfg(feature = "std")]
5252
pub mod stdio;

0 commit comments

Comments
 (0)