From eca605bf01c5ff3552bf76a9a6ed3c39937215dc Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Thu, 4 Nov 2021 10:08:50 +0100 Subject: [PATCH] MultiMapObserver and sancov 8bit-counters instrumentation (#343) * MultiMapObserver and 8bit instrumentation * fix test * clippy * fix * fix tutorial * sancov_8bit targets feature --- fuzzers/tutorial/src/input.rs | 4 +- libafl/Cargo.toml | 1 + libafl/src/bolts/mod.rs | 11 ++ libafl/src/bolts/tuples.rs | 12 +- libafl/src/corpus/minimizer.rs | 4 +- libafl/src/corpus/testcase.rs | 4 +- libafl/src/feedbacks/map.rs | 10 +- libafl/src/inputs/bytes.rs | 4 +- libafl/src/inputs/encoded.rs | 5 +- libafl/src/inputs/gramatron.rs | 5 +- libafl/src/inputs/mod.rs | 11 -- libafl/src/mutators/mod.rs | 4 +- libafl/src/mutators/mutations.rs | 2 +- libafl/src/observers/map.rs | 307 +++++++++++++++++++++++++++--- libafl_targets/Cargo.toml | 1 + libafl_targets/src/lib.rs | 8 + libafl_targets/src/sancov_8bit.rs | 14 ++ 17 files changed, 337 insertions(+), 70 deletions(-) create mode 100644 libafl_targets/src/sancov_8bit.rs diff --git a/fuzzers/tutorial/src/input.rs b/fuzzers/tutorial/src/input.rs index 0ff3aeed5d..599c9b5e94 100644 --- a/fuzzers/tutorial/src/input.rs +++ b/fuzzers/tutorial/src/input.rs @@ -1,6 +1,6 @@ use libafl::{ - bolts::ownedref::OwnedSlice, - inputs::{HasLen, HasTargetBytes, Input}, + bolts::{ownedref::OwnedSlice, HasLen}, + inputs::{HasTargetBytes, Input}, }; use lain::prelude::*; diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 9ba3e82a44..6dfdcec40f 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -68,6 +68,7 @@ ctor = "0.1.20" num_enum = { version = "0.5.1", default-features = false } typed-builder = "0.9.0" # Implement the builder pattern at compiletime ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown +intervaltree = { git = "https://github.com/andreafioraldi/rust-intervaltree", version = "0.2.6", default-features = false, features = ["serde"] } libafl_derive = { version = "0.6.1", optional = true, path = "../libafl_derive" } serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap diff --git a/libafl/src/bolts/mod.rs b/libafl/src/bolts/mod.rs index 9a11689142..e8a8a07861 100644 --- a/libafl/src/bolts/mod.rs +++ b/libafl/src/bolts/mod.rs @@ -27,6 +27,17 @@ pub trait AsSlice { fn as_slice(&self) -> &[T]; } +/// Has a length field +pub trait HasLen { + /// The length + fn len(&self) -> usize; + + /// Returns `true` if it has no elements. + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + /// Current time #[cfg(feature = "std")] #[must_use] diff --git a/libafl/src/bolts/tuples.rs b/libafl/src/bolts/tuples.rs index f8bb191133..36a327d956 100644 --- a/libafl/src/bolts/tuples.rs +++ b/libafl/src/bolts/tuples.rs @@ -35,7 +35,7 @@ const fn type_eq() -> bool { } /// Gets the length of the element -pub trait HasLen { +pub trait HasConstLen { /// The length as constant `usize` const LEN: usize; @@ -47,7 +47,7 @@ pub trait HasLen { } } -impl HasLen for () { +impl HasConstLen for () { const LEN: usize = 0; fn len(&self) -> usize { @@ -55,9 +55,9 @@ impl HasLen for () { } } -impl HasLen for (Head, Tail) +impl HasConstLen for (Head, Tail) where - Tail: HasLen, + Tail: HasConstLen, { const LEN: usize = 1 + Tail::LEN; @@ -78,7 +78,7 @@ pub trait HasNameId { } /// Gets the id and `const_name` for the given index in a tuple -pub trait HasNameIdTuple: HasLen { +pub trait HasNameIdTuple: HasConstLen { /// Gets the `const_name` for the entry at the given index fn const_name_for(&self, index: usize) -> Option<&'static str>; @@ -199,7 +199,7 @@ pub trait Named { } /// A named tuple -pub trait NamedTuple: HasLen { +pub trait NamedTuple: HasConstLen { /// Gets the name of this tuple fn name(&self, index: usize) -> Option<&str>; } diff --git a/libafl/src/corpus/minimizer.rs b/libafl/src/corpus/minimizer.rs index 6cafbedd4b..7962cf759b 100644 --- a/libafl/src/corpus/minimizer.rs +++ b/libafl/src/corpus/minimizer.rs @@ -2,10 +2,10 @@ // with testcases only from a subset of the total corpus. use crate::{ - bolts::{rands::Rand, serdeany::SerdeAny, AsSlice}, + bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasLen}, corpus::{Corpus, CorpusScheduler, Testcase}, feedbacks::MapIndexesMetadata, - inputs::{HasLen, Input}, + inputs::Input, state::{HasCorpus, HasMetadata, HasRand}, Error, }; diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index 1b539e8f57..0f1d1df4db 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -6,8 +6,8 @@ use core::{convert::Into, default::Default, option::Option, time::Duration}; use serde::{Deserialize, Serialize}; use crate::{ - bolts::serdeany::SerdeAnyMap, - inputs::{HasLen, Input}, + bolts::{serdeany::SerdeAnyMap, HasLen}, + inputs::Input, state::HasMetadata, Error, }; diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index 486602dcc2..a8ccd4169c 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -171,7 +171,7 @@ where O: MapObserver, { Self { - history_map: vec![T::default(); map_observer.map().len()], + history_map: vec![T::default(); map_observer.len()], name: map_observer.name().to_string(), } } @@ -245,12 +245,12 @@ where if size > map_state.history_map.len() { panic!("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.map().len()); + assert!(size <= observer.len()); if self.novelties.is_some() { for i in 0..size { let history = map_state.history_map[i]; - let item = observer.map()[i]; + let item = *observer.get(i); let reduced = R::reduce(history, item); if history != reduced { @@ -262,7 +262,7 @@ where } else { for i in 0..size { let history = map_state.history_map[i]; - let item = observer.map()[i]; + let item = *observer.get(i); let reduced = R::reduce(history, item); if history != reduced { @@ -457,7 +457,7 @@ where let mut hit_target: bool = false; //check if we've hit any targets. for i in 0..size { - if observer.map()[i] > 0 { + if *observer.get(i) > 0 { self.target_idx.push(i); hit_target = true; } diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 5006abef2b..5ecea7ef2f 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -12,8 +12,8 @@ use std::{fs::File, io::Read, path::Path}; #[cfg(feature = "std")] use crate::{bolts::fs::write_file_atomic, Error}; use crate::{ - bolts::ownedref::OwnedSlice, - inputs::{HasBytesVec, HasLen, HasTargetBytes, Input}, + bolts::{ownedref::OwnedSlice, HasLen}, + inputs::{HasBytesVec, HasTargetBytes, Input}, }; /// A bytes input is the basic input diff --git a/libafl/src/inputs/encoded.rs b/libafl/src/inputs/encoded.rs index eccfb66ba6..95c141bc5c 100644 --- a/libafl/src/inputs/encoded.rs +++ b/libafl/src/inputs/encoded.rs @@ -13,10 +13,7 @@ use hashbrown::HashMap; use regex::Regex; use serde::{Deserialize, Serialize}; -use crate::{ - inputs::{HasLen, Input}, - Error, -}; +use crate::{bolts::HasLen, inputs::Input, Error}; pub trait InputEncoder where diff --git a/libafl/src/inputs/gramatron.rs b/libafl/src/inputs/gramatron.rs index 3e3f21950d..f5b0f39fc2 100644 --- a/libafl/src/inputs/gramatron.rs +++ b/libafl/src/inputs/gramatron.rs @@ -5,10 +5,7 @@ use alloc::{rc::Rc, string::String, vec::Vec}; use core::{cell::RefCell, convert::From}; use serde::{Deserialize, Serialize}; -use crate::{ - inputs::{HasLen, Input}, - Error, -}; +use crate::{bolts::HasLen, inputs::Input, Error}; #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)] pub struct Terminal { diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 8e7afa0ba0..b4a2d70974 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -90,14 +90,3 @@ pub trait HasBytesVec { /// The internal bytes map (as mutable borrow) fn bytes_mut(&mut self) -> &mut Vec; } - -/// Has a length field -pub trait HasLen { - /// The length - fn len(&self) -> usize; - - /// Returns `true` if it has no elements. - fn is_empty(&self) -> bool { - self.len() == 0 - } -} diff --git a/libafl/src/mutators/mod.rs b/libafl/src/mutators/mod.rs index 8f9cf7481d..ca4d649f9b 100644 --- a/libafl/src/mutators/mod.rs +++ b/libafl/src/mutators/mod.rs @@ -14,7 +14,7 @@ pub mod gramatron; pub use gramatron::*; use crate::{ - bolts::tuples::{HasLen, Named}, + bolts::tuples::{HasConstLen, Named}, inputs::Input, Error, }; @@ -59,7 +59,7 @@ where } /// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row. -pub trait MutatorsTuple: HasLen +pub trait MutatorsTuple: HasConstLen where I: Input, { diff --git a/libafl/src/mutators/mutations.rs b/libafl/src/mutators/mutations.rs index 91a908afe7..903cdcfd22 100644 --- a/libafl/src/mutators/mutations.rs +++ b/libafl/src/mutators/mutations.rs @@ -1579,7 +1579,7 @@ mod tests { use crate::{ bolts::{ rands::StdRand, - tuples::{tuple_list, HasLen}, + tuples::{tuple_list, HasConstLen}, }, corpus::{Corpus, InMemoryCorpus}, inputs::BytesInput, diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 22416318b3..221a43926d 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -9,6 +9,7 @@ use core::{ hash::Hasher, slice::{from_raw_parts, from_raw_parts_mut}, }; +use intervaltree::IntervalTree; use num::Integer; use serde::{Deserialize, Serialize}; @@ -16,25 +17,38 @@ use crate::{ bolts::{ ownedref::{OwnedRefMut, OwnedSliceMut}, tuples::Named, + HasLen, }, observers::Observer, Error, }; /// A [`MapObserver`] observes the static map, as oftentimes used for afl-like coverage information -pub trait MapObserver: Named + serde::Serialize + serde::de::DeserializeOwned +pub trait MapObserver: HasLen + Named + serde::Serialize + serde::de::DeserializeOwned where T: Integer + Default + Copy, { - /// Get the map - fn map(&self) -> &[T]; + /// Get the map if the observer can be represented with a slice + fn map(&self) -> Option<&[T]>; - /// Get the map (mutable) - fn map_mut(&mut self) -> &mut [T]; + /// Get the map (mutable) if the observer can be represented with a slice + fn map_mut(&mut self) -> Option<&mut [T]>; + + fn get(&self, idx: usize) -> &T { + &self + .map() + .expect("Cannot get a map that cannot be represented as slice")[idx] + } + + fn get_mut(&mut self, idx: usize) -> &mut T { + &mut self + .map_mut() + .expect("Cannot get a map that cannot be represented as slice")[idx] + } /// Get the number of usable entries in the map (all by default) fn usable_count(&self) -> usize { - self.map().len() + self.len() } /// Count the set bytes in the map @@ -42,8 +56,8 @@ where let initial = self.initial(); let cnt = self.usable_count(); let mut res = 0; - for x in self.map()[0..cnt].iter() { - if *x != initial { + for i in 0..cnt { + if *self.get(i) != initial { res += 1; } } @@ -53,12 +67,14 @@ where /// Compute the hash of the map fn hash(&self) -> u64 { let mut hasher = AHasher::new_with_keys(0, 0); - let ptr = self.map().as_ptr() as *const u8; - let map_size = self.map().len() / core::mem::size_of::(); + let slice = self + .map() + .expect("Cannot hash a map that cannot be represented as slice"); + let ptr = slice.as_ptr() as *const u8; + let map_size = slice.len() / core::mem::size_of::(); unsafe { hasher.write(from_raw_parts(ptr, map_size)); } - hasher.finish() } @@ -77,8 +93,8 @@ where // Normal memset, see https://rust.godbolt.org/z/Trs5hv let initial = self.initial(); let cnt = self.usable_count(); - for x in self.map_mut()[0..cnt].iter_mut() { - *x = initial; + for i in 0..cnt { + *self.get_mut(i) = initial; } Ok(()) } @@ -120,18 +136,28 @@ where } } +impl<'a, T> HasLen for StdMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn len(&self) -> usize { + self.map.as_slice().len() + } +} + impl<'a, T> MapObserver for StdMapObserver<'a, T> where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { #[inline] - fn map(&self) -> &[T] { - self.map.as_slice() + fn map(&self) -> Option<&[T]> { + Some(self.map.as_slice()) } #[inline] - fn map_mut(&mut self) -> &mut [T] { - self.map.as_mut_slice() + fn map_mut(&mut self) -> Option<&mut [T]> { + Some(self.map.as_mut_slice()) } #[inline] @@ -225,6 +251,16 @@ where } } +impl<'a, T, const N: usize> HasLen for ConstMapObserver<'a, T, N> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn len(&self) -> usize { + N + } +} + impl<'a, T, const N: usize> MapObserver for ConstMapObserver<'a, T, N> where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, @@ -235,13 +271,13 @@ where } #[inline] - fn map(&self) -> &[T] { - self.map.as_slice() + fn map(&self) -> Option<&[T]> { + Some(self.map.as_slice()) } #[inline] - fn map_mut(&mut self) -> &mut [T] { - self.map.as_mut_slice() + fn map_mut(&mut self) -> Option<&mut [T]> { + Some(self.map.as_mut_slice()) } #[inline] @@ -337,18 +373,28 @@ where } } +impl<'a, T> HasLen for VariableMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn len(&self) -> usize { + self.map.as_slice().len() + } +} + impl<'a, T> MapObserver for VariableMapObserver<'a, T> where T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { #[inline] - fn map(&self) -> &[T] { - self.map.as_slice() + fn map(&self) -> Option<&[T]> { + Some(self.map.as_slice()) } #[inline] - fn map_mut(&mut self) -> &mut [T] { - self.map.as_mut_slice() + fn map_mut(&mut self) -> Option<&mut [T]> { + Some(self.map.as_mut_slice()) } #[inline] @@ -444,8 +490,8 @@ where #[inline] fn post_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> { let cnt = self.usable_count(); - for x in self.map_mut()[0..cnt].iter_mut() { - *x = COUNT_CLASS_LOOKUP[*x as usize]; + for i in 0..cnt { + *self.get_mut(i) = COUNT_CLASS_LOOKUP[*self.get(i) as usize]; } self.base.post_exec(state, input) } @@ -461,17 +507,27 @@ where } } +impl HasLen for HitcountsMapObserver +where + M: MapObserver, +{ + #[inline] + fn len(&self) -> usize { + self.base.len() + } +} + impl MapObserver for HitcountsMapObserver where M: MapObserver, { #[inline] - fn map(&self) -> &[u8] { + fn map(&self) -> Option<&[u8]> { self.base.map() } #[inline] - fn map_mut(&mut self) -> &mut [u8] { + fn map_mut(&mut self) -> Option<&mut [u8]> { self.base.map_mut() } @@ -505,3 +561,196 @@ where Self { base } } } + +/// The Multi Map Observer merge different maps into one observer +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "T: serde::de::DeserializeOwned")] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct MultiMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + maps: Vec>, + intervals: IntervalTree, + len: usize, + initial: T, + name: String, +} + +impl<'a, I, S, T> Observer for MultiMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, + Self: MapObserver, +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.reset_map() + } +} + +impl<'a, T> Named for MultiMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl<'a, T> HasLen for MultiMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn len(&self) -> usize { + self.len + } +} + +impl<'a, T> MapObserver for MultiMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn map(&self) -> Option<&[T]> { + None + } + + #[inline] + fn map_mut(&mut self) -> Option<&mut [T]> { + None + } + + #[inline] + fn get(&self, idx: usize) -> &T { + let elem = self.intervals.query_point(idx).next().unwrap(); + let i = elem.value; + let j = idx - elem.range.start; + &self.maps[i].as_slice()[j] + } + + #[inline] + fn get_mut(&mut self, idx: usize) -> &mut T { + let elem = self.intervals.query_point(idx).next().unwrap(); + let i = elem.value; + let j = idx - elem.range.start; + &mut self.maps[i].as_mut_slice()[j] + } + + #[inline] + fn initial(&self) -> T { + self.initial + } + + #[inline] + fn initial_mut(&mut self) -> &mut T { + &mut self.initial + } + + #[inline] + fn set_initial(&mut self, initial: T) { + self.initial = initial; + } + + fn count_bytes(&self) -> u64 { + let initial = self.initial(); + let mut res = 0; + for map in &self.maps { + for x in map.as_slice() { + if *x != initial { + res += 1; + } + } + } + res + } + + fn hash(&self) -> u64 { + let mut hasher = AHasher::new_with_keys(0, 0); + for map in &self.maps { + let slice = map.as_slice(); + let ptr = slice.as_ptr() as *const u8; + let map_size = slice.len() / core::mem::size_of::(); + unsafe { + hasher.write(from_raw_parts(ptr, map_size)); + } + } + hasher.finish() + } + + fn reset_map(&mut self) -> Result<(), Error> { + let initial = self.initial(); + for map in &mut self.maps { + for x in map.as_mut_slice() { + *x = initial; + } + } + Ok(()) + } +} + +impl<'a, T> MultiMapObserver<'a, T> +where + T: Integer + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`MultiMapObserver`] + #[must_use] + pub fn new(name: &'static str, maps: &'a mut [&'a mut [T]]) -> Self { + let mut idx = 0; + let mut v = 0; + let mut initial = T::default(); + let mut builder = vec![]; + let maps: Vec<_> = maps + .iter_mut() + .map(|x| { + if !x.is_empty() { + initial = x[0]; + } + let l = x.len(); + let r = (idx..(idx + l), v); + idx += l; + builder.push(r); + v += 1; + OwnedSliceMut::Ref(x) + }) + .collect(); + Self { + maps, + intervals: builder.into_iter().collect::>(), + len: idx, + name: name.to_string(), + initial, + } + } + + /// Creates a new [`MultiMapObserver`] with an owned map + #[must_use] + pub fn new_owned(name: &'static str, maps: Vec>) -> Self { + let mut idx = 0; + let mut v = 0; + let mut initial = T::default(); + let mut builder = vec![]; + let maps: Vec<_> = maps + .into_iter() + .map(|x| { + if !x.is_empty() { + initial = x[0]; + } + let l = x.len(); + let r = (idx..(idx + l), v); + idx += l; + builder.push(r); + v += 1; + OwnedSliceMut::Owned(x) + }) + .collect(); + Self { + maps, + intervals: builder.into_iter().collect::>(), + len: idx, + name: name.to_string(), + initial, + } + } +} diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index aebfb6c695..0e80b84974 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -18,6 +18,7 @@ sancov_pcguard_hitcounts = [] sancov_pcguard_edges_ptr = [] sancov_pcguard_hitcounts_ptr = [] sancov_value_profile = [] +sancov_8bit = [] sancov_cmplog = [] sancov_pcguard = ["sancov_pcguard_hitcounts"] clippy = [] # Ignore compiler warnings during clippy diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 51bb795c27..d52757da92 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,5 +1,8 @@ //! `libafl_targets` contains runtime code, injected in the target itself during compilation. +#[macro_use] +extern crate alloc; + include!(concat!(env!("OUT_DIR"), "/constants.rs")); #[cfg(any( @@ -27,6 +30,11 @@ pub mod libfuzzer; #[cfg(feature = "libfuzzer")] pub use libfuzzer::*; +#[cfg(feature = "sancov_8bit")] +pub mod sancov_8bit; +#[cfg(feature = "sancov_8bit")] +pub use sancov_8bit::*; + pub mod coverage; pub use coverage::*; diff --git a/libafl_targets/src/sancov_8bit.rs b/libafl_targets/src/sancov_8bit.rs new file mode 100644 index 0000000000..bf099ed261 --- /dev/null +++ b/libafl_targets/src/sancov_8bit.rs @@ -0,0 +1,14 @@ +//! [`LLVM` `8-bi-counters`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`. +use alloc::vec::Vec; +use core::slice::from_raw_parts_mut; + +pub static mut COUNTERS_MAPS: Vec<&'static mut [u8]> = Vec::new(); + +/// Initialize the sancov `8-bit-counters` - usually called by `llvm`. +/// +/// # Safety +/// Set up our coverage maps. +#[no_mangle] +pub fn __sanitizer_cov_8bit_counters_init(start: *mut u8, stop: *mut u8) { + unsafe { COUNTERS_MAPS.push(from_raw_parts_mut(start, stop.offset_from(start) as usize)) } +}