From c9f802a3b8a463557571f8351268f29f451f7bee Mon Sep 17 00:00:00 2001 From: Dongjia Zhang Date: Fri, 10 Jun 2022 17:14:12 +0900 Subject: [PATCH] Improve map feedback/observer (#665) * improve * a * fix it back * more * NO * try * fix python * more * specialize map map feedback with u8 * more * fmt * usable_count = len * clp * restore iterator based map feedback * simd specialization * optimize hitcounts * fix hitcounts * no_std * moar unsafe * fix * clippy * clippy * opt non-specialized is_interesting * fmt * op post_exec * cleanup * even more * allow Co-authored-by: Andrea Fioraldi --- fuzzers/forkserver_simple/src/main.rs | 3 +- fuzzers/forkserver_simple/src/program.c | 2 +- libafl/Cargo.toml | 2 +- libafl/src/feedbacks/map.rs | 284 +++++++++++++++++----- libafl/src/lib.rs | 4 + libafl/src/observers/map.rs | 311 ++++++++++++++++++++---- libafl/src/observers/mod.rs | 1 - 7 files changed, 492 insertions(+), 115 deletions(-) diff --git a/fuzzers/forkserver_simple/src/main.rs b/fuzzers/forkserver_simple/src/main.rs index 2e1789a2b9..9f23de321e 100644 --- a/fuzzers/forkserver_simple/src/main.rs +++ b/fuzzers/forkserver_simple/src/main.rs @@ -197,7 +197,8 @@ pub fn main() { state.add_metadata(tokens); // Setup a mutational stage with a basic bytes mutator - let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); + let mutator = + StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); fuzzer diff --git a/fuzzers/forkserver_simple/src/program.c b/fuzzers/forkserver_simple/src/program.c index 5987751523..f6c1a515b5 100644 --- a/fuzzers/forkserver_simple/src/program.c +++ b/fuzzers/forkserver_simple/src/program.c @@ -32,4 +32,4 @@ int main(int argc, char **argv) { vuln(buf); return 0; -} +} \ No newline at end of file diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index fa4183ad9c..8e04c34142 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [features] -default = ["std", "derive", "llmp_compression", "rand_trait", "fork"] +default = ["std", "derive", "llmp_compression", "rand_trait", "fork", "introspection"] std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "bincode", "wait-timeout", "regex", "byteorder", "once_cell", "uuid", "tui_monitor", "ctor", "backtrace", "num_cpus"] # print, env, launcher ... support derive = ["libafl_derive"] # provide derive(SerdeAny) macro. fork = [] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std). diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 78a0e2bf72..d733ef2cf1 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -308,7 +308,10 @@ where /// Reset the map pub fn reset(&mut self) -> Result<(), Error> { - self.history_map.iter_mut().for_each(|x| *x = T::default()); + let cnt = self.history_map.len(); + for i in 0..cnt { + self.history_map[i] = T::default(); + } Ok(()) } } @@ -353,79 +356,36 @@ where Ok(()) } - #[allow(clippy::wrong_self_convention)] - fn is_interesting( + #[rustversion::nightly] + default fn is_interesting( &mut self, state: &mut S, manager: &mut EM, - _input: &I, + input: &I, observers: &OT, - _exit_kind: &ExitKind, + exit_kind: &ExitKind, ) -> Result where EM: EventFirer, OT: ObserversTuple, { - let mut interesting = false; - // TODO Replace with match_name_type when stable - let observer = observers.match_name::(&self.observer_name).unwrap(); - let size = observer.usable_count(); - let initial = observer.initial(); + self.is_interesting_default(state, manager, input, observers, exit_kind) + } - let map_state = state - .named_metadata_mut() - .get_mut::>(&self.name) - .unwrap(); - if map_state.history_map.len() < observer.len() { - map_state.history_map.resize(observer.len(), T::default()); - } - - // assert!(size <= map_state.history_map.len(), "The size of the associated map observer cannot exceed the size of the history map of the feedback. If you are running multiple instances of slightly different fuzzers (e.g. one with ASan and another without) synchronized using LLMP please check the `configuration` field of the LLMP manager."); - - assert!(size <= observer.len()); - - if self.novelties.is_some() { - for (i, &item) in observer.as_ref_iter().enumerate() { - let history = map_state.history_map[i]; - let reduced = R::reduce(history, item); - if N::is_novel(history, reduced) { - map_state.history_map[i] = reduced; - interesting = true; - self.novelties.as_mut().unwrap().push(i); - } - } - } else { - for (i, &item) in observer.as_ref_iter().enumerate() { - let history = map_state.history_map[i]; - let reduced = R::reduce(history, item); - if N::is_novel(history, reduced) { - map_state.history_map[i] = reduced; - interesting = true; - } - } - } - - if interesting { - let mut filled = 0; - for i in 0..size { - if map_state.history_map[i] != initial { - filled += 1; - if self.indexes.is_some() { - self.indexes.as_mut().unwrap().push(i); - } - } - } - manager.fire( - state, - Event::UpdateUserStats { - name: self.stats_name.to_string(), - value: UserStats::Ratio(filled, size as u64), - phantom: PhantomData, - }, - )?; - } - - Ok(interesting) + #[rustversion::not(nightly)] + fn is_interesting( + &mut self, + state: &mut S, + manager: &mut EM, + input: &I, + observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + self.is_interesting_default(state, manager, input, observers, exit_kind) } fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { @@ -452,6 +412,127 @@ where } } +/// Specialize for the common coverage map size, maximization of u8s +#[rustversion::nightly] +impl Feedback for MapFeedback +where + O: MapObserver + AsSlice, + for<'it> O: AsRefIterator<'it, Item = u8>, + I: Input, + S: HasNamedMetadata + HasClientPerfMonitor + Debug, +{ + #[allow(clippy::wrong_self_convention)] + #[allow(clippy::needless_range_loop)] + fn is_interesting( + &mut self, + state: &mut S, + manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // 128 bits vectors + type VectorType = core::simd::u8x16; + + let mut interesting = false; + // TODO Replace with match_name_type when stable + let observer = observers.match_name::(&self.observer_name).unwrap(); + + let map_state = state + .named_metadata_mut() + .get_mut::>(&self.name) + .unwrap(); + + let size = observer.usable_count(); + if map_state.history_map.len() < size { + map_state.history_map.resize(size, u8::default()); + } + + let map = observer.as_slice(); + debug_assert!(map.len() >= size); + + let history_map = map_state.history_map.as_mut_slice(); + + // Non vector implementation for reference + /*for (i, history) in history_map.iter_mut().enumerate() { + let item = map[i]; + let reduced = MaxReducer::reduce(*history, item); + if DifferentIsNovel::is_novel(*history, reduced) { + *history = reduced; + interesting = true; + if self.novelties.is_some() { + self.novelties.as_mut().unwrap().push(i); + } + } + }*/ + + let steps = size / VectorType::LANES; + let left = size % VectorType::LANES; + + for step in 0..steps { + let i = step * VectorType::LANES; + let history = VectorType::from_slice(&history_map[i..]); + let items = VectorType::from_slice(&map[i..]); + + if items.max(history) != history { + interesting = true; + unsafe { + for j in i..(i + VectorType::LANES) { + let item = *map.get_unchecked(j); + if item > *history_map.get_unchecked(j) { + *history_map.get_unchecked_mut(j) = item; + if self.novelties.is_some() { + self.novelties.as_mut().unwrap().push(j); + } + } + } + } + } + } + + for j in (size - left)..size { + unsafe { + let item = *map.get_unchecked(j); + if item > *history_map.get_unchecked(j) { + interesting = true; + *history_map.get_unchecked_mut(j) = item; + if self.novelties.is_some() { + self.novelties.as_mut().unwrap().push(j); + } + } + } + } + + let initial = observer.initial(); + if interesting { + let len = history_map.len(); + let mut filled = 0; + for i in 0..len { + if history_map[i] != initial { + filled += 1; + if self.indexes.is_some() { + self.indexes.as_mut().unwrap().push(i); + } + } + } + manager.fire( + state, + Event::UpdateUserStats { + name: self.stats_name.to_string(), + value: UserStats::Ratio(filled, len as u64), + phantom: PhantomData, + }, + )?; + } + + Ok(interesting) + } +} + impl Named for MapFeedback where T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug, @@ -490,10 +571,11 @@ impl MapFeedback where T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug, R: Reducer, - N: IsNovel, O: MapObserver, for<'it> O: AsRefIterator<'it, Item = T>, - S: HasNamedMetadata, + N: IsNovel, + I: Input, + S: HasNamedMetadata + HasClientPerfMonitor + Debug, { /// Create new `MapFeedback` #[must_use] @@ -551,6 +633,76 @@ where phantom: PhantomData, } } + + #[allow(clippy::wrong_self_convention)] + #[allow(clippy::needless_range_loop)] + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_interesting_default( + &mut self, + state: &mut S, + manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + let mut interesting = false; + // TODO Replace with match_name_type when stable + let observer = observers.match_name::(&self.observer_name).unwrap(); + + let map_state = state + .named_metadata_mut() + .get_mut::>(&self.name) + .unwrap(); + let size = observer.usable_count(); + if map_state.history_map.len() < size { + map_state.history_map.resize(size, T::default()); + } + + let history_map = map_state.history_map.as_mut_slice(); + + for (i, (item, history)) in observer + .as_ref_iter() + .zip(history_map.iter_mut()) + .enumerate() + { + let reduced = R::reduce(*history, *item); + if N::is_novel(*history, reduced) { + *history = reduced; + interesting = true; + if self.novelties.is_some() { + self.novelties.as_mut().unwrap().push(i); + } + } + } + + let initial = observer.initial(); + if interesting { + let len = history_map.len(); + let mut filled = 0; + for i in 0..len { + if history_map[i] != initial { + filled += 1; + if self.indexes.is_some() { + self.indexes.as_mut().unwrap().push(i); + } + } + } + manager.fire( + state, + Event::UpdateUserStats { + name: self.stats_name.to_string(), + value: UserStats::Ratio(filled, len as u64), + phantom: PhantomData, + }, + )?; + } + + Ok(interesting) + } } /// A [`ReachabilityFeedback`] reports if a target has been reached. diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index ca64edd213..3d1dfdd67a 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -8,6 +8,8 @@ Welcome to `LibAFL` #![cfg_attr(unstable_feature, feature(specialization))] // For `type_id` and owned things #![cfg_attr(unstable_feature, feature(intrinsics))] +// For `std::simd` +#![cfg_attr(unstable_feature, feature(portable_simd))] #![warn(clippy::cargo)] #![deny(clippy::cargo_common_metadata)] #![deny(rustdoc::broken_intra_doc_links)] @@ -68,6 +70,8 @@ Welcome to `LibAFL` while_true ) )] +// Till they fix this buggy lint in clippy +#![allow(clippy::borrow_deref_ref)] #[cfg(feature = "std")] #[macro_use] diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 151da52ae1..bc202b0e46 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -58,17 +58,7 @@ pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned fn usable_count(&self) -> usize; /// Count the set bytes in the map - fn count_bytes(&self) -> u64 { - let initial = self.initial(); - let cnt = self.usable_count(); - let mut res = 0; - for i in 0..cnt { - if *self.get(i) != initial { - res += 1; - } - } - res - } + fn count_bytes(&self) -> u64; /// Compute the hash of the map fn hash(&self) -> u64; @@ -85,39 +75,13 @@ pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned } /// Reset the map - #[inline] - fn reset_map(&mut self) -> Result<(), Error> { - // Normal memset, see https://rust.godbolt.org/z/Trs5hv - let initial = self.initial(); - let cnt = self.usable_count(); - for i in 0..cnt { - *self.get_mut(i) = initial; - } - Ok(()) - } + fn reset_map(&mut self) -> Result<(), Error>; /// Get these observer's contents as [`Vec`] - fn to_vec(&self) -> Vec { - let cnt = self.usable_count(); - let mut res = Vec::with_capacity(cnt); - for i in 0..cnt { - res.push(*self.get(i)); - } - res - } + fn to_vec(&self) -> Vec; /// Get the number of set entries with the specified indexes - fn how_many_set(&self, indexes: &[usize]) -> usize { - let initial = self.initial(); - let cnt = self.usable_count(); - let mut res = 0; - for i in indexes { - if *i < cnt && *self.get(*i) != initial { - res += 1; - } - } - res - } + fn how_many_set(&self, indexes: &[usize]) -> usize; } /// A Simple iterator calling `MapObserver::get` @@ -333,6 +297,20 @@ where &mut self.as_mut_slice()[idx] } + /// Count the set bytes in the map + fn count_bytes(&self) -> u64 { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for x in map[0..cnt].iter() { + if *x != initial { + res += 1; + } + } + res + } + #[inline] fn usable_count(&self) -> usize { self.as_slice().len() @@ -355,6 +333,32 @@ where fn to_vec(&self) -> Vec { self.as_slice().to_vec() } + + /// Reset the map + #[inline] + fn reset_map(&mut self) -> Result<(), Error> { + // Normal memset, see https://rust.godbolt.org/z/Trs5hv + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_mut_slice(); + for x in map[0..cnt].iter_mut() { + *x = initial; + } + Ok(()) + } + + fn how_many_set(&self, indexes: &[usize]) -> usize { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for i in indexes { + if *i < cnt && map[*i] != initial { + res += 1; + } + } + res + } } impl<'a, T> AsSlice for StdMapObserver<'a, T> @@ -598,6 +602,20 @@ where &mut self.as_mut_slice()[idx] } + /// Count the set bytes in the map + fn count_bytes(&self) -> u64 { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for x in map[0..cnt].iter() { + if *x != initial { + res += 1; + } + } + res + } + fn usable_count(&self) -> usize { self.as_slice().len() } @@ -606,9 +624,36 @@ where hash_slice(self.as_slice()) } + /// Reset the map + #[inline] + fn reset_map(&mut self) -> Result<(), Error> { + // Normal memset, see https://rust.godbolt.org/z/Trs5hv + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_mut_slice(); + for x in map[0..cnt].iter_mut() { + *x = initial; + } + Ok(()) + } + fn to_vec(&self) -> Vec { self.as_slice().to_vec() } + + /// Get the number of set entries with the specified indexes + fn how_many_set(&self, indexes: &[usize]) -> usize { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for i in indexes { + if *i < cnt && map[*i] != initial { + res += 1; + } + } + res + } } impl<'a, T, const N: usize> AsSlice for ConstMapObserver<'a, T, N> @@ -831,12 +876,52 @@ where &mut self.map.as_mut_slice()[idx] } + /// Count the set bytes in the map + fn count_bytes(&self) -> u64 { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for x in map[0..cnt].iter() { + if *x != initial { + res += 1; + } + } + res + } fn hash(&self) -> u64 { hash_slice(self.as_slice()) } + + /// Reset the map + #[inline] + fn reset_map(&mut self) -> Result<(), Error> { + // Normal memset, see https://rust.godbolt.org/z/Trs5hv + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_mut_slice(); + for x in map[0..cnt].iter_mut() { + *x = initial; + } + Ok(()) + } + fn to_vec(&self) -> Vec { self.as_slice().to_vec() } + + fn how_many_set(&self, indexes: &[usize]) -> usize { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for i in indexes { + if *i < cnt && map[*i] != initial { + res += 1; + } + } + res + } } impl<'a, T> AsSlice for VariableMapObserver<'a, T> @@ -924,9 +1009,27 @@ static COUNT_CLASS_LOOKUP: [u8; 256] = [ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, ]; +static mut COUNT_CLASS_LOOKUP_16: Vec = vec![]; + +fn init_count_class_16() { + unsafe { + if !COUNT_CLASS_LOOKUP_16.is_empty() { + return; + } + + COUNT_CLASS_LOOKUP_16 = vec![0; 65536]; + for i in 0..256 { + for j in 0..256 { + COUNT_CLASS_LOOKUP_16[(i << 8) + j] = + (u16::from(COUNT_CLASS_LOOKUP[i]) << 8) | u16::from(COUNT_CLASS_LOOKUP[j]); + } + } + } +} + impl Observer for HitcountsMapObserver where - M: MapObserver + Observer, + M: MapObserver + Observer + AsMutSlice, for<'it> M: AsMutIterator<'it, Item = u8>, { #[inline] @@ -935,9 +1038,23 @@ where } #[inline] + #[allow(clippy::cast_ptr_alignment)] fn post_exec(&mut self, state: &mut S, input: &I, exit_kind: &ExitKind) -> Result<(), Error> { - for elem in self.as_mut_iter() { - *elem = COUNT_CLASS_LOOKUP[*elem as usize]; + let map = self.as_mut_slice(); + let len = map.len(); + if (len & 1) != 0 { + unsafe { + *map.get_unchecked_mut(len - 1) = + *COUNT_CLASS_LOOKUP.get_unchecked(*map.get_unchecked(len - 1) as usize); + } + } + + let cnt = len / 2; + let map16 = unsafe { core::slice::from_raw_parts_mut(map.as_mut_ptr() as *mut u16, cnt) }; + for (_i, item) in map16[0..cnt].iter_mut().enumerate() { + unsafe { + *item = *COUNT_CLASS_LOOKUP_16.get_unchecked(*item as usize); + } } self.base.post_exec(state, input, exit_kind) } @@ -995,12 +1112,27 @@ where self.base.get_mut(idx) } + /// Count the set bytes in the map + fn count_bytes(&self) -> u64 { + self.base.count_bytes() + } + + /// Reset the map + #[inline] + fn reset_map(&mut self) -> Result<(), Error> { + self.base.reset_map() + } + fn hash(&self) -> u64 { self.base.hash() } fn to_vec(&self) -> Vec { self.base.to_vec() } + + fn how_many_set(&self, indexes: &[usize]) -> usize { + self.base.how_many_set(indexes) + } } impl AsSlice for HitcountsMapObserver @@ -1028,6 +1160,7 @@ where { /// Creates a new [`MapObserver`] pub fn new(base: M) -> Self { + init_count_class_16(); Self { base } } } @@ -1207,6 +1340,28 @@ where fn usable_count(&self) -> usize { self.len() } + + fn to_vec(&self) -> Vec { + let cnt = self.usable_count(); + let mut res = Vec::with_capacity(cnt); + for i in 0..cnt { + res.push(*self.get(i)); + } + res + } + + /// Get the number of set entries with the specified indexes + fn how_many_set(&self, indexes: &[usize]) -> usize { + let initial = self.initial(); + let cnt = self.usable_count(); + let mut res = 0; + for i in indexes { + if *i < cnt && *self.get(*i) != initial { + res += 1; + } + } + res + } } impl<'a, T> MultiMapObserver<'a, T> @@ -1434,6 +1589,20 @@ where &mut self.as_mut_slice()[idx] } + /// Count the set bytes in the map + fn count_bytes(&self) -> u64 { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for x in map[0..cnt].iter() { + if *x != initial { + res += 1; + } + } + res + } + #[inline] fn usable_count(&self) -> usize { self.as_slice().len() @@ -1458,9 +1627,34 @@ where self.initial = initial; } + /// Reset the map + #[inline] + fn reset_map(&mut self) -> Result<(), Error> { + // Normal memset, see https://rust.godbolt.org/z/Trs5hv + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_mut_slice(); + for x in map[0..cnt].iter_mut() { + *x = initial; + } + Ok(()) + } fn to_vec(&self) -> Vec { self.as_slice().to_vec() } + + fn how_many_set(&self, indexes: &[usize]) -> usize { + let initial = self.initial(); + let cnt = self.usable_count(); + let map = self.as_slice(); + let mut res = 0; + for i in indexes { + if *i < cnt && map[*i] != initial { + res += 1; + } + } + res + } } impl AsSlice for OwnedMapObserver @@ -1505,8 +1699,8 @@ where #[allow(missing_docs)] pub mod pybind { use super::{ - AsMutIterator, AsRefIterator, Debug, Error, HasLen, Iter, IterMut, MapObserver, Named, - Observer, OwnedMapObserver, StdMapObserver, String, Vec, + AsMutIterator, AsMutSlice, AsRefIterator, AsSlice, Debug, Error, HasLen, Iter, IterMut, + MapObserver, Named, Observer, OwnedMapObserver, StdMapObserver, String, Vec, }; use crate::observers::pybind::PythonObserver; use concat_idents::concat_idents; @@ -1758,6 +1952,18 @@ pub mod pybind { } } + impl AsSlice<$datatype> for $struct_name_trait { + fn as_slice(&self) -> &[$datatype] { + mapob_unwrap_me!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute(m.as_slice()) }} ) + } + } + + impl AsMutSlice<$datatype> for $struct_name_trait { + fn as_mut_slice(&mut self) -> &mut [$datatype] { + mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute(m.as_mut_slice()) }} ) + } + } + impl MapObserver for $struct_name_trait { type Entry = $datatype; @@ -1773,6 +1979,10 @@ pub mod pybind { unsafe { ptr.as_mut().unwrap() } } + #[inline] + fn count_bytes(&self) -> u64 { + mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.count_bytes() }) + } #[inline] fn usable_count(&self) -> usize { mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.usable_count() }) @@ -1798,9 +2008,20 @@ pub mod pybind { mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.set_initial(initial) }); } + #[inline] + fn reset_map(&mut self) -> Result<(), Error> { + mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.reset_map() }) + } + + #[inline] fn to_vec(&self) -> Vec<$datatype> { mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.to_vec() }) } + + #[inline] + fn how_many_set(&self, indexes: &[usize]) -> usize { + mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.how_many_set(indexes) }) + } } impl Named for $struct_name_trait { diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 5296bdcbf8..763b9f3959 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -813,7 +813,6 @@ 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 {