diff --git a/libafl/src/corpus/testcase.rs b/libafl/src/corpus/testcase.rs index bd3ed96739..c8c5368bc9 100644 --- a/libafl/src/corpus/testcase.rs +++ b/libafl/src/corpus/testcase.rs @@ -326,6 +326,10 @@ where /// The Metadata for each testcase used in power schedules. #[derive(Serialize, Deserialize, Clone, Debug)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct SchedulerTestcaseMetadata { /// Number of bits set in bitmap, updated in calibrate_case bitmap_size: u64, diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 709d1327c7..c0bede923d 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -47,6 +47,10 @@ use crate::{ /// How an execution finished. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub enum ExitKind { /// The run exited normally. Ok, @@ -69,6 +73,10 @@ pub enum ExitKind { /// How one of the diffing executions finished. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub enum DiffExitKind { /// The run exited normally. Ok, diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index f4ef3a52bc..c8d3c8ec85 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -222,6 +222,10 @@ where /// A testcase metadata holding a list of indexes of a map #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct MapIndexesMetadata { /// The list of indexes. pub list: Vec, @@ -266,6 +270,10 @@ impl MapIndexesMetadata { /// A testcase metadata holding a list of indexes of a map #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct MapNoveltiesMetadata { /// A `list` of novelties. pub list: Vec, @@ -300,6 +308,10 @@ impl MapNoveltiesMetadata { /// The state of [`MapFeedback`] #[derive(Default, Serialize, Deserialize, Clone, Debug)] #[serde(bound = "T: DeserializeOwned")] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct MapFeedbackMetadata where T: Default + Copy + 'static + Serialize, diff --git a/libafl/src/inputs/bytes.rs b/libafl/src/inputs/bytes.rs index 20ff39e5c0..a70bd40d14 100644 --- a/libafl/src/inputs/bytes.rs +++ b/libafl/src/inputs/bytes.rs @@ -109,7 +109,7 @@ impl From for Vec { impl BytesInput { /// Creates a new bytes input using the given bytes #[must_use] - pub fn new(bytes: Vec) -> Self { + pub const fn new(bytes: Vec) -> Self { Self { bytes } } } diff --git a/libafl/src/inputs/generalized.rs b/libafl/src/inputs/generalized.rs index 27c117d79a..6eb4739f05 100644 --- a/libafl/src/inputs/generalized.rs +++ b/libafl/src/inputs/generalized.rs @@ -24,6 +24,10 @@ pub enum GeneralizedItem { /// Metadata regarding the generalised content of an input #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct GeneralizedInputMetadata { generalized: Vec, } diff --git a/libafl/src/mutators/gramatron.rs b/libafl/src/mutators/gramatron.rs index 59793a16c4..231523b3e7 100644 --- a/libafl/src/mutators/gramatron.rs +++ b/libafl/src/mutators/gramatron.rs @@ -72,6 +72,10 @@ where /// The metadata used for `gramatron` #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct GramatronIdxMapMetadata { /// The map containing a vec for each terminal pub map: HashMap>, diff --git a/libafl/src/mutators/mopt_mutator.rs b/libafl/src/mutators/mopt_mutator.rs index 7bad4050fc..6955c5252e 100644 --- a/libafl/src/mutators/mopt_mutator.rs +++ b/libafl/src/mutators/mopt_mutator.rs @@ -28,6 +28,10 @@ use crate::{ /// On the other hand, in the core fuzzing mode, the fuzzer chooses the best `swarms`, which was determined during the pilot fuzzing mode, to compute the probability to choose the operation operator. /// With the current implementation we are always in the pacemaker fuzzing mode. #[derive(Serialize, Deserialize, Clone)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct MOpt { /// Random number generator pub rand: StdRand, diff --git a/libafl/src/mutators/scheduled.rs b/libafl/src/mutators/scheduled.rs index cb43b3f104..9ac7e43c5d 100644 --- a/libafl/src/mutators/scheduled.rs +++ b/libafl/src/mutators/scheduled.rs @@ -24,6 +24,10 @@ use crate::{ /// The metadata placed in a [`crate::corpus::Testcase`] by a [`LoggerScheduledMutator`]. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct LogMutationMetadata { /// A list of logs pub list: Vec, diff --git a/libafl/src/mutators/token_mutations.rs b/libafl/src/mutators/token_mutations.rs index 3a2d4ba939..ba3f07eeee 100644 --- a/libafl/src/mutators/token_mutations.rs +++ b/libafl/src/mutators/token_mutations.rs @@ -34,8 +34,8 @@ use crate::{ }; /// A state metadata holding a list of tokens -#[derive(Debug, Default, Clone, Serialize, Deserialize)] #[allow(clippy::unsafe_derive_deserialize)] +#[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Tokens { // We keep a vec and a set, set for faster deduplication, vec for access tokens_vec: Vec>, @@ -1759,18 +1759,22 @@ impl TextType { } } +/// Returns `true` if the given `u8` char is +/// in the valid ascii range (`<= 0x7F`) #[inline] -fn isascii(c: u8) -> bool { +const fn isascii(c: u8) -> bool { c <= 0x7F } +/// Returns `true` if the given `u8` char is +/// a valid printable character (between `0x20` and `0x7E`) #[inline] -fn isprint(c: u8) -> bool { - (0x20..=0x7e).contains(&c) +const fn isprint(c: u8) -> bool { + c >= 0x20 && c <= 0x7E } #[inline] -fn strlen(buf: &[u8]) -> usize { +const fn strlen(buf: &[u8]) -> usize { let mut count = 0; while count < buf.len() { if buf[count] == 0x0 { diff --git a/libafl/src/mutators/tuneable.rs b/libafl/src/mutators/tuneable.rs index 36bec3fac2..057f283a3a 100644 --- a/libafl/src/mutators/tuneable.rs +++ b/libafl/src/mutators/tuneable.rs @@ -8,7 +8,9 @@ use core::{ marker::PhantomData, }; -use libafl_bolts::{calculate_cumulative_sum_in_place, impl_serdeany, rands::Rand, Named}; +use libafl_bolts::{ + impl_serdeany, math::calculate_cumulative_distribution_in_place, rands::Rand, Named, +}; use serde::{Deserialize, Serialize}; pub use crate::mutators::{mutations::*, token_mutations::*}; @@ -22,6 +24,10 @@ use crate::{ /// Metadata in the state, that controls the behavior of the [`TuneableScheduledMutator`] at runtime #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct TuneableScheduledMutatorMetadata { /// The offsets of mutators to run, in order. Clear to fall back to random, /// or use `mutation_probabilities` @@ -40,6 +46,8 @@ pub struct TuneableScheduledMutatorMetadata { pub iter_probabilities_pow_cumulative: Vec, } +impl_serdeany!(TuneableScheduledMutatorMetadata); + impl Default for TuneableScheduledMutatorMetadata { fn default() -> Self { Self { @@ -70,8 +78,6 @@ impl TuneableScheduledMutatorMetadata { } } -impl_serdeany!(TuneableScheduledMutatorMetadata); - /// A [`Mutator`] that schedules one of the embedded mutations on each call. /// The index of the next mutation can be set. pub struct TuneableScheduledMutator @@ -247,36 +253,6 @@ where } } -// Returns the cumulative distribution function for a discrete distribution. -fn calculate_cumulative_distribution(probabilities: Vec) -> Result, Error> { - if probabilities.is_empty() { - return Err(Error::illegal_argument("empty list of probabilities")); - } - - if !probabilities.iter().all(|&p| (0.0..=1.0).contains(&p)) { - return Err(Error::illegal_argument(format!( - "invalid probability distribution: {probabilities:?}" - ))); - } - - let mut cumulative = probabilities; - calculate_cumulative_sum_in_place(&mut cumulative); - - // The cumulative sum should be roughly equal to 1. - let last = cumulative.last_mut().unwrap(); - if num_traits::abs(*last - 1.0_f32) > 1.0e-4 { - return Err(Error::illegal_argument(format!( - "sum of probabilities ({}) is not 1", - *last - ))); - } - - // Clamp the end of the vector to account for floating point errors. - *last = 1.0_f32; - - Ok(cumulative) -} - impl TuneableScheduledMutator<(), (), S> where S: HasRand + HasMetadata, @@ -317,7 +293,7 @@ where /// Setting this function will unset everything previously set in `set_iters`. pub fn set_iter_probabilities_pow( state: &mut S, - iter_probabilities_pow: Vec, + mut iter_probabilities_pow: Vec, ) -> Result<(), Error> { if iter_probabilities_pow.len() >= 32 { return Err(Error::illegal_argument( @@ -328,8 +304,8 @@ where metadata.iters = None; // we precalculate the cumulative probability to be faster when sampling later. - metadata.iter_probabilities_pow_cumulative = - calculate_cumulative_distribution(iter_probabilities_pow)?; + calculate_cumulative_distribution_in_place(&mut iter_probabilities_pow)?; + metadata.iter_probabilities_pow_cumulative = iter_probabilities_pow; Ok(()) } @@ -353,15 +329,15 @@ where /// Setting the probabilities will remove the value set through `set_mutation_ids`. pub fn set_mutation_probabilities( state: &mut S, - mutation_probabilities: Vec, + mut mutation_probabilities: Vec, ) -> Result<(), Error> { let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap(); metadata.mutation_ids.clear(); metadata.next_id = 0.into(); // we precalculate the cumulative probability to be faster when sampling later. - metadata.mutation_probabilities_cumulative = - calculate_cumulative_distribution(mutation_probabilities)?; + calculate_cumulative_distribution_in_place(&mut mutation_probabilities)?; + metadata.mutation_probabilities_cumulative = mutation_probabilities; Ok(()) } @@ -395,8 +371,7 @@ mod test { use libafl_bolts::tuples::tuple_list; use super::{ - calculate_cumulative_distribution, BitFlipMutator, ByteDecMutator, - TuneableScheduledMutator, TuneableScheduledMutatorMetadata, + BitFlipMutator, ByteDecMutator, TuneableScheduledMutator, TuneableScheduledMutatorMetadata, }; use crate::{ inputs::BytesInput, @@ -406,6 +381,11 @@ mod test { #[test] fn test_tuning() { + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + unsafe { + TuneableScheduledMutatorMetadata::register(); + } + let mut state: NopState = NopState::new(); let mutators = tuple_list!( BitFlipMutator::new(), @@ -422,29 +402,13 @@ mod test { assert_eq!(tuneable.schedule(&mut state, &input), 1.into()); } - #[test] - fn get_cdf_fails_with_invalid_probs() { - // Distribution has to sum up to 1. - assert!(calculate_cumulative_distribution(vec![]).is_err()); - assert!(calculate_cumulative_distribution(vec![0.0]).is_err()); - assert!(calculate_cumulative_distribution(vec![0.9]).is_err()); - assert!(calculate_cumulative_distribution(vec![0.9, 0.9]).is_err()); - assert!(calculate_cumulative_distribution(vec![f32::NAN]).is_err()); - assert!(calculate_cumulative_distribution(vec![f32::INFINITY]).is_err()); - assert!(calculate_cumulative_distribution(vec![f32::NEG_INFINITY]).is_err()); - - // Elements have to be between 0 and 1 - assert!(calculate_cumulative_distribution(vec![-0.5, 0.5, 0.5]).is_err()); - - assert!(calculate_cumulative_distribution(vec![1.0]).is_ok()); - assert!(calculate_cumulative_distribution(vec![0.0, 1.0]).is_ok()); - assert!(calculate_cumulative_distribution(vec![0.0, 1.0, 0.0]).is_ok()); - assert!(calculate_cumulative_distribution(vec![0.5, 0.5]).is_ok()); - assert!(calculate_cumulative_distribution(vec![0.2; 5]).is_ok()); - } - #[test] fn test_mutation_distribution() { + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + unsafe { + TuneableScheduledMutatorMetadata::register(); + } + let mut state: NopState = NopState::new(); let mutators = tuple_list!( BitFlipMutator::new(), diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index eca533190d..f97566d91f 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -55,6 +55,10 @@ impl CmpValues { /// A state metadata holding a list of values logged from comparisons #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct CmpValuesMetadata { /// A `list` of values. #[serde(skip)] @@ -562,6 +566,10 @@ where /// A state metadata holding a list of values logged from comparisons. AFL++ RQ version. #[derive(Debug, Default, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct AFLppCmpValuesMetadata { /// The first map of AFLppCmpVals retrieved by running the un-mutated input #[serde(skip)] diff --git a/libafl/src/schedulers/accounting.rs b/libafl/src/schedulers/accounting.rs index cd9d86ee5b..e6778cbce0 100644 --- a/libafl/src/schedulers/accounting.rs +++ b/libafl/src/schedulers/accounting.rs @@ -22,6 +22,10 @@ use crate::{ /// A testcase metadata holding a list of indexes of a map #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct AccountingIndexesMetadata { /// The list of indexes. pub list: Vec, @@ -73,6 +77,10 @@ impl AccountingIndexesMetadata { /// A state metadata holding a map of favoreds testcases for each map entry #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct TopAccountingMetadata { /// map index -> corpus index pub map: HashMap, diff --git a/libafl/src/schedulers/ecofuzz.rs b/libafl/src/schedulers/ecofuzz.rs index de897bca1a..07d789010b 100644 --- a/libafl/src/schedulers/ecofuzz.rs +++ b/libafl/src/schedulers/ecofuzz.rs @@ -3,6 +3,7 @@ use alloc::string::{String, ToString}; use core::marker::PhantomData; +use libafl_bolts::math::integer_sqrt; use serde::{Deserialize, Serialize}; use crate::{ @@ -14,16 +15,6 @@ use crate::{ Error, }; -fn integer_sqrt(val: u64) -> u64 { - let mut i = 0; - let mut r = 0; - while r <= val { - r = i * i; - i += 1; - } - i - 1 -} - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Copy, Default)] /// The state of the `EcoFuzz` scheduling algorithm pub enum EcoState { @@ -38,6 +29,10 @@ pub enum EcoState { /// The testcase Metadata for `EcoScheduler` #[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct EcoTestcaseMetadata { mutation_num: u64, exec_num: u64, @@ -53,6 +48,10 @@ libafl_bolts::impl_serdeany!(EcoTestcaseMetadata); /// The state Metadata for `EcoScheduler` #[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct EcoMetadata { state: EcoState, initial_corpus_count: Option, @@ -283,7 +282,7 @@ where let tcmeta = tc.metadata_mut::()?; tcmeta.exec_num = exec_num; - tcmeta.serial = state.corpus().count() as u64 + 1; + tcmeta.serial = (state.corpus().count() as u64).saturating_add(1); Ok(()) } diff --git a/libafl/src/schedulers/minimizer.rs b/libafl/src/schedulers/minimizer.rs index d055a55c1c..295c0a9a62 100644 --- a/libafl/src/schedulers/minimizer.rs +++ b/libafl/src/schedulers/minimizer.rs @@ -23,12 +23,20 @@ pub const DEFAULT_SKIP_NON_FAVORED_PROB: u64 = 95; /// A testcase metadata saying if a testcase is favored #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct IsFavoredMetadata {} libafl_bolts::impl_serdeany!(IsFavoredMetadata); /// A state metadata holding a map of favoreds testcases for each map entry #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct TopRatedsMetadata { /// map index -> corpus index pub map: HashMap, diff --git a/libafl/src/schedulers/powersched.rs b/libafl/src/schedulers/powersched.rs index c57b30a911..2d8edd17f9 100644 --- a/libafl/src/schedulers/powersched.rs +++ b/libafl/src/schedulers/powersched.rs @@ -24,6 +24,10 @@ libafl_bolts::impl_serdeany!(SchedulerMetadata); /// The metadata used for power schedules #[derive(Serialize, Deserialize, Clone, Debug)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct SchedulerMetadata { /// Powerschedule strategy strat: Option, diff --git a/libafl/src/schedulers/probabilistic_sampling.rs b/libafl/src/schedulers/probabilistic_sampling.rs index 44dd8335a4..8de52a80f7 100644 --- a/libafl/src/schedulers/probabilistic_sampling.rs +++ b/libafl/src/schedulers/probabilistic_sampling.rs @@ -27,6 +27,10 @@ where /// A state metadata holding a map of probability of corpus elements. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct ProbabilityMetadata { /// corpus index -> probability pub map: HashMap, diff --git a/libafl/src/schedulers/tuneable.rs b/libafl/src/schedulers/tuneable.rs index 96aca13819..a7f4bc19ee 100644 --- a/libafl/src/schedulers/tuneable.rs +++ b/libafl/src/schedulers/tuneable.rs @@ -18,6 +18,10 @@ use crate::{ }; #[derive(Default, Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny struct TuneableSchedulerMetadata { next: Option, } diff --git a/libafl/src/schedulers/weighted.rs b/libafl/src/schedulers/weighted.rs index 34d4b5f011..7d5b88d44f 100644 --- a/libafl/src/schedulers/weighted.rs +++ b/libafl/src/schedulers/weighted.rs @@ -24,9 +24,12 @@ use crate::{ Error, }; -#[derive(Serialize, Deserialize, Clone, Debug)] - /// The Metadata for `WeightedScheduler` +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct WeightedScheduleMetadata { /// The fuzzer execution spent in the current cycles runs_in_current_cycle: usize, diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index 95411f252f..741a725adb 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -7,7 +7,7 @@ use alloc::{ use core::{fmt::Debug, marker::PhantomData, time::Duration}; use hashbrown::HashSet; -use libafl_bolts::{current_time, AsIter, Named}; +use libafl_bolts::{current_time, impl_serdeany, AsIter, Named}; use num_traits::Bounded; use serde::{Deserialize, Serialize}; @@ -26,15 +26,19 @@ use crate::{ Error, }; -libafl_bolts::impl_serdeany!(UnstableEntriesMetadata); /// The metadata to keep unstable entries /// In libafl, the stability is the number of the unstable entries divided by the size of the map /// This is different from AFL++, which shows the number of the unstable entries divided by the number of filled entries. +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Serialize, Deserialize, Clone, Debug)] pub struct UnstableEntriesMetadata { unstable_entries: HashSet, map_len: usize, } +impl_serdeany!(UnstableEntriesMetadata); impl UnstableEntriesMetadata { #[must_use] diff --git a/libafl/src/stages/colorization.rs b/libafl/src/stages/colorization.rs index 4a631d9fd9..8ac46e0f4f 100644 --- a/libafl/src/stages/colorization.rs +++ b/libafl/src/stages/colorization.rs @@ -101,8 +101,12 @@ where } } -#[derive(Debug, Serialize, Deserialize)] /// Store the taint and the input +#[derive(Debug, Serialize, Deserialize)] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny pub struct TaintMetadata { input_vec: Vec, ranges: Vec>, diff --git a/libafl/src/stages/dump.rs b/libafl/src/stages/dump.rs index 89f7b63d46..a007b66923 100644 --- a/libafl/src/stages/dump.rs +++ b/libafl/src/stages/dump.rs @@ -16,6 +16,10 @@ use crate::{ }; /// Metadata used to store information about disk dump indexes for names +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Default, Serialize, Deserialize, Clone, Debug)] pub struct DumpToDiskMetadata { last_corpus: Option, diff --git a/libafl/src/stages/generalization.rs b/libafl/src/stages/generalization.rs index 1ef4d25509..1378ecd28d 100644 --- a/libafl/src/stages/generalization.rs +++ b/libafl/src/stages/generalization.rs @@ -25,7 +25,7 @@ use crate::{ const MAX_GENERALIZED_LEN: usize = 8192; -fn increment_by_offset(_list: &[Option], idx: usize, off: u8) -> usize { +const fn increment_by_offset(_list: &[Option], idx: usize, off: u8) -> usize { idx + 1 + off as usize } diff --git a/libafl/src/stages/sync.rs b/libafl/src/stages/sync.rs index cc413d7fe9..f8e3c4acca 100644 --- a/libafl/src/stages/sync.rs +++ b/libafl/src/stages/sync.rs @@ -22,6 +22,10 @@ use crate::{ }; /// Metadata used to store information about disk sync time +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Serialize, Deserialize, Debug)] pub struct SyncFromDiskMetadata { /// The last time the sync was done @@ -191,6 +195,10 @@ where } /// Metadata used to store information about the last sent testcase with `SyncFromBrokerStage` +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Serialize, Deserialize, Debug)] pub struct SyncFromBrokerMetadata { /// The `CorpusId` of the last sent testcase diff --git a/libafl/src/stages/tuneable.rs b/libafl/src/stages/tuneable.rs index 30bd46a6a8..39d8f4859c 100644 --- a/libafl/src/stages/tuneable.rs +++ b/libafl/src/stages/tuneable.rs @@ -20,6 +20,10 @@ use crate::{ Error, Evaluator, }; +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Default, Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)] struct TuneableMutationalStageMetadata { iters: Option, diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index 837738382b..dd0ef89004 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -75,6 +75,21 @@ Welcome to `LibAFL` #![allow(clippy::borrow_as_ptr)] #![allow(clippy::borrow_deref_ref)] +/// We need some sort of "[`String`]" for errors in `no_alloc`... +/// We can only support `'static` without allocator, so let's do that. +#[cfg(not(feature = "alloc"))] +type String = &'static str; + +/// We also need a non-allocating format... +/// This one simply returns the `fmt` string. +/// Good enough for simple errors, for anything else, use the `alloc` feature. +#[cfg(not(feature = "alloc"))] +macro_rules! format { + ($fmt:literal) => {{ + $fmt + }}; +} + #[cfg(feature = "std")] #[macro_use] extern crate std; @@ -85,6 +100,55 @@ pub extern crate alloc; #[cfg(feature = "ctor")] #[doc(hidden)] pub use ctor::ctor; +#[cfg(feature = "alloc")] +pub mod anymap; +#[cfg(feature = "std")] +pub mod build_id; +#[cfg(all( + any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"), + feature = "std" +))] +pub mod cli; +#[cfg(feature = "gzip")] +pub mod compress; +#[cfg(feature = "std")] +pub mod core_affinity; +pub mod cpu; +#[cfg(feature = "std")] +pub mod fs; +#[cfg(feature = "alloc")] +pub mod llmp; +pub mod math; +#[cfg(all(feature = "std", unix))] +pub mod minibsod; +pub mod os; +#[cfg(feature = "alloc")] +pub mod ownedref; +pub mod rands; +#[cfg(feature = "alloc")] +pub mod serdeany; +pub mod shmem; +#[cfg(feature = "std")] +pub mod staterestore; +pub mod tuples; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::{iter::Iterator, time}; +#[cfg(feature = "std")] +use std::time::{SystemTime, UNIX_EPOCH}; + +use serde::{Deserialize, Serialize}; + +/// The client ID == the sender id. +#[repr(transparent)] +#[derive( + Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, +)] +pub struct ClientId(pub u32); + +#[cfg(feature = "std")] +use log::{Metadata, Record}; #[deprecated( since = "0.11.0", @@ -112,21 +176,6 @@ use std::{env::VarError, io}; #[cfg(feature = "libafl_derive")] pub use libafl_derive::SerdeAny; -/// We need some sort of "[`String`]" for errors in `no_alloc`... -/// We can only support `'static` without allocator, so let's do that. -#[cfg(not(feature = "alloc"))] -type String = &'static str; - -/// We also need a non-allocating format... -/// This one simply returns the `fmt` string. -/// Good enough for simple errors, for anything else, use the `alloc` feature. -#[cfg(not(feature = "alloc"))] -macro_rules! format { - ($fmt:literal) => {{ - $fmt - }}; -} - /// We need fixed names for many parts of this lib. pub trait Named { /// Provide the name of this element. @@ -467,55 +516,6 @@ pub unsafe extern "C" fn external_current_millis() -> u64 { 1000 } -#[cfg(feature = "alloc")] -pub mod anymap; -#[cfg(feature = "std")] -pub mod build_id; -#[cfg(all( - any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"), - feature = "std" -))] -pub mod cli; -#[cfg(feature = "gzip")] -pub mod compress; -#[cfg(feature = "std")] -pub mod core_affinity; -pub mod cpu; -#[cfg(feature = "std")] -pub mod fs; -#[cfg(feature = "alloc")] -pub mod llmp; -#[cfg(all(feature = "std", unix))] -pub mod minibsod; -pub mod os; -#[cfg(feature = "alloc")] -pub mod ownedref; -pub mod rands; -#[cfg(feature = "alloc")] -pub mod serdeany; -pub mod shmem; -#[cfg(feature = "std")] -pub mod staterestore; -pub mod tuples; - -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use core::{iter::Iterator, ops::AddAssign, time}; -#[cfg(feature = "std")] -use std::time::{SystemTime, UNIX_EPOCH}; - -use serde::{Deserialize, Serialize}; - -/// The client ID == the sender id. -#[repr(transparent)] -#[derive( - Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, -)] -pub struct ClientId(pub u32); - -#[cfg(feature = "std")] -use log::{Metadata, Record}; - /// Can be converted to a slice pub trait AsSlice { /// Type of the entries in this slice @@ -656,23 +656,6 @@ pub fn current_time() -> time::Duration { time::Duration::from_millis(millis) } -/// Given a u64 number, return a hashed number using this mixing function -/// This function is used to hash an address into a more random number (used in `libafl_frida`). -/// Mixing function: -#[inline] -#[must_use] -pub fn xxh3_rrmxmx_mixer(v: u64) -> u64 { - let tmp = (v >> 32) + ((v & 0xffffffff) << 32); - let bitflip = 0x1cad21f72c81017c ^ 0xdb979082e96dd4de; - let mut h64 = tmp ^ bitflip; - h64 = h64.rotate_left(49) & h64.rotate_left(24); - h64 = h64.wrapping_mul(0x9FB21C651E98DF25); - h64 ^= (h64 >> 35) + 8; - h64 = h64.wrapping_mul(0x9FB21C651E98DF25); - h64 ^= h64 >> 28; - h64 -} - /// Gets current nanoseconds since [`UNIX_EPOCH`] #[must_use] #[inline] @@ -695,30 +678,6 @@ pub fn format_duration_hms(duration: &time::Duration) -> String { format!("{}h-{}m-{}s", (secs / 60) / 60, (secs / 60) % 60, secs % 60) } -/// Calculates the cumulative sum for a slice, in-place. -/// The values are useful for example for cumulative probabilities. -/// -/// So, to give an example: -/// ```rust -/// # extern crate libafl_bolts; -/// use libafl_bolts::calculate_cumulative_sum_in_place; -/// -/// let mut value = [2, 4, 1, 3]; -/// calculate_cumulative_sum_in_place(&mut value); -/// assert_eq!(&[2, 6, 7, 10], &value); -/// ``` -pub fn calculate_cumulative_sum_in_place(mut_slice: &mut [T]) -where - T: Default + AddAssign + Copy, -{ - let mut acc = T::default(); - - for val in mut_slice { - acc += *val; - *val = acc; - } -} - /// Stderr logger #[cfg(feature = "std")] pub static LIBAFL_STDERR_LOGGER: SimpleStderrLogger = SimpleStderrLogger::new(); diff --git a/libafl_bolts/src/llmp.rs b/libafl_bolts/src/llmp.rs index b8b352730e..ab4f931a6b 100644 --- a/libafl_bolts/src/llmp.rs +++ b/libafl_bolts/src/llmp.rs @@ -521,6 +521,9 @@ fn next_shmem_size(max_alloc: usize) -> usize { /// Initialize a new `llmp_page`. The size should be relative to /// `llmp_page->messages` +/// +/// # Safety +/// Will write to the raw SHM page header, should be safe for correct [`ShMem`] implementations unsafe fn llmp_page_init(shmem: &mut SHM, sender_id: ClientId, allow_reinit: bool) { #[cfg(feature = "llmp_debug")] log::trace!("llmp_page_init: shmem {:?}", &shmem); @@ -551,6 +554,9 @@ unsafe fn llmp_page_init(shmem: &mut SHM, sender_id: ClientId, allow } /// Get the next pointer and make sure it's in the current page, and has enough space. +/// +/// # Safety +/// Will dereference `last_msg` #[inline] unsafe fn llmp_next_msg_ptr_checked( map: &mut LlmpSharedMap, @@ -576,6 +582,9 @@ unsafe fn llmp_next_msg_ptr_checked( /// Pointer to the message behind the last message /// The messages are padded, so accesses will be aligned properly. +/// +/// # Safety +/// Will dereference the `last_msg` ptr #[inline] #[allow(clippy::cast_ptr_alignment)] unsafe fn _llmp_next_msg_ptr(last_msg: *const LlmpMsg) -> *mut LlmpMsg { diff --git a/libafl_bolts/src/math.rs b/libafl_bolts/src/math.rs new file mode 100644 index 0000000000..dec9a2f3d8 --- /dev/null +++ b/libafl_bolts/src/math.rs @@ -0,0 +1,145 @@ +//! Math-related functions that we commonly (or at least sometimes) need + +use core::ops::AddAssign; + +use crate::Error; + +/// Returns the cumulative distribution function for a discrete distribution. +pub fn calculate_cumulative_distribution_in_place(probabilities: &mut [f32]) -> Result<(), Error> { + if probabilities.is_empty() { + return Err(Error::illegal_argument("empty list of probabilities")); + } + + if !probabilities.iter().all(|&p| (0.0..=1.0).contains(&p)) { + return Err(Error::illegal_argument(format!( + "invalid probability distribution: {probabilities:?}" + ))); + } + + let cumulative = probabilities; + calculate_cumulative_sum_in_place(cumulative); + + // The cumulative sum should be roughly equal to 1. + let last = cumulative.last_mut().unwrap(); + let offset_to_1 = *last - 1.0_f32; + if offset_to_1.is_nan() || offset_to_1 > 1.0e-4 || -offset_to_1 > 1.0e-4 { + return Err(Error::illegal_argument(format!( + "sum of probabilities ({last}) is not 1" + ))); + } + + // Clamp the end of the vector to account for floating point errors. + *last = 1.0_f32; + + Ok(()) +} + +/// Calculates the integer square root using binary search +/// Algorithm from +/// . +#[must_use] +pub const fn integer_sqrt(val: u64) -> u64 { + if val == u64::MAX { + return 2_u64.pow(32) - 1; + } + let mut ret = 0; + let mut i = val + 1; + let mut m; + + while ret != i - 1 { + m = (ret + i) / 2; + + if m.saturating_mul(m) <= val { + ret = m; + } else { + i = m; + } + } + + ret +} + +/// Given a u64 number, return a hashed number using this mixing function +/// This function is used to hash an address into a more random number (used in `libafl_frida`). +/// Mixing function: +#[inline] +#[must_use] +pub const fn xxh3_rrmxmx_mixer(v: u64) -> u64 { + let tmp = (v >> 32) + ((v & 0xffffffff) << 32); + let bitflip = 0x1cad21f72c81017c ^ 0xdb979082e96dd4de; + let mut h64 = tmp ^ bitflip; + h64 = h64.rotate_left(49) & h64.rotate_left(24); + h64 = h64.wrapping_mul(0x9FB21C651E98DF25); + h64 ^= (h64 >> 35) + 8; + h64 = h64.wrapping_mul(0x9FB21C651E98DF25); + h64 ^= h64 >> 28; + h64 +} + +/// Calculates the cumulative sum for a slice, in-place. +/// The values are useful for example for cumulative probabilities. +/// +/// So, to give an example: +/// ```rust +/// # extern crate libafl_bolts; +/// use libafl_bolts::math::calculate_cumulative_sum_in_place; +/// +/// let mut value = [2, 4, 1, 3]; +/// calculate_cumulative_sum_in_place(&mut value); +/// assert_eq!(&[2, 6, 7, 10], &value); +/// ``` +pub fn calculate_cumulative_sum_in_place(mut_slice: &mut [T]) +where + T: Default + AddAssign + Copy, +{ + let mut acc = T::default(); + + for val in mut_slice { + acc += *val; + *val = acc; + } +} + +#[cfg(test)] +mod test { + use super::{calculate_cumulative_distribution_in_place, integer_sqrt}; + + #[test] + fn test_integer_sqrt() { + assert_eq!(0, integer_sqrt(0)); + assert_eq!(1, integer_sqrt(1)); + assert_eq!(2, integer_sqrt(4)); + assert_eq!(10, integer_sqrt(120)); + assert_eq!(11, integer_sqrt(121)); + assert_eq!(11, integer_sqrt(128)); + assert_eq!(2_u64.pow(16) - 1, integer_sqrt(u64::from(u32::MAX))); + assert_eq!(2_u64.pow(32) - 1, integer_sqrt(u64::MAX)); + assert_eq!(2_u64.pow(32) - 1, integer_sqrt(u64::MAX - 1)); + assert_eq!(128, integer_sqrt(128 * 128)); + assert_eq!(128, integer_sqrt((128 * 128) + 1)); + assert_eq!(128, integer_sqrt((128 * 128) + 127)); + assert_eq!(128, integer_sqrt((128 * 128) + 127)); + assert_eq!(999999, integer_sqrt((999999 * 999999) + 9)); + } + + #[test] + fn get_cdf_fails_with_invalid_probs() { + // Distribution has to sum up to 1. + assert!(calculate_cumulative_distribution_in_place(&mut []).is_err()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.0]).is_err()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.9]).is_err()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.9, 0.9]).is_err()); + assert!(calculate_cumulative_distribution_in_place(&mut [f32::NAN]).is_err()); + assert!(calculate_cumulative_distribution_in_place(&mut [f32::INFINITY]).is_err()); + assert!(calculate_cumulative_distribution_in_place(&mut [f32::NEG_INFINITY]).is_err()); + + // Elements have to be between 0 and 1 + assert!(calculate_cumulative_distribution_in_place(&mut [-0.5, 0.5, 0.5]).is_err()); + + assert!(calculate_cumulative_distribution_in_place(&mut [1.0]).is_ok()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.0, 1.0]).is_ok()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.0, 1.0, 0.0]).is_ok()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.5, 0.5]).is_ok()); + assert!(calculate_cumulative_distribution_in_place(&mut [0.2; 5]).is_ok()); + } +} diff --git a/libafl_bolts/src/serdeany.rs b/libafl_bolts/src/serdeany.rs index 8050dd7a17..f63e4f850e 100644 --- a/libafl_bolts/src/serdeany.rs +++ b/libafl_bolts/src/serdeany.rs @@ -618,15 +618,6 @@ pub use serdeany_registry::*; macro_rules! create_register { ($struct_type:ty) => { const _: () = { - /// Manually register this type at a later point in time - /// - /// # Safety - /// This may never be called concurrently as it dereferences the `RegistryBuilder` without acquiring a lock. - #[cfg(not(feature = "serdeany_autoreg"))] - pub unsafe fn register() { - $crate::serdeany::RegistryBuilder::register::<$struct_type>(); - } - /// Automatically register this type #[cfg(feature = "serdeany_autoreg")] #[$crate::ctor] @@ -665,6 +656,18 @@ macro_rules! impl_serdeany { } } + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + impl< $( $lt $( : $clt $(+ $dlt )* )? ),+ > $struct_name < $( $lt ),+ > { + + /// Manually register this type at a later point in time + /// + /// # Safety + /// This may never be called concurrently as it dereferences the `RegistryBuilder` without acquiring a lock. + pub unsafe fn register() { + $crate::serdeany::RegistryBuilder::register::<$struct_name < $( $lt ),+ >>(); + } + } + $( $crate::create_register!($struct_name < $( $opt ),+ >); )* @@ -690,6 +693,17 @@ macro_rules! impl_serdeany { } } + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + impl $struct_name { + /// Manually register this type at a later point in time + /// + /// # Safety + /// This may never be called concurrently as it dereferences the `RegistryBuilder` without acquiring a lock. + pub unsafe fn register() { + $crate::serdeany::RegistryBuilder::register::<$struct_name>(); + } + } + $crate::create_register!($struct_name); }; } diff --git a/libafl_bolts/src/shmem.rs b/libafl_bolts/src/shmem.rs index b8346c3afd..cd346f12b6 100644 --- a/libafl_bolts/src/shmem.rs +++ b/libafl_bolts/src/shmem.rs @@ -141,13 +141,13 @@ impl ShMemId { /// Returns `true` if this `ShMemId` has an empty backing slice. /// If this is the case something went wrong, and this `ShMemId` may not be read from. #[must_use] - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.id[0] == 0 } /// Get the id as a fixed-length slice #[must_use] - pub fn as_array(&self) -> &[u8; 20] { + pub const fn as_array(&self) -> &[u8; 20] { &self.id } diff --git a/libafl_frida/src/coverage_rt.rs b/libafl_frida/src/coverage_rt.rs index 1312bda3f2..b16b45b1a4 100644 --- a/libafl_frida/src/coverage_rt.rs +++ b/libafl_frida/src/coverage_rt.rs @@ -6,7 +6,7 @@ use std::{cell::RefCell, marker::PhantomPinned, pin::Pin, rc::Rc}; use dynasmrt::DynasmLabelApi; use dynasmrt::{dynasm, DynasmApi}; use frida_gum::{instruction_writer::InstructionWriter, stalker::StalkerOutput}; -use libafl_bolts::xxh3_rrmxmx_mixer; +use libafl_bolts::math::xxh3_rrmxmx_mixer; use rangemap::RangeMap; use crate::helper::FridaRuntime; diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index 67e7d82e7f..e6df0c2a4a 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -12,6 +12,10 @@ use crate::{ GuestAddr, }; +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuCmpsMapMetadata { pub map: HashMap, diff --git a/libafl_qemu/src/drcov.rs b/libafl_qemu/src/drcov.rs index 6bcdf8831c..fdf04dbb6e 100644 --- a/libafl_qemu/src/drcov.rs +++ b/libafl_qemu/src/drcov.rs @@ -19,6 +19,10 @@ static DRCOV_IDS: Mutex>> = Mutex::new(None); static DRCOV_MAP: Mutex>> = Mutex::new(None); static DRCOV_LENGTHS: Mutex>> = Mutex::new(None); +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuDrCovMetadata { pub current_id: u64, diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index d9a735e64f..90b39568c4 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -14,6 +14,10 @@ use crate::{ hooks::QemuHooks, }; +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuEdgesMapMetadata { pub map: HashMap<(GuestAddr, GuestAddr), u64>,