diff --git a/libafl_bolts/Cargo.toml b/libafl_bolts/Cargo.toml index feb0938d18..f0a0dbb3b7 100644 --- a/libafl_bolts/Cargo.toml +++ b/libafl_bolts/Cargo.toml @@ -27,7 +27,7 @@ document-features = ["dep:document-features"] std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "uuid", "backtrace", "uds", "serial_test", "alloc"] ## Enables all features that allocate in `no_std` -alloc = ["serde/alloc", "hashbrown", "postcard", "erased-serde/alloc", "ahash"] +alloc = ["serde/alloc", "hashbrown", "postcard", "erased-serde/alloc", "ahash"] ## Provide the `#[derive(SerdeAny)]` macro. derive = ["libafl_derive"] @@ -95,25 +95,24 @@ rustversion = "1.0" libafl_derive = { version = "0.12.0", optional = true, path = "../libafl_derive" } static_assertions = "1.1.0" -rustversion = "1.0" tuple_list = { version = "0.1.3" } -hashbrown = { version = "0.14", features = ["serde", "ahash"], default-features=false, optional = true } # A faster hashmap, nostd compatible +hashbrown = { version = "0.14", features = ["serde", "ahash"], default-features = false, optional = true } # A faster hashmap, nostd compatible xxhash-rust = { version = "0.8.5", features = ["xxh3"], optional = true } # xxh3 hashing for rust serde = { version = "1.0", default-features = false, features = ["derive"] } # serialization lib erased-serde = { version = "0.3.21", default-features = false, optional = true } # erased serde postcard = { version = "1.0", features = ["alloc"], default-features = false, optional = true } # no_std compatible serde serialization format num_enum = { version = "0.7", default-features = false } -ahash = { version = "0.8", default-features=false, optional = true } # The hash function already used in hashbrown -backtrace = {version = "0.3", optional = true} # Used to get the stacktrace in StacktraceObserver +ahash = { version = "0.8", default-features = false, optional = true } # The hash function already used in hashbrown +backtrace = { version = "0.3", optional = true } # Used to get the stacktrace in StacktraceObserver ctor = { optional = true, version = "0.2" } serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } -miniz_oxide = { version = "0.7.1", optional = true} +miniz_oxide = { version = "0.7.1", optional = true } hostname = { version = "^0.3", optional = true } # Is there really no gethostname in the stdlib? rand_core = { version = "0.6", optional = true } nix = { version = "0.27", default-features = false, optional = true, features = ["signal", "socket", "poll"] } uuid = { version = "1.4", optional = true, features = ["serde", "v4"] } -clap = {version = "4.5", features = ["derive", "wrap_help"], optional = true} # CLI parsing, for libafl_bolts::cli / the `cli` feature +clap = { version = "4.5", features = ["derive", "wrap_help"], optional = true } # CLI parsing, for libafl_bolts::cli / the `cli` feature log = "0.4.20" pyo3 = { version = "0.18", optional = true, features = ["serde", "macros"] } diff --git a/libafl_bolts/src/tuples.rs b/libafl_bolts/src/tuples.rs index 9d93fdddb8..a892ba24ff 100644 --- a/libafl_bolts/src/tuples.rs +++ b/libafl_bolts/src/tuples.rs @@ -6,6 +6,7 @@ use alloc::{borrow::Cow, vec::Vec}; use core::ops::{Deref, DerefMut}; use core::{ any::{type_name, TypeId}, + cell::Cell, fmt::{Debug, Formatter}, marker::PhantomData, mem::transmute, @@ -23,40 +24,37 @@ use crate::HasLen; #[cfg(feature = "alloc")] use crate::Named; -/// Returns if the type `T` is equal to `U` -/// From -#[rustversion::nightly] -#[inline] -#[must_use] -pub const fn type_eq() -> bool { - // Helper trait. `VALUE` is false, except for the specialization of the - // case where `T == U`. - trait TypeEq { - const VALUE: bool; - } - - // Default implementation. - impl TypeEq for T { - default const VALUE: bool = false; - } - - // Specialization for `T == U`. - impl TypeEq for T { - const VALUE: bool = true; - } - - >::VALUE -} - -/// Returns if the type `T` is equal to `U` -/// As this relies on [`type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html#note) internally, -/// there is a chance for collisions. -/// Use `nightly` if you need a perfect match at all times. -#[rustversion::not(nightly)] -#[inline] +/// Returns if the type `T` is equal to `U`, ignoring lifetimes. +#[inline] // this entire call gets optimized away :) #[must_use] pub fn type_eq() -> bool { - type_name::() == type_name::() + // decider struct: hold a cell (which we will update if the types are unequal) and some + // phantom data using a function pointer to allow for Copy to be implemented + struct W<'a, T: ?Sized, U: ?Sized>(&'a Cell, PhantomData (&'a T, &'a U)>); + + // default implementation: if the types are unequal, we will use the clone implementation + impl<'a, T: ?Sized, U: ?Sized> Clone for W<'a, T, U> { + #[inline] + fn clone(&self) -> Self { + // indicate that the types are unequal + // unfortunately, use of interior mutability (Cell) makes this not const-compatible + // not really possible to get around at this time + self.0.set(false); + W(self.0, self.1) + } + } + + // specialized implementation: Copy is only implemented if the types are the same + #[allow(clippy::mismatching_type_param_order)] + impl<'a, T: ?Sized> Copy for W<'a, T, T> {} + + let detected = Cell::new(true); + // [].clone() is *specialized* in core. + // Types which implement copy will have their copy implementations used, falling back to clone. + // If the types are the same, then our clone implementation (which sets our Cell to false) + // will never be called, meaning that our Cell's content remains true. + let res = [W::(&detected, PhantomData)].clone(); + res[0].0.get() } /// Borrow each member of the tuple @@ -453,10 +451,6 @@ where } /// Match for a name and return the value -/// -/// # Note -/// This operation may not be 100% accurate with Rust stable, see the notes for [`type_eq`] -/// (in `nightly`, it uses [specialization](https://stackoverflow.com/a/60138532/7658998)). #[cfg(feature = "alloc")] pub trait MatchName { /// Match for a name and return the borrowed value