diff --git a/lain/Cargo.toml b/lain/Cargo.toml index b4172f1..cbffcf5 100644 --- a/lain/Cargo.toml +++ b/lain/Cargo.toml @@ -18,12 +18,14 @@ num-traits = "0.2" num-derive = "0.3" num = "0.4" lazy_static = "1.2" -serde = { version = "1.0" , optional = true, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } field-offset = "0.3" [features] default_features = [] serde_support = ["serde"] +pick_invalid_enum = [] +ignore_min_max = [] [profile.release] debug = true diff --git a/lain/src/mutatable.rs b/lain/src/mutatable.rs index 78d13ad..4feeeb0 100644 --- a/lain/src/mutatable.rs +++ b/lain/src/mutatable.rs @@ -38,17 +38,23 @@ enum VecResizeType { fn grow_vec( vec: &mut Vec, mutator: &mut Mutator, + max_elems: Option, mut max_size: Option, ) { let resize_count = VecResizeCount::new_fuzzed(mutator, None); + let resize_max = if let Some(max) = max_elems { + max + } else { + 9 // old magic value from lain + }; let mut num_elements = if vec.is_empty() { - mutator.gen_range(1, 9) + mutator.gen_range(1, resize_max) } else { match resize_count { VecResizeCount::Quarter => vec.len() / 4, VecResizeCount::Half => vec.len() / 2, VecResizeCount::ThreeQuarters => vec.len() - (vec.len() / 4), - VecResizeCount::FixedBytes => mutator.gen_range(1, 9), + VecResizeCount::FixedBytes => mutator.gen_range(1, resize_max), VecResizeCount::AllBytes => mutator.gen_range(1, vec.len() + 1), } }; @@ -58,6 +64,10 @@ fn grow_vec( num_elements = min(num_elements, max_size / T::max_default_object_size()); } + if let Some(max_elems) = max_elems { + num_elements = min(max_elems - vec.len(), num_elements); + } + if num_elements == 0 { return; } @@ -125,28 +135,30 @@ fn grow_vec( /// Shrinks a `Vec`. /// This will randomly select to resize by a factor of 1/4, 1/2, 3/4, or a fixed number of bytes /// in the range of [1, 8]. Elements may be removed randomly from the beginning or end of the the vec -fn shrink_vec(vec: &mut Vec, mutator: &mut Mutator) { +fn shrink_vec(vec: &mut Vec, mutator: &mut Mutator, min_size: Option) { if vec.is_empty() { return; } + let min_size = if let Some(min) = min_size { min } else { 0 }; + let resize_count = VecResizeCount::new_fuzzed(mutator, None); let mut num_elements = match resize_count { VecResizeCount::Quarter => vec.len() / 4, VecResizeCount::Half => vec.len() / 2, VecResizeCount::ThreeQuarters => vec.len() - (vec.len() / 4), - VecResizeCount::FixedBytes => mutator.gen_range(1, 9), - VecResizeCount::AllBytes => vec.len(), + VecResizeCount::FixedBytes => min(min(mutator.gen_range(1, 9), vec.len()), min_size), + VecResizeCount::AllBytes => min(vec.len(), min_size), }; if num_elements == 0 { num_elements = mutator.gen_range(0, vec.len() + 1); } - num_elements = std::cmp::min(num_elements, vec.len()); + num_elements = std::cmp::min(num_elements, vec.len() - min_size); // Special case probably isn't required here, but better to be explicit - if num_elements == vec.len() { + if num_elements == vec.len() && min_size == 0 { vec.drain(..); return; } @@ -177,7 +189,7 @@ where // 1% chance to resize this vec if mutator.gen_chance(CHANCE_TO_RESIZE_VEC) { - shrink_vec(self, mutator); + shrink_vec(self, mutator, Some(1)); } else { // Recreate the constraints so that the min/max types match let constraints = constraints.and_then(|c| { @@ -209,6 +221,7 @@ where constraints: Option<&Constraints>, ) { const CHANCE_TO_RESIZE_VEC: f64 = 0.01; + const CHANCE_TO_RESIZE_EMPTY_VEC: f64 = 0.33; if T::max_default_object_size() == 0 { return; @@ -216,37 +229,74 @@ where // we can grow the vector if we have no size constraint or the max size quota hasn't // been fulfilled - let can_grow = constraints - .map(|c| { - c.max_size - .map(|s| s > 0 && s > T::max_default_object_size()) - .unwrap_or(true) - }) - .unwrap_or(false); + let mut can_grow = true; + if let Some(max_elems) = constraints.and_then(|c| c.max) { + if self.len() >= max_elems { + can_grow = false; + } + } - if mutator.gen_chance(CHANCE_TO_RESIZE_VEC) { - let resize_type = VecResizeType::new_fuzzed(mutator, None); - if resize_type == VecResizeType::Grow && can_grow { - grow_vec(self, mutator, constraints.and_then(|c| c.max_size)); + if let Some(max_size) = constraints.and_then(|c| c.max_size) { + if self.len() >= max_size / T::max_default_object_size() { + can_grow = false; + } + } + + if self.is_empty() { + if mutator.gen_chance(CHANCE_TO_RESIZE_EMPTY_VEC) { + grow_vec( + self, + mutator, + constraints.and_then(|c| c.max), + constraints.and_then(|c| c.max_size), + ); } else { - shrink_vec(self, mutator); + // Recreate the constraints so that the min/max types match + let constraints = constraints.and_then(|c| { + if c.max_size.is_none() { + None + } else { + let mut new_constraints = Constraints::new(); + new_constraints.base_object_size_accounted_for = + c.base_object_size_accounted_for; + new_constraints.max_size = c.max_size; + + Some(new_constraints) + } + }); + + self.as_mut_slice().mutate(mutator, constraints.as_ref()); } } else { - // Recreate the constraints so that the min/max types match - let constraints = constraints.and_then(|c| { - if c.max_size.is_none() { - None + if mutator.gen_chance(CHANCE_TO_RESIZE_VEC) { + let resize_type = VecResizeType::new_fuzzed(mutator, None); + if resize_type == VecResizeType::Grow && can_grow { + grow_vec( + self, + mutator, + constraints.and_then(|c| c.max), + constraints.and_then(|c| c.max_size), + ); } else { - let mut new_constraints = Constraints::new(); - new_constraints.base_object_size_accounted_for = - c.base_object_size_accounted_for; - new_constraints.max_size = c.max_size; - - Some(new_constraints) + shrink_vec(self, mutator, constraints.and_then(|c| c.min)); } - }); + } else { + // Recreate the constraints so that the min/max types match + let constraints = constraints.and_then(|c| { + if c.max_size.is_none() { + None + } else { + let mut new_constraints = Constraints::new(); + new_constraints.base_object_size_accounted_for = + c.base_object_size_accounted_for; + new_constraints.max_size = c.max_size; - self.as_mut_slice().mutate(mutator, constraints.as_ref()); + Some(new_constraints) + } + }); + + self.as_mut_slice().mutate(mutator, constraints.as_ref()); + } } } } @@ -264,6 +314,7 @@ where constraints: Option<&Constraints>, ) { const CHANCE_TO_RESIZE_VEC: f64 = 0.01; + const CHANCE_TO_RESIZE_EMPTY_VEC: f64 = 0.33; if T::max_default_object_size() == 0 { return; @@ -275,29 +326,61 @@ where .map(|c| c.max_size.map(|s| s > 0).unwrap_or(true)) .unwrap_or(false); - if mutator.gen_chance(CHANCE_TO_RESIZE_VEC) { - let resize_type = VecResizeType::new_fuzzed(mutator, None); - if resize_type == VecResizeType::Grow && can_grow { - grow_vec(self, mutator, constraints.and_then(|c| c.max_size)); + if self.is_empty() { + if mutator.gen_chance(CHANCE_TO_RESIZE_EMPTY_VEC) { + grow_vec( + self, + mutator, + constraints.and_then(|c| c.max), + constraints.and_then(|c| c.max_size), + ); } else { - shrink_vec(self, mutator); + // Recreate the constraints so that the min/max types match + let constraints = constraints.and_then(|c| { + if c.max_size.is_none() { + None + } else { + let mut new_constraints = Constraints::new(); + new_constraints.base_object_size_accounted_for = + c.base_object_size_accounted_for; + new_constraints.max_size = c.max_size; + + Some(new_constraints) + } + }); + + self.as_mut_slice().mutate(mutator, constraints.as_ref()); } } else { - // Recreate the constraints so that the min/max types match - let constraints = constraints.and_then(|c| { - if c.max_size.is_none() { - None + if mutator.gen_chance(CHANCE_TO_RESIZE_VEC) { + let resize_type = VecResizeType::new_fuzzed(mutator, None); + if resize_type == VecResizeType::Grow && can_grow { + grow_vec( + self, + mutator, + constraints.and_then(|c| c.max), + constraints.and_then(|c| c.max_size), + ); } else { - let mut new_constraints = Constraints::new(); - new_constraints.base_object_size_accounted_for = - c.base_object_size_accounted_for; - new_constraints.max_size = c.max_size; - - Some(new_constraints) + shrink_vec(self, mutator, constraints.and_then(|c| c.min)); } - }); + } else { + // Recreate the constraints so that the min/max types match + let constraints = constraints.and_then(|c| { + if c.max_size.is_none() { + None + } else { + let mut new_constraints = Constraints::new(); + new_constraints.base_object_size_accounted_for = + c.base_object_size_accounted_for; + new_constraints.max_size = c.max_size; - self.as_mut_slice().mutate(mutator, constraints.as_ref()); + Some(new_constraints) + } + }); + + self.as_mut_slice().mutate(mutator, constraints.as_ref()); + } } } } @@ -618,18 +701,19 @@ where mutator: &mut Mutator, constraints: Option<&Constraints>, ) { - const CHANCE_TO_FLIP_OPTION_STATE: f64 = 0.01; + const CHANCE_TO_FLIP_SOME_STATE: f64 = 0.05; + const CHANCE_TO_FLIP_NONE_STATE: f64 = 0.10; match self { Some(inner) => { // small chance to make this None - if mutator.gen_chance(CHANCE_TO_FLIP_OPTION_STATE) { + if mutator.gen_chance(CHANCE_TO_FLIP_SOME_STATE) { *self = None; } else { inner.mutate(mutator, constraints); } } None => { - if mutator.gen_chance(CHANCE_TO_FLIP_OPTION_STATE) { + if mutator.gen_chance(CHANCE_TO_FLIP_NONE_STATE) { let new_item = T::new_fuzzed(mutator, constraints); *self = Some(new_item); diff --git a/lain/src/mutator.rs b/lain/src/mutator.rs index 0c99aa8..2ca8cf3 100644 --- a/lain/src/mutator.rs +++ b/lain/src/mutator.rs @@ -16,8 +16,14 @@ use serde::{Deserialize, Serialize}; // set these to 0 to disable pub const CHANCE_TO_REPEAT_ARRAY_VALUE: f64 = 0.05; -pub const CHANCE_TO_PICK_INVALID_ENUM: f64 = 0.10; -pub const CHANCE_TO_IGNORE_MIN_MAX: f64 = 0.05; +#[cfg(feature = "pick_invalid_enum")] +pub const CHANCE_TO_PICK_INVALID_ENUM: f64 = 0.01; +#[cfg(not(feature = "pick_invalid_enum"))] +pub const CHANCE_TO_PICK_INVALID_ENUM: f64 = 0.0; +#[cfg(feature = "ignore_min_max")] +pub const CHANCE_TO_IGNORE_MIN_MAX: f64 = 0.01; +#[cfg(not(feature = "ignore_min_max"))] +pub const CHANCE_TO_IGNORE_MIN_MAX: f64 = 0.0; #[repr(u8)] #[derive(Debug, Copy, Clone, NewFuzzed)] @@ -107,7 +113,7 @@ impl Mutator { self.corpus_state.fields_fuzzed += 1; } - if self.gen_chance(0.10) { + if self.gen_chance(0.01) { *num = T::select_dangerous_number(&mut self.rng); return; } diff --git a/lain_derive/src/mutations.rs b/lain_derive/src/mutations.rs index 1c7a130..16679df 100644 --- a/lain_derive/src/mutations.rs +++ b/lain_derive/src/mutations.rs @@ -271,13 +271,16 @@ fn mutatable_enum_visitor(variants: &[Variant], cont_ident: &syn::Ident) -> Vec< fn mutatable_struct_visitor(fields: &[Field]) -> Vec { fields .iter() - .map(|field| { + .filter_map(|field| { + if field.attrs.ignore() { + return None; + } let (_field_ident, _field_ident_string, initializer) = field_mutator(field, "self.", false); - quote! { + Some(quote! { #initializer - } + }) }) .collect() }