From 89c2d9e7bc5daf80ab5e38d19277dd7c196a7fee Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:44:41 -0400 Subject: [PATCH 01/13] bumped version; libafl now optional --- Cargo.toml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c198398..af81f4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "feroxfuzz" -version = "1.0.0-rc.11" +version = "1.0.0-rc.12" edition = "2021" authors = ["Ben 'epi' Risher (@epi052)"] license = "Apache-2.0" @@ -29,6 +29,9 @@ blocking = ["reqwest/blocking"] # wrapper around providing different encoders encoders = ["base64", "hex"] +# wrapper around providing havoc mutations +havoc = ["libafl", "libafl_bolts"] + [dependencies] # MIT Licenses tuple_list = { version = "0.1" } @@ -47,7 +50,10 @@ tokio = { version = "1.20", optional = true, features = [ num = { version = "0.4" } cfg-if = { version = "1.0" } dyn-clone = { version = "1.0.9" } -libafl = { version = "0.10.1", default-features = false, features = ["std"] } +libafl = { version = "0.11.1", default-features = false, features = [ + "std", +], optional = true } +libafl_bolts = { version = "0.11.1", default-features = false, optional = true } url = { version = "2.2", features = ["serde"] } ## optional serde = { version = "1.0", optional = true, features = ["derive", "rc"] } From ab303c820862cea068c825fd0a88782f01bfc149 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:44:56 -0400 Subject: [PATCH 02/13] updated havoc example exec string --- examples/havoc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/havoc.rs b/examples/havoc.rs index 017f74f..28ef09f 100644 --- a/examples/havoc.rs +++ b/examples/havoc.rs @@ -1,7 +1,7 @@ //! first, update the variable named target with a valid url to scan //! //! then run the example with the following command -//! cargo run --example havoc +//! cargo run --features havoc --example havoc use feroxfuzz::actions::{Action, FlowControl}; use feroxfuzz::client::{AsyncClient, HttpClient}; use feroxfuzz::corpora::{CorpusItemType, Wordlist}; From dd7fd0110b46527b636a143eacabc71763e00921 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:46:38 -0400 Subject: [PATCH 03/13] gated libafl usage behind cfg checks --- src/error.rs | 1 + src/input.rs | 34 ++++++++++++++++++++-------------- src/lib.rs | 3 ++- src/mutators/mod.rs | 24 +++++++++++++++--------- src/schedulers/random.rs | 3 ++- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/error.rs b/src/error.rs index 28cfc7e..b78962c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -189,6 +189,7 @@ pub enum FeroxFuzzError { /// Represents a failed mutation of a [`Request`] object /// /// [`Request`]: crate::requests::Request + #[cfg(feature = "libafl")] #[error("Mutation failed")] MutationError { /// underlying source error-type diff --git a/src/input.rs b/src/input.rs index e275cb3..0c5298a 100644 --- a/src/input.rs +++ b/src/input.rs @@ -3,6 +3,7 @@ use crate::error::FeroxFuzzError; use crate::std_ext::convert::{AsInner, IntoInner}; use crate::std_ext::fmt::DisplayExt; use crate::AsBytes; +#[cfg(feature = "libafl")] use libafl::inputs::{HasBytesVec, Input}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -10,6 +11,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::str::FromStr; use std::string::ParseError; +use cfg_if::cfg_if; use tracing::{error, instrument}; /// Base-level input type; can be marked `Fuzzable` or `Static` @@ -339,22 +341,26 @@ impl PartialEq for Vec { } } -// implement the HasBytesVec and Input traits from libafl so our -// state plays nicely with libafl mutators -impl HasBytesVec for Data { - fn bytes(&self) -> &[u8] { - self.as_ref() - } +cfg_if! { + if #[cfg(feature = "libafl")] { + // implement the HasBytesVec and Input traits from libafl so our + // state plays nicely with libafl mutators + impl HasBytesVec for Data { + fn bytes(&self) -> &[u8] { + self.as_ref() + } - fn bytes_mut(&mut self) -> &mut Vec { - match self { - Self::Fuzzable(inner) | Self::Static(inner) => inner, + fn bytes_mut(&mut self) -> &mut Vec { + match self { + Self::Fuzzable(inner) | Self::Static(inner) => inner, + } + } } - } -} -impl Input for Data { - fn generate_name(&self, idx: usize) -> String { - format!("{}_{idx}", self.format()) + impl Input for Data { + fn generate_name(&self, idx: usize) -> String { + format!("{}_{idx}", self.format()) + } + } } } diff --git a/src/lib.rs b/src/lib.rs index ace5ac5..b4ca17a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,8 @@ pub use tuple_list::TupleList as MutatorsList; pub use tuple_list::TupleList as ProcessorsList; // re-exported 3rd party crate traits -pub use libafl::bolts::rands::Rand; +#[cfg(feature = "libafl")] +pub use libafl_bolts::rands::Rand; /// Wrapper `Atomic*.fetch_add` to save me from writing `Ordering::SeqCst` a bajillion times /// diff --git a/src/mutators/mod.rs b/src/mutators/mod.rs index 61dd520..0d60653 100644 --- a/src/mutators/mod.rs +++ b/src/mutators/mod.rs @@ -1,10 +1,23 @@ //! actions taken against [`Data`] that change the underlying bytes in some way -mod afl; -mod havoc; mod wordlist_token; +pub use self::wordlist_token::ReplaceKeyword; + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(any(feature = "havoc", feature = "libafl"))] { + mod afl; + mod havoc; + pub use self::afl::*; + pub use self::havoc::HavocMutator; + } +} use std::sync::{Arc, Once, RwLock}; +use dyn_clone::DynClone; +use tracing::instrument; + use crate::error::FeroxFuzzError; use crate::events::{EventPublisher, Mutation, Publisher}; use crate::input::Data; @@ -14,13 +27,6 @@ use crate::state::SharedState; use crate::std_ext::tuple::Named; use crate::MutatorsList; -pub use self::afl::*; -pub use self::havoc::HavocMutator; -pub use self::wordlist_token::ReplaceKeyword; - -use dyn_clone::DynClone; -use tracing::instrument; - static mut HAS_LISTENERS: bool = false; static INIT: Once = Once::new(); diff --git a/src/schedulers/random.rs b/src/schedulers/random.rs index 6b32bcb..41af2dd 100644 --- a/src/schedulers/random.rs +++ b/src/schedulers/random.rs @@ -7,7 +7,8 @@ use crate::state::SharedState; use crate::std_ext::ops::Len; use crate::std_ext::tuple::Named; -use libafl::bolts::rands::Rand; +#[cfg(feature = "libafl")] +use libafl_bolts::rands::Rand; use tracing::{error, instrument, trace}; /// Random access of the associated [`Corpus`] From 1793be06f96a85a9d662019e9e105b6056509144 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:47:02 -0400 Subject: [PATCH 04/13] added non-libafl impl of Named/MatchName --- src/std_ext/tuple.rs | 77 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/std_ext/tuple.rs b/src/std_ext/tuple.rs index 6a822de..57ae7cb 100644 --- a/src/std_ext/tuple.rs +++ b/src/std_ext/tuple.rs @@ -1 +1,76 @@ -pub use libafl::bolts::tuples::{MatchName, Named}; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "libafl")] { + pub use libafl_bolts::tuples::MatchName; + pub use libafl_bolts::Named; + + } else { + // note: the following traits are from libafl. It was too heavy a solution to bring LibAFL + // along for a few traits and their rand implementation, so they're copied here instead. + // Full credit for the following traits goes to the LibAFL authors. + + use core::any::type_name; + use core::ptr::{addr_of, addr_of_mut}; + + /// We need fixed names for many parts of this lib. + pub trait Named { + /// Provide the name of this element. + fn name(&self) -> &str; + } + + /// 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. + #[inline] + #[must_use] + pub fn type_eq() -> bool { + type_name::() == type_name::() + } + + /// 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)). + pub trait MatchName { + /// Match for a name and return the borrowed value + fn match_name(&self, name: &str) -> Option<&T>; + /// Match for a name and return the mut borrowed value + fn match_name_mut(&mut self, name: &str) -> Option<&mut T>; + } + + impl MatchName for () { + fn match_name(&self, _name: &str) -> Option<&T> { + None + } + fn match_name_mut(&mut self, _name: &str) -> Option<&mut T> { + None + } + } + + impl MatchName for (Head, Tail) + where + Head: Named, + Tail: MatchName, + { + fn match_name(&self, name: &str) -> Option<&T> { + if type_eq::() && name == self.0.name() { + unsafe { (addr_of!(self.0) as *const T).as_ref() } + } else { + self.1.match_name::(name) + } + } + + fn match_name_mut(&mut self, name: &str) -> Option<&mut T> { + if type_eq::() && name == self.0.name() { + unsafe { (addr_of_mut!(self.0) as *mut T).as_mut() } + } else { + self.1.match_name_mut::(name) + } + } + } + + } +} From ab902f94fe5a2cfc0560128677bfbd757e3f5772 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:47:26 -0400 Subject: [PATCH 05/13] added non-libafl impl of RomuDuoJr --- src/state.rs | 128 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 23 deletions(-) diff --git a/src/state.rs b/src/state.rs index 5200716..1659d27 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,6 +4,7 @@ use std::fmt::{self, Display, Formatter}; use std::sync::atomic::AtomicUsize; use std::sync::{Arc, Once, RwLock}; +use cfg_if::cfg_if; use tracing::{debug, error, instrument, warn}; use crate::actions::Action; @@ -21,8 +22,85 @@ use crate::Len; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -use libafl::bolts::rands::RomuDuoJrRand; -use libafl::state::{HasMaxSize, HasRand, DEFAULT_MAX_SIZE}; +cfg_if! { + if #[cfg(feature = "libafl")] { + use libafl_bolts::rands::RomuDuoJrRand; + use libafl::state::{HasMaxSize, HasRand, DEFAULT_MAX_SIZE}; + } else { + // note: the following rng is from libafl. It was too heavy a solution to bring LibAFL + // along for a few traits and this rand implementation, so it's copied here instead. + // Full credit for the following rng implementation goes to the LibAFL authors. + use std::time::{UNIX_EPOCH, SystemTime}; + + /// see + #[derive(Copy, Clone, Debug, Serialize, Deserialize)] + pub struct RomuDuoJrRand { + x_state: u64, + y_state: u64, + } + + impl RomuDuoJrRand { + /// Creates a rand instance, pre-seeded with the current time in nanoseconds. + pub fn new() -> Self { + let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + Self::with_seed(current_time.as_nanos() as u64) + } + + /// Creates a new `RomuDuoJrRand` with the given seed. + #[must_use] + pub fn with_seed(seed: u64) -> Self { + let mut rand = Self { + x_state: 0, + y_state: 0, + }; + rand.set_seed(seed); + rand + } + + fn set_seed(&mut self, seed: u64) { + self.x_state = seed ^ 0x12345; + self.y_state = seed ^ 0x6789A; + } + + #[inline] + fn next(&mut self) -> u64 { + let xp = self.x_state; + self.x_state = 15241094284759029579_u64.wrapping_mul(self.y_state); + self.y_state = self.y_state.wrapping_sub(xp).rotate_left(27); + xp + } + + /// Gets a value below the given 64 bit val (exclusive) + pub fn below(&mut self, upper_bound_excl: u64) -> u64 { + if upper_bound_excl <= 1 { + return 0; + } + + /* + Modulo is biased - we don't want our fuzzing to be biased so let's do it + right. See + https://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator + */ + let mut unbiased_rnd: u64; + loop { + unbiased_rnd = self.next(); + if unbiased_rnd < (u64::MAX - (u64::MAX % upper_bound_excl)) { + break; + } + } + + unbiased_rnd % upper_bound_excl + } + + } + + impl Default for RomuDuoJrRand { + fn default() -> Self { + Self::new() + } + } + } +} static mut HAS_LISTENERS: bool = false; static INIT: Once = Once::new(); @@ -693,33 +771,37 @@ impl Display for SharedState { } } -// implement the HasRand and HasMaxSize traits from libafl so our -// state plays nicely with libafl mutators -impl HasRand for SharedState { - type Rand = RomuDuoJrRand; +cfg_if! { + if #[cfg(feature = "libafl")] { + // implement the HasRand and HasMaxSize traits from libafl so our + // state plays nicely with libafl mutators + impl HasRand for SharedState { + type Rand = RomuDuoJrRand; - fn rand(&self) -> &Self::Rand { - &self.rng - } + fn rand(&self) -> &Self::Rand { + &self.rng + } - fn rand_mut(&mut self) -> &mut Self::Rand { - &mut self.rng - } -} + fn rand_mut(&mut self) -> &mut Self::Rand { + &mut self.rng + } + } -impl HasMaxSize for SharedState { - fn max_size(&self) -> usize { - DEFAULT_MAX_SIZE - } + impl HasMaxSize for SharedState { + fn max_size(&self) -> usize { + DEFAULT_MAX_SIZE + } + + fn set_max_size(&mut self, _max_size: usize) { + // - pass - + // + // nothing calls this from libafl's code, and i don't see a + // need for it in feroxfuzz + } + } - fn set_max_size(&mut self, _max_size: usize) { - // - pass - - // - // nothing calls this from libafl's code, and i don't see a - // need for it in feroxfuzz } } - #[cfg(test)] mod tests { use super::*; From d913486f2d083ee258d4e3f8c06d0c979d592e05 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:48:01 -0400 Subject: [PATCH 06/13] libafl::bolts to libafl_bolts --- src/mutators/afl.rs | 2 +- src/mutators/havoc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mutators/afl.rs b/src/mutators/afl.rs index a45a02c..3331b75 100644 --- a/src/mutators/afl.rs +++ b/src/mutators/afl.rs @@ -17,9 +17,9 @@ use crate::std_ext::ops::Len; use crate::std_ext::tuple::Named; use crate::{atomic_load, AsBytes}; -use libafl::bolts::rands::Rand; use libafl::inputs::HasBytesVec; use libafl::state::{HasMaxSize, HasRand}; +use libafl_bolts::rands::Rand; #[cfg(feature = "serde")] use serde::{ de::{self, Deserialize, Deserializer, MapAccess, Visitor}, diff --git a/src/mutators/havoc.rs b/src/mutators/havoc.rs index 4ae5bf0..0a7f1f9 100644 --- a/src/mutators/havoc.rs +++ b/src/mutators/havoc.rs @@ -2,7 +2,7 @@ use std::any::Any; use std::sync::atomic::Ordering; -use libafl::bolts::rands::Rand; +use libafl_bolts::rands::Rand; use libafl::state::HasRand; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; From 28f8ff868c53567bb009501d6629e216af5bec2d Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:48:08 -0400 Subject: [PATCH 07/13] nitpickery --- src/observers/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/observers/mod.rs b/src/observers/mod.rs index 474ee52..ab6bc60 100644 --- a/src/observers/mod.rs +++ b/src/observers/mod.rs @@ -3,8 +3,7 @@ //! [`Deciders`]: crate::deciders::Deciders use crate::requests::Request; use crate::responses::Response; -use crate::std_ext::tuple::Named; -use crate::MatchName; +use crate::std_ext::tuple::{MatchName, Named}; use crate::ObserversList; mod response; From fae3982015d430cc35c3fd9b9cdb3b985a8e8f54 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 07:51:11 -0400 Subject: [PATCH 08/13] fmt --- src/mutators/havoc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mutators/havoc.rs b/src/mutators/havoc.rs index 0a7f1f9..d1c0155 100644 --- a/src/mutators/havoc.rs +++ b/src/mutators/havoc.rs @@ -2,8 +2,8 @@ use std::any::Any; use std::sync::atomic::Ordering; -use libafl_bolts::rands::Rand; use libafl::state::HasRand; +use libafl_bolts::rands::Rand; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; From 24f355d5969129f0b2ca01e3809c9634dce73eaa Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 08:35:22 -0400 Subject: [PATCH 09/13] clippy --- src/error.rs | 13 +++++++++++++ src/fuzzers/async_fuzzer.rs | 3 ++- src/fuzzers/mod.rs | 2 +- src/mutators/mod.rs | 4 ++-- src/mutators/wordlist_token.rs | 22 +++++++++++++++++++++- src/requests/encoders.rs | 6 ++---- src/schedulers/ordered.rs | 2 +- src/schedulers/product.rs | 4 ++-- src/schedulers/random.rs | 2 +- src/schedulers/unique.rs | 2 +- src/state.rs | 8 ++++---- src/statistics.rs | 4 ++-- 12 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/error.rs b/src/error.rs index b78962c..94e1f8d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -196,6 +196,19 @@ pub enum FeroxFuzzError { source: libafl::Error, }, + /// Represents a failure to convert between two number types + #[error("Conversion from {value}_{from} to {to} failed")] + ConversionError { + /// value that couldn't be converted + value: String, + + /// type to which conversion was attempted + to: String, + + /// type from which conversion was attempted + from: String, + }, + /// Represents a failure encountered during sending a request / receiving a response #[error("An error occurred while sending the request: {kind:?} {message}")] RequestError { diff --git a/src/fuzzers/async_fuzzer.rs b/src/fuzzers/async_fuzzer.rs index 9c63277..6ebc315 100644 --- a/src/fuzzers/async_fuzzer.rs +++ b/src/fuzzers/async_fuzzer.rs @@ -479,10 +479,11 @@ where // if any of the tasks failed, log the error and move along, nothing can really be // done about it from here + #[allow(clippy::tuple_array_conversions)] // false positive [first, second, third, fourth, fifth, sixth] .into_iter() .filter_map(|result| match result { - Ok(_) => None, + Ok(()) => None, Err(err) => Some(err), }) .for_each(|err| { diff --git a/src/fuzzers/mod.rs b/src/fuzzers/mod.rs index 7717194..a510f2f 100644 --- a/src/fuzzers/mod.rs +++ b/src/fuzzers/mod.rs @@ -212,7 +212,7 @@ impl Debug for FuzzingLoopHook { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FuzzingLoopHook") .field("called", &self.called) - .finish() + .finish_non_exhaustive() // purposely ignore callback } } diff --git a/src/mutators/mod.rs b/src/mutators/mod.rs index 0d60653..0c75de9 100644 --- a/src/mutators/mod.rs +++ b/src/mutators/mod.rs @@ -164,7 +164,7 @@ pub trait Mutator: DynClone + AsAny + Named + Send + Sync { } if let Some(headers) = request.headers.as_mut() { - for (key, value) in headers.iter_mut() { + for (key, value) in &mut *headers { if key.is_fuzzable() { self.mutate(key, state)?; notify_listeners( @@ -188,7 +188,7 @@ pub trait Mutator: DynClone + AsAny + Named + Send + Sync { } if let Some(params) = request.params.as_mut() { - for (key, value) in params.iter_mut() { + for (key, value) in &mut *params { if key.is_fuzzable() { self.mutate(key, state)?; notify_listeners( diff --git a/src/mutators/wordlist_token.rs b/src/mutators/wordlist_token.rs index 62eda7f..5ef840b 100644 --- a/src/mutators/wordlist_token.rs +++ b/src/mutators/wordlist_token.rs @@ -144,7 +144,27 @@ impl Mutator for ReplaceKeyword { // piece of data, the index gathered above will move by some amount. // the `step` value here is one piece of info necessary to calculate // how far the move is. - let step = entry.len() as i64 - self.keyword.len() as i64; + let entry_length = i64::try_from(entry.len()).map_err(|source| { + tracing::error!(%source, "could not convert from {} to an i64", entry.len()); + + FeroxFuzzError::ConversionError { + value: format!("{}", entry.len()), + to: String::from("i64"), + from: String::from("usize"), + } + })?; + + let keyword_length = i64::try_from(self.keyword.len()).map_err(|source| { + tracing::error!(%source, "could not convert from {} to an i64", self.keyword.len()); + + FeroxFuzzError::ConversionError { + value: format!("{}", self.keyword.len()), + to: String::from("i64"), + from: String::from("usize"), + } + })?; + + let step = entry_length - keyword_length; indices.iter().enumerate().for_each(|(i, &idx)| { // when the step is negative, we need to subtract the diff --git a/src/requests/encoders.rs b/src/requests/encoders.rs index 0968901..de68b44 100644 --- a/src/requests/encoders.rs +++ b/src/requests/encoders.rs @@ -27,8 +27,7 @@ macro_rules! encode_optional_field { } // buffer of a size big enough to hold the encoded value - let mut buffer = Vec::new(); - buffer.resize($field.len() * 4 / 3 + 4, 0); + let mut buffer = vec![0; $field.len() * 4 / 3 + 4]; // perform the write let written = general_purpose::URL_SAFE.encode_slice( @@ -63,8 +62,7 @@ macro_rules! encode_optional_field { } // buffer of a size big enough to hold the encoded value - let mut buffer = Vec::new(); - buffer.resize($field.len() * 2, 0); + let mut buffer = vec![0; $field.len() * 2]; // perform the write // diff --git a/src/schedulers/ordered.rs b/src/schedulers/ordered.rs index bf78131..93030ef 100644 --- a/src/schedulers/ordered.rs +++ b/src/schedulers/ordered.rs @@ -165,7 +165,7 @@ impl OrderedScheduler { let mut indices = Vec::with_capacity(corpora.len()); - for (name, corpus) in corpora.iter() { + for (name, corpus) in &*corpora { let length = corpus.len(); if length == 0 { diff --git a/src/schedulers/product.rs b/src/schedulers/product.rs index f007ef4..c3a7bbe 100644 --- a/src/schedulers/product.rs +++ b/src/schedulers/product.rs @@ -49,7 +49,7 @@ impl std::fmt::Debug for ProductScheduler { f.debug_struct("ProductScheduler") .field("current", &self.current) .field("indices", &self.indices) - .finish() + .finish_non_exhaustive() // purposely skip state } } @@ -121,7 +121,7 @@ impl Scheduler for ProductScheduler { // The pattern is that when an index reaches its modulo value, it is // reset to 0 and the next greater loop is incremented. - for index in self.indices[1..].iter_mut() { + for index in &mut self.indices[1..] { // due to len==1 check above, the slice is ok index.next()?; diff --git a/src/schedulers/random.rs b/src/schedulers/random.rs index 41af2dd..754cf3f 100644 --- a/src/schedulers/random.rs +++ b/src/schedulers/random.rs @@ -180,7 +180,7 @@ impl RandomScheduler { .read() .map_or(0, |stats| stats.requests() as usize); - for (name, corpus) in corpora.iter() { + for (name, corpus) in &*corpora { let length = corpus.len(); if length == 0 { diff --git a/src/schedulers/unique.rs b/src/schedulers/unique.rs index 5bb3749..49cee35 100644 --- a/src/schedulers/unique.rs +++ b/src/schedulers/unique.rs @@ -92,7 +92,7 @@ impl std::fmt::Debug for UniqueProductScheduler { f.debug_struct("UniqueProductScheduler") .field("current", &self.current) .field("indices", &self.indices) - .finish() + .finish_non_exhaustive() // purposely skip state } } diff --git a/src/state.rs b/src/state.rs index 1659d27..15d38ac 100644 --- a/src/state.rs +++ b/src/state.rs @@ -631,7 +631,7 @@ impl SharedState { } if let Some(headers) = request.headers() { - for (key, value) in headers.iter() { + for (key, value) in headers { if key.is_fuzzable() { guard.add(key.clone()); @@ -656,7 +656,7 @@ impl SharedState { } if let Some(params) = request.params() { - for (key, value) in params.iter() { + for (key, value) in params { if key.is_fuzzable() { guard.add(key.clone()); @@ -748,7 +748,7 @@ impl Display for SharedState { writeln!(f, " Seed={}", self.seed)?; writeln!(f, " Rng={:?}", self.rng)?; - for (key, corpus) in self.corpora.iter() { + for (key, corpus) in &*self.corpora { if let Ok(guard) = corpus.read() { writeln!(f, " Corpus[{key}]={guard},")?; } @@ -760,7 +760,7 @@ impl Display for SharedState { if let Ok(guard) = self.metadata().read() { #[allow(clippy::significant_drop_in_scrutinee)] // doesn't appear to be an accurate lint - for (key, value) in guard.iter() { + for (key, value) in &*guard { writeln!(f, " Metadata[{key}]={value:?}")?; } } diff --git a/src/statistics.rs b/src/statistics.rs index 9640cfb..46ff6e4 100644 --- a/src/statistics.rs +++ b/src/statistics.rs @@ -291,7 +291,7 @@ impl Statistics { *self .actions .entry("request".to_string()) - .or_insert_with(HashMap::new) + .or_default() .entry(to_update) .or_insert(0) += 1; } @@ -299,7 +299,7 @@ impl Statistics { *self .actions .entry("response".to_string()) - .or_insert_with(HashMap::new) + .or_default() .entry(to_update) .or_insert(0) += 1; } From 8ec1355d556aeee1d2cd045ced88f96e92be803f Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 08:36:51 -0400 Subject: [PATCH 10/13] fmt --- src/schedulers/unique.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schedulers/unique.rs b/src/schedulers/unique.rs index 49cee35..c7767f8 100644 --- a/src/schedulers/unique.rs +++ b/src/schedulers/unique.rs @@ -92,7 +92,7 @@ impl std::fmt::Debug for UniqueProductScheduler { f.debug_struct("UniqueProductScheduler") .field("current", &self.current) .field("indices", &self.indices) - .finish_non_exhaustive() // purposely skip state + .finish_non_exhaustive() // purposely skip state } } From 4d81a393460b65663d6df05d3d382b771d841aaf Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 08:46:19 -0400 Subject: [PATCH 11/13] added second lib test for default features only --- Makefile.toml | 1 + src/deciders/mod.rs | 2 +- src/requests/encoders.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.toml b/Makefile.toml index cd8629b..8ef973d 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -54,6 +54,7 @@ cargo test --all-features --doc "${@}" [tasks.test-lib] clear = true script = """ +cargo nextest run --retries 10 --lib "${@}" cargo nextest run --all-features --retries 10 --lib "${@}" """ diff --git a/src/deciders/mod.rs b/src/deciders/mod.rs index 22f6005..ad5b73b 100644 --- a/src/deciders/mod.rs +++ b/src/deciders/mod.rs @@ -267,7 +267,7 @@ where } } -#[cfg(test)] +#[cfg(all(test, feature = "blocking"))] mod tests { #![allow(clippy::similar_names)] use super::*; diff --git a/src/requests/encoders.rs b/src/requests/encoders.rs index de68b44..7bb66e4 100644 --- a/src/requests/encoders.rs +++ b/src/requests/encoders.rs @@ -343,7 +343,7 @@ impl RequestExt for Request { } } -#[cfg(test)] +#[cfg(all(test, feature = "encoders"))] mod tests { use super::*; use crate::requests::ShouldFuzz; From 31fd63f046f702be67a77729120677a3c72d7808 Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 08:59:22 -0400 Subject: [PATCH 12/13] fixed up ci workflow --- .github/workflows/check.yml | 14 ++++++++++++-- Cargo.toml | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 02c58b4..69d3723 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -33,11 +33,21 @@ jobs: args: --all-features --doc - name: Install latest nextest release uses: taiki-e/install-action@nextest - - name: Test with latest nextest release + - name: Test with all features uses: actions-rs/cargo@v1 with: command: nextest - args: run --all-targets --all-features + args: run --retries 10 --lib --all-targets --all-features + - name: Test with default features + uses: actions-rs/cargo@v1 + with: + command: nextest + args: run --retries 10 --lib + + semver: + name: Semver Check + runs-on: ubuntu-latest + steps: - name: Cargo semver checks uses: obi1kenobi/cargo-semver-checks-action@v2 diff --git a/Cargo.toml b/Cargo.toml index af81f4b..d86fb53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,10 +50,6 @@ tokio = { version = "1.20", optional = true, features = [ num = { version = "0.4" } cfg-if = { version = "1.0" } dyn-clone = { version = "1.0.9" } -libafl = { version = "0.11.1", default-features = false, features = [ - "std", -], optional = true } -libafl_bolts = { version = "0.11.1", default-features = false, optional = true } url = { version = "2.2", features = ["serde"] } ## optional serde = { version = "1.0", optional = true, features = ["derive", "rc"] } @@ -72,6 +68,10 @@ futures = { version = "0.3", optional = true } base64 = { version = "0.21.2", optional = true } hex = { version = "0.4.3", optional = true } flume = { version = "0.11.0" } +libafl = { version = "0.11.1", default-features = false, features = [ + "std", +], optional = true } +libafl_bolts = { version = "0.11.1", default-features = false, optional = true } [dev-dependencies] http = { version = "0.2" } From 9e01e4aade738ac38acad8ff307bcb476fceeafe Mon Sep 17 00:00:00 2001 From: epi Date: Sat, 14 Oct 2023 11:28:57 -0400 Subject: [PATCH 13/13] fix semver pipeline --- .github/workflows/check.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 69d3723..26b54c1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -7,7 +7,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -21,7 +21,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -48,6 +48,8 @@ jobs: name: Semver Check runs-on: ubuntu-latest steps: + - name: Checkout + uses: actions/checkout@v3 - name: Cargo semver checks uses: obi1kenobi/cargo-semver-checks-action@v2 @@ -55,7 +57,7 @@ jobs: name: Rust fmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal @@ -71,7 +73,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal