Add bloom filter for duplicate execution of the same inputs (#2771)

* fixing empty multipart name

* fixing clippy

* New rules for the contributing (#2752)

* Rules

* more

* aa

* Improve Flexibility of DumpToDiskStage (#2753)

* fixing empty multipart name

* fixing clippy

* improve flexibility of DumpToDiskStage

* adding note to MIGRATION.md

* Update bindgen requirement from 0.70.1 to 0.71.1 (#2756)

Updates the requirements on [bindgen](https://github.com/rust-lang/rust-bindgen) to permit the latest version.
- [Release notes](https://github.com/rust-lang/rust-bindgen/releases)
- [Changelog](https://github.com/rust-lang/rust-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/rust-bindgen/compare/v0.70.1...v0.71.1)

---
updated-dependencies:
- dependency-name: bindgen
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* No Use* from stages (#2745)

* no from stage

* fixer

* doc fix

* how was this working????

* more fixes

* delete more

* rq

* cargo-fuzz

* m

* aa

* Update CONTRIBUTING.md MIGRATION.md (#2762)

* No Uses* from `fuzzer` (#2761)

* go

* fixing stuf

* hello from windows

* more

* lolg

* lolf

* fix

* a

---------

Co-authored-by: Your Name <you@example.com>

* Remove useless cfgs (#2764)

* Link libresolv on all Apple OSs (#2767)

* Somewhat ugly CI fix... (#2768)

* Maybe fix CI

* does this help?

* Very dirty 'fix'

* Add Input Types and Mutators for Numeric Types (#2760)

* fixing empty multipart name

* fixing clippy

* New rules for the contributing (#2752)

* Rules

* more

* aa

* Improve Flexibility of DumpToDiskStage (#2753)

* fixing empty multipart name

* fixing clippy

* improve flexibility of DumpToDiskStage

* adding note to MIGRATION.md

* Introduce WrappingMutator

* introducing mutators for int types

* fixing no_std

* random fixes

* Add hash derivation for WrappingInput

* Revert fixes that broke things

* Derive Default on WrappingInput

* Add unit tests

* Fixes according to code review

* introduce mappable ValueInputs

* remove unnecessary comments

* Elide more lifetimes

* remove dead code

* simplify hashing

* improve docs

* improve randomization

* rename method to align with standard library

* add typedefs for int types for ValueMutRefInput

* rename test

* add safety notice to trait function

* improve randomize performance for i128/u128

* rename macro

* improve comment

* actually check return values in test

* make 128 bit int randomize even more efficient

* shifting signed values

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>

* Add HashMutator

* Fix docs

* Fix docs again

* introducing bloom filter

* fix tests

* Implement evaluate_filtered

* Add macros to libafl_bolts tuples for mapping and merging types (#2788)

* Add macros

* Use the macros for havoc_mutations

* Fix docs

* improve merge_tuple_list_type to accept n items

* libafl_cc: Automatically find llvm_ar path (#2790)

* imemory_ondisk: Don't fail write under any circumstances if locking is disabled (#2791)

* imemory_ondisk: Don't fail write under any circumstances if locking is disabled

* fmt

* inmemory_ondisk: Add a log message on failure

* clippy'

* micro optimization

* Revert changes to global Cargo.toml

* Hide std-dependent dependency behind std feature

* Fix example fuzzer

* Rename constructor for filtered fuzzer

* Reorder generics alphabetically

* Rename HashingMutator, add note to MutationResult about filtered fuzzers

* Improve StdFuzzer according to feedback

* rename hashing mutator

* Fix english in comment

* Cleanup of old PRs that break the CI

* Fix more CI bugs

* Code cleanup

* Remove unnecessary comments

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Your Name <you@example.com>
Co-authored-by: Sharad Khanna <sharad@mineo333.dev>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: s1341 <s1341@users.noreply.github.com>
This commit is contained in:
Valentin Huber 2024-12-28 15:22:31 +01:00 committed by GitHub
parent 2a79ee5b4f
commit d8ec991b48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 298 additions and 47 deletions

View File

@ -8,8 +8,9 @@ authors = [
edition = "2021"
[features]
default = ["std"]
default = ["std", "bloom_input_filter"]
tui = ["libafl/tui_monitor"]
bloom_input_filter = ["std"]
std = []
[profile.dev]

View File

@ -134,7 +134,11 @@ pub fn main() {
let scheduler = QueueScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
#[cfg(not(feature = "bloom_input_filter"))]
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
#[cfg(feature = "bloom_input_filter")]
let mut fuzzer =
StdFuzzer::with_bloom_input_filter(scheduler, feedback, objective, 10_000_000, 0.001);
// Create the executor for an in-process function with just one observer
let executor = CustomExecutor::new(&state);

View File

@ -58,6 +58,7 @@ std = [
"serial_test",
"libafl_bolts/std",
"typed-builder",
"fastbloom",
]
## Tracks the Feedbacks and the Objectives that were interesting for a Testcase
@ -291,6 +292,7 @@ document-features = { workspace = true, optional = true }
clap = { workspace = true, optional = true }
num_enum = { workspace = true, optional = true }
libipt = { workspace = true, optional = true }
fastbloom = { version = "0.8.0", optional = true }
[lints]
workspace = true

View File

@ -557,7 +557,7 @@ mod tests {
let mut mgr = NopEventManager::new();
let mut state =
StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap();
let mut fuzzer = StdFuzzer::<_, _, _>::new(sche, feedback, objective);
let mut fuzzer = StdFuzzer::new(sche, feedback, objective);
let mut in_process_executor = InProcessExecutor::new(
&mut harness,

View File

@ -2,7 +2,11 @@
use alloc::{string::ToString, vec::Vec};
use core::{fmt::Debug, time::Duration};
#[cfg(feature = "std")]
use std::hash::Hash;
#[cfg(feature = "std")]
use fastbloom::BloomFilter;
use libafl_bolts::{current_time, tuples::MatchName};
use serde::Serialize;
@ -138,6 +142,16 @@ pub trait EvaluatorObservers<E, EM, I, S> {
/// Evaluate an input modifying the state of the fuzzer
pub trait Evaluator<E, EM, I, S> {
/// Runs the input if it was (likely) not previously run and triggers observers and feedback and adds the input to the previously executed list
/// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus
fn evaluate_filtered(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: I,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error>;
/// Runs the input and triggers observers and feedback,
/// returns if is interesting an (option) the index of the new [`crate::corpus::Testcase`] in the corpus
fn evaluate_input(
@ -242,13 +256,14 @@ pub enum ExecuteInputResult {
/// Your default fuzzer instance, for everyday use.
#[derive(Debug)]
pub struct StdFuzzer<CS, F, OF> {
pub struct StdFuzzer<CS, F, IF, OF> {
scheduler: CS,
feedback: F,
objective: OF,
input_filter: IF,
}
impl<CS, F, OF, S> HasScheduler<<S::Corpus as Corpus>::Input, S> for StdFuzzer<CS, F, OF>
impl<CS, F, IF, OF, S> HasScheduler<<S::Corpus as Corpus>::Input, S> for StdFuzzer<CS, F, IF, OF>
where
S: HasCorpus,
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
@ -264,7 +279,7 @@ where
}
}
impl<CS, F, OF> HasFeedback for StdFuzzer<CS, F, OF> {
impl<CS, F, IF, OF> HasFeedback for StdFuzzer<CS, F, IF, OF> {
type Feedback = F;
fn feedback(&self) -> &Self::Feedback {
@ -276,7 +291,7 @@ impl<CS, F, OF> HasFeedback for StdFuzzer<CS, F, OF> {
}
}
impl<CS, F, OF> HasObjective for StdFuzzer<CS, F, OF> {
impl<CS, F, IF, OF> HasObjective for StdFuzzer<CS, F, IF, OF> {
type Objective = OF;
fn objective(&self) -> &OF {
@ -288,8 +303,8 @@ impl<CS, F, OF> HasObjective for StdFuzzer<CS, F, OF> {
}
}
impl<CS, EM, F, OF, OT, S> ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>
for StdFuzzer<CS, F, OF>
impl<CS, EM, F, IF, OF, OT, S> ExecutionProcessor<EM, <S::Corpus as Corpus>::Input, OT, S>
for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
EM: EventFirer<State = S>,
@ -494,8 +509,8 @@ where
}
}
impl<CS, E, EM, F, OF, S> EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, OF>
impl<CS, E, EM, F, IF, OF, S> EvaluatorObservers<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + Executor<EM, Self, State = S>,
@ -532,7 +547,48 @@ where
}
}
impl<CS, E, EM, F, OF, S> Evaluator<E, EM, <S::Corpus as Corpus>::Input, S> for StdFuzzer<CS, F, OF>
trait InputFilter<I> {
fn should_execute(&mut self, input: &I) -> bool;
}
/// A pseudo-filter that will execute each input.
#[derive(Debug)]
pub struct NopInputFilter;
impl<I> InputFilter<I> for NopInputFilter {
#[inline]
#[must_use]
fn should_execute(&mut self, _input: &I) -> bool {
true
}
}
/// A filter that probabilistically prevents duplicate execution of the same input based on a bloom filter.
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct BloomInputFilter {
bloom: BloomFilter,
}
#[cfg(feature = "std")]
impl BloomInputFilter {
#[must_use]
fn new(items_count: usize, fp_p: f64) -> Self {
let bloom = BloomFilter::with_false_pos(fp_p).expected_items(items_count);
Self { bloom }
}
}
#[cfg(feature = "std")]
impl<I: Hash> InputFilter<I> for BloomInputFilter {
#[inline]
#[must_use]
fn should_execute(&mut self, input: &I) -> bool {
!self.bloom.insert(input)
}
}
impl<CS, E, EM, F, IF, OF, S> Evaluator<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
E: HasObservers + Executor<EM, Self, State = S>,
@ -549,7 +605,22 @@ where
+ UsesInput<Input = <S::Corpus as Corpus>::Input>,
<S::Corpus as Corpus>::Input: Input,
S::Solutions: Corpus<Input = <S::Corpus as Corpus>::Input>,
IF: InputFilter<<S::Corpus as Corpus>::Input>,
{
fn evaluate_filtered(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
input: <S::Corpus as Corpus>::Input,
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
if self.input_filter.should_execute(&input) {
self.evaluate_input(state, executor, manager, input)
} else {
Ok((ExecuteInputResult::None, None))
}
}
/// Process one input, adding to the respective corpora if needed and firing the right events
#[inline]
fn evaluate_input_events(
@ -562,6 +633,7 @@ where
) -> Result<(ExecuteInputResult, Option<CorpusId>), Error> {
self.evaluate_input_with_observers(state, executor, manager, input, send_events)
}
fn add_disabled_input(
&mut self,
state: &mut S,
@ -573,6 +645,7 @@ where
let id = state.corpus_mut().add_disabled(testcase)?;
Ok(id)
}
/// Adds an input, even if it's not considered `interesting` by any of the executors
fn add_input(
&mut self,
@ -672,7 +745,7 @@ where
}
}
impl<CS, E, EM, F, OF, S, ST> Fuzzer<E, EM, S, ST> for StdFuzzer<CS, F, OF>
impl<CS, E, EM, F, IF, OF, S, ST> Fuzzer<E, EM, S, ST> for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<S::Input, S>,
E: UsesState<State = S>,
@ -796,17 +869,44 @@ where
}
}
impl<CS, F, OF> StdFuzzer<CS, F, OF> {
/// Create a new `StdFuzzer` with standard behavior.
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
impl<CS, F, IF, OF> StdFuzzer<CS, F, IF, OF> {
/// Create a new [`StdFuzzer`] with standard behavior and the provided duplicate input execution filter.
pub fn with_input_filter(scheduler: CS, feedback: F, objective: OF, input_filter: IF) -> Self {
Self {
scheduler,
feedback,
objective,
input_filter,
}
}
}
impl<CS, F, OF> StdFuzzer<CS, F, NopInputFilter, OF> {
/// Create a new [`StdFuzzer`] with standard behavior and no duplicate input execution filtering.
pub fn new(scheduler: CS, feedback: F, objective: OF) -> Self {
Self::with_input_filter(scheduler, feedback, objective, NopInputFilter)
}
}
#[cfg(feature = "std")] // hashing requires std
impl<CS, F, OF> StdFuzzer<CS, F, BloomInputFilter, OF> {
/// Create a new [`StdFuzzer`], which, with a certain certainty, executes each input only once.
///
/// This is achieved by hashing each input and using a bloom filter to differentiate inputs.
///
/// Use this implementation if hashing each input is very fast compared to executing potential duplicate inputs.
pub fn with_bloom_input_filter(
scheduler: CS,
feedback: F,
objective: OF,
items_count: usize,
fp_p: f64,
) -> Self {
let input_filter = BloomInputFilter::new(items_count, fp_p);
Self::with_input_filter(scheduler, feedback, objective, input_filter)
}
}
/// Structs with this trait will execute an input
pub trait ExecutesInput<E, EM, I, S> {
/// Runs the input and triggers observers and feedback
@ -819,8 +919,8 @@ pub trait ExecutesInput<E, EM, I, S> {
) -> Result<ExitKind, Error>;
}
impl<CS, E, EM, F, OF, S> ExecutesInput<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, OF>
impl<CS, E, EM, F, IF, OF, S> ExecutesInput<E, EM, <S::Corpus as Corpus>::Input, S>
for StdFuzzer<CS, F, IF, OF>
where
CS: Scheduler<<S::Corpus as Corpus>::Input, S>,
E: Executor<EM, Self, State = S> + HasObservers,
@ -913,3 +1013,63 @@ where
unimplemented!("NopFuzzer cannot fuzz");
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use core::cell::RefCell;
use libafl_bolts::rands::StdRand;
use super::{Evaluator, StdFuzzer};
use crate::{
corpus::InMemoryCorpus,
events::NopEventManager,
executors::{ExitKind, InProcessExecutor},
inputs::BytesInput,
schedulers::StdScheduler,
state::StdState,
};
#[test]
fn filtered_execution() {
let execution_count = RefCell::new(0);
let scheduler = StdScheduler::new();
let mut fuzzer = StdFuzzer::with_bloom_input_filter(scheduler, (), (), 100, 1e-4);
let mut state = StdState::new(
StdRand::new(),
InMemoryCorpus::new(),
InMemoryCorpus::new(),
&mut (),
&mut (),
)
.unwrap();
let mut manager = NopEventManager::new();
let mut harness = |_input: &BytesInput| {
*execution_count.borrow_mut() += 1;
ExitKind::Ok
};
let mut executor =
InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut manager)
.unwrap();
let input = BytesInput::new(vec![1, 2, 3]);
assert!(fuzzer
.evaluate_input(&mut state, &mut executor, &mut manager, input.clone())
.is_ok());
assert_eq!(1, *execution_count.borrow()); // evaluate_input does not add it to the filter
assert!(fuzzer
.evaluate_filtered(&mut state, &mut executor, &mut manager, input.clone())
.is_ok());
assert_eq!(2, *execution_count.borrow()); // at to the filter
assert!(fuzzer
.evaluate_filtered(&mut state, &mut executor, &mut manager, input.clone())
.is_ok());
assert_eq!(2, *execution_count.borrow()); // the harness is not called
assert!(fuzzer
.evaluate_input(&mut state, &mut executor, &mut manager, input.clone())
.is_ok());
assert_eq!(3, *execution_count.borrow()); // evaluate_input ignores filters
}
}

View File

@ -0,0 +1,80 @@
//! A wrapper around a [`Mutator`] that ensures an input really changed [`MutationResult::Mutated`]
//! by hashing pre- and post-mutation
use std::{borrow::Cow, hash::Hash};
use libafl_bolts::{generic_hash_std, Error, Named};
use super::{MutationResult, Mutator};
/// A wrapper around a [`Mutator`] that ensures an input really changed [`MutationResult::Mutated`]
/// by hashing pre- and post-mutation and comparing the values
#[derive(Debug)]
pub struct MutationChecker<M> {
inner: M,
name: Cow<'static, str>,
}
impl<M> MutationChecker<M>
where
M: Named,
{
/// Create a new [`MutationChecker`]
pub fn new(inner: M) -> Self {
let name = Cow::Owned(format!("MutationChecker<{}>", inner.name().clone()));
Self { inner, name }
}
}
impl<M, I, S> Mutator<I, S> for MutationChecker<M>
where
I: Hash,
M: Mutator<I, S>,
{
fn mutate(&mut self, state: &mut S, input: &mut I) -> Result<MutationResult, Error> {
let before = generic_hash_std(input);
self.inner.mutate(state, input)?;
if before == generic_hash_std(input) {
Ok(MutationResult::Skipped)
} else {
Ok(MutationResult::Mutated)
}
}
}
impl<M> Named for MutationChecker<M> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
#[cfg(test)]
mod tests {
use crate::{
inputs::BytesInput,
mutators::{BytesSetMutator, MutationChecker, MutationResult, Mutator},
state::NopState,
};
#[test]
fn not_mutated() {
let mut state: NopState<BytesInput> = NopState::new();
let mut inner = BytesSetMutator::new();
let mut input = BytesInput::new(vec![0; 5]);
// nothing changed, yet `MutationResult::Mutated` was reported
assert_eq!(
MutationResult::Mutated,
inner.mutate(&mut state, &mut input).unwrap()
);
assert_eq!(BytesInput::new(vec![0; 5]), input);
// now it is correctly reported as `MutationResult::Skipped`
let mut hash_mutator = MutationChecker::new(inner);
assert_eq!(
MutationResult::Skipped,
hash_mutator.mutate(&mut state, &mut input).unwrap()
);
assert_eq!(BytesInput::new(vec![0; 5]), input);
}
}

View File

@ -28,6 +28,11 @@ pub use mapping::*;
pub mod tuneable;
pub use tuneable::*;
#[cfg(feature = "std")]
pub mod hash;
#[cfg(feature = "std")]
pub use hash::*;
#[cfg(feature = "unicode")]
pub mod unicode;
#[cfg(feature = "unicode")]
@ -84,12 +89,15 @@ impl From<i32> for MutationId {
}
}
/// The result of a mutation.
/// If the mutation got skipped, the target
/// will not be executed with the returned input.
/// Result of the mutation.
///
/// [`MutationResult::Skipped`] does not necessarily mean that the input changed,
/// just that the mutator did something. For slow targets, consider using
/// a filtered fuzzer (see [`crate::fuzzer::StdFuzzer::with_input_filter`])
/// or wrapping your mutator in a [`hash::MutationChecker`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MutationResult {
/// The [`Mutator`] mutated this `Input`.
/// The [`Mutator`] executed on this `Input`. It may not guarantee that the input has actually been changed.
Mutated,
/// The [`Mutator`] did not mutate this `Input`. It was `Skipped`.
Skipped,

View File

@ -417,8 +417,7 @@ where
for (index, new_byte) in mutation {
input_copy.bytes_mut()[index] = new_byte;
}
// Time is measured directly the `evaluate_input` function
fuzzer.evaluate_input(state, executor, manager, input_copy)?;
fuzzer.evaluate_filtered(state, executor, manager, input_copy)?;
}
}
Ok(())

View File

@ -44,7 +44,7 @@ where
manager: &mut EM,
) -> Result<(), Error> {
let input = self.0.generate(state)?;
fuzzer.evaluate_input(state, executor, manager, input)?;
fuzzer.evaluate_filtered(state, executor, manager, input)?;
Ok(())
}

View File

@ -277,9 +277,9 @@ where
continue;
}
// Time is measured directly the `evaluate_input` function
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
let (_, corpus_id) =
fuzzer.evaluate_filtered(state, executor, manager, untransformed)?;
start_timer!(state);
self.mutator_mut().post_exec(state, corpus_id)?;
@ -345,9 +345,9 @@ where
let generated = self.mutator.multi_mutate(state, &input, None)?;
for new_input in generated {
// Time is measured directly the `evaluate_input` function
let (untransformed, post) = new_input.try_transform_into(state)?;
let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
let (_, corpus_id) =
fuzzer.evaluate_filtered(state, executor, manager, untransformed)?;
self.mutator.multi_post_exec(state, corpus_id)?;
post.post_exec(state, corpus_id)?;
}

View File

@ -189,9 +189,9 @@ where
continue;
}
// Time is measured directly the `evaluate_input` function
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
let (_, corpus_id) =
fuzzer.evaluate_filtered(state, executor, manager, untransformed)?;
start_timer!(state);
self.mutator_mut().post_exec(state, corpus_id)?;

View File

@ -453,9 +453,8 @@ where
return Ok(());
}
// Time is measured directly the `evaluate_input` function
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_id) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
let (_, corpus_id) = fuzzer.evaluate_filtered(state, executor, manager, untransformed)?;
start_timer!(state);
self.mutator_mut().post_exec(state, corpus_id)?;

View File

@ -2669,7 +2669,11 @@ where
self.inner
.llmp_clients
.binary_search_by_key(&client_id, |x| x.id)
.expect("Fatal error, client ID {client_id} not found in llmp_clients.")
.unwrap_or_else(|_| {
panic!(
"Fatal error, client ID {client_id:?} not found in llmp_clients."
)
})
};
let client = &mut self.inner.llmp_clients[pos];
match client.recv()? {
@ -2768,7 +2772,7 @@ where
self.inner
.llmp_clients
.binary_search_by_key(&client_id, |x| x.id)
.expect("Fatal error, client ID {client_id} not found in llmp_clients.")
.unwrap_or_else(|_| panic!("Fatal error, client ID {client_id:?} not found in llmp_clients."))
};
let map = &mut self.inner.llmp_clients[pos].current_recv_shmem;

View File

@ -18,7 +18,7 @@ static SEED_COUNTER: AtomicUsize = AtomicUsize::new(0);
/// Return a pseudo-random seed. For `no_std` environments, a single deterministic sequence is used.
#[must_use]
#[expect(unreachable_code)]
#[allow(unreachable_code)] // cfg dependent
pub fn random_seed() -> u64 {
#[cfg(feature = "std")]
return random_seed_from_random_state();
@ -365,7 +365,7 @@ impl Rand for Lehmer64Rand {
fn set_seed(&mut self, mut seed: u64) {
let hi = splitmix64(&mut seed);
let lo = splitmix64(&mut seed) | 1;
self.s = u128::from(hi) << 64 | u128::from(lo);
self.s = (u128::from(hi) << 64) | u128::from(lo);
}
#[inline]

View File

@ -624,12 +624,9 @@ where
/// Is needed on top.
#[cfg(all(unix, feature = "std", not(target_os = "haiku")))]
pub mod unix_shmem {
/// Mmap [`ShMemProvider`] for Unix
#[cfg(not(target_os = "android"))]
pub use default::MmapShMemProvider;
/// Mmap [`ShMem`] for Unix
#[cfg(not(target_os = "android"))]
pub use default::{MmapShMem, MAX_MMAP_FILENAME_LEN};
pub use default::{MmapShMem, MmapShMemProvider, MAX_MMAP_FILENAME_LEN};
#[cfg(doc)]
use crate::shmem::{ShMem, ShMemProvider};
@ -669,7 +666,7 @@ pub mod unix_shmem {
Error,
};
/// The size of the buffer of the filename of mmap mapped memory regions
/// The max number of bytes used when generating names for [`MmapShMem`]s.
pub const MAX_MMAP_FILENAME_LEN: usize = 20;
/// Mmap-based The sharedmap impl for unix using [`shm_open`] and [`mmap`].

View File

@ -1532,8 +1532,7 @@ mod addr2line_legacy {
/// # Safety
/// Will access the global [`FullBacktraceCollector`].
/// Calling this function concurrently might be racey.
#[expect(clippy::unnecessary_cast)]
#[expect(clippy::too_many_lines)]
#[expect(clippy::too_many_lines, clippy::unnecessary_cast)]
pub unsafe fn asan_report(rt: &AsanGiovese, qemu: Qemu, pc: GuestAddr, err: &AsanError) {
let mut regions = HashMap::new();
for region in qemu.mappings() {

View File

@ -780,8 +780,7 @@ where
SyscallHookResult::new(None)
}
#[expect(clippy::too_many_arguments)]
#[expect(non_upper_case_globals)]
#[expect(non_upper_case_globals, clippy::too_many_arguments)]
pub fn trace_mmap_snapshot<ET, S>(
emulator_modules: &mut EmulatorModules<ET, S>,
_state: Option<&mut S>,

View File

@ -26,8 +26,7 @@ extern "C" {
///
/// # Safety
/// Calls the libfuzzer-style init function which is native code.
#[expect(clippy::similar_names)]
#[expect(clippy::must_use_candidate)] // nobody uses that return code...
#[expect(clippy::must_use_candidate, clippy::similar_names)] // nobody uses that return code...
pub unsafe fn libfuzzer_initialize(args: &[String]) -> i32 {
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect();
let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect();