From a97cb173f4b20f6f1923279d007a9fe9538c94fb Mon Sep 17 00:00:00 2001 From: Lander Brandt Date: Tue, 21 Apr 2020 11:09:25 -0700 Subject: [PATCH] fix(mutations): ignore_chance should now be working for enum variants --- lain/src/new_fuzzed.rs | 4 +-- lain_derive/src/mutations.rs | 64 ++++++++++++++++++++++++++++-------- testsuite/Cargo.toml | 3 -- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/lain/src/new_fuzzed.rs b/lain/src/new_fuzzed.rs index 3fb788c..77e439a 100644 --- a/lain/src/new_fuzzed.rs +++ b/lain/src/new_fuzzed.rs @@ -667,7 +667,7 @@ macro_rules! impl_new_fuzzed { if mutator.gen_chance(crate::mutator::CHANCE_TO_IGNORE_MIN_MAX) { $name::max_value() } else { - ignore_min = false; + ignore_max = false; *max } } else { @@ -847,4 +847,4 @@ impl NewFuzzed for *const std::ffi::c_void { ) -> Self { std::ptr::null() } -} \ No newline at end of file +} diff --git a/lain_derive/src/mutations.rs b/lain_derive/src/mutations.rs index d409421..9f2dd8f 100644 --- a/lain_derive/src/mutations.rs +++ b/lain_derive/src/mutations.rs @@ -248,7 +248,7 @@ fn new_fuzzed_enum ( cont_ident: &syn::Ident, ) -> TokenStream { let constraints_prelude = constraints_prelude(); - let (weights, new_fuzzed_fields) = new_fuzzed_enum_visitor(variants, cont_ident); + let (weights, new_fuzzed_fields, ignore_chances) = new_fuzzed_enum_visitor(variants, cont_ident); let variant_count = new_fuzzed_fields.len(); if new_fuzzed_fields.is_empty() { @@ -276,6 +276,7 @@ fn new_fuzzed_enum ( use _lain::rand::distributions::Distribution; static weights: [u64; #variant_count] = [#(#weights,)*]; + static ignore_chances: [f32; #variant_count] = [#(#ignore_chances,)*]; _lain::lazy_static::lazy_static! { static ref dist: _lain::rand::distributions::WeightedIndex = @@ -284,7 +285,22 @@ fn new_fuzzed_enum ( #constraints_prelude - let idx: usize = dist.sample(&mut mutator.rng); + // compiler analysis doesn't think we loop at least once, so it thinks this + // var is uninitialized. this is a stupid bypass + let mut idx: Option = None; + // loop a max of 5 times to avoid an infinite loop + for _i in 0..5 { + idx = Some(dist.sample(&mut mutator.rng)); + let chance = ignore_chances[idx.unwrap()]; + + // negate the gen_chance call since this is a chance to *ignore* + if chance >= 100.0 || !mutator.gen_chance(chance) { + break; + } + } + + let idx = idx.unwrap(); + match idx { #(#match_arms)* _ => unreachable!(), @@ -293,7 +309,7 @@ fn new_fuzzed_enum ( } fn new_fuzzed_unit_enum(variants: &[Variant], cont_ident: &syn::Ident) -> TokenStream { - let (weights, variant_tokens) = new_fuzzed_unit_enum_visitor(variants, cont_ident); + let (weights, variant_tokens, ignore_chances) = new_fuzzed_unit_enum_visitor(variants, cont_ident); if variant_tokens.is_empty() { return quote!{Default::default()}; @@ -301,13 +317,12 @@ fn new_fuzzed_unit_enum(variants: &[Variant], cont_ident: &syn::Ident) -> TokenS let variant_count = variant_tokens.len(); - quote! { use _lain::rand::seq::SliceRandom; use _lain::rand::distributions::Distribution; static options: [#cont_ident; #variant_count] = [#(#variant_tokens,)*]; - + static ignore_chances: [f32; #variant_count] = [#(#ignore_chances,)*]; static weights: [u64; #variant_count] = [#(#weights,)*]; _lain::lazy_static::lazy_static! { @@ -315,8 +330,23 @@ fn new_fuzzed_unit_enum(variants: &[Variant], cont_ident: &syn::Ident) -> TokenS _lain::rand::distributions::WeightedIndex::new(weights.iter()).unwrap(); } - let idx: usize = dist.sample(&mut mutator.rng); - options[idx] + // this shouldn't need to be an option but is because the compiler analysis + // doesn't think the loop will go at least once + let mut option: Option<#cont_ident> = None; + + // loop a max of 5 times so we don't infinite loop + for _i in 0..5 { + let idx: usize = dist.sample(&mut mutator.rng); + option = Some(options[idx]); + + let chance = ignore_chances[idx]; + // negate gen_chance since it's a chance to *ignore* + if chance >= 100.0 || !mutator.gen_chance(chance) { + break; + } + } + + option.unwrap() } } @@ -572,32 +602,38 @@ fn field_mutator(field: &Field, name_prefix: &'static str, is_destructured: bool fn new_fuzzed_unit_enum_visitor( variants: &[Variant], cont_ident: &syn::Ident, -) -> (Vec, Vec) { +) -> (Vec, Vec, Vec) { let mut weights = vec![]; + let mut ignore_chances = vec![]; let variants = variants.iter().filter_map(|variant| { - if variant.attrs.ignore() || variant.attrs.ignore_chance().is_some() { + if variant.attrs.ignore() { None } else { let variant_ident = &variant.ident; + weights.push(variant.attrs.weight().unwrap_or(1)); + ignore_chances.push(variant.attrs.ignore_chance().unwrap_or(100.0)); + Some(quote!{#cont_ident::#variant_ident}) } }) .collect(); - (weights, variants) + (weights, variants, ignore_chances) } fn new_fuzzed_enum_visitor( variants: &[Variant], cont_ident: &syn::Ident, -) -> (Vec, Vec) { +) -> (Vec, Vec, Vec) { let mut weights = vec![]; + let mut ignore_chances = vec![]; + let initializers = variants .iter() .filter_map(|variant| { - if variant.attrs.ignore() || variant.attrs.ignore_chance().is_some() { + if variant.attrs.ignore() { return None; } @@ -607,7 +643,9 @@ fn new_fuzzed_enum_visitor( let field_initializers: Vec = variant.fields.iter().map(|field| { let (value_ident, _field_ident_string, initializer) = field_initializer(field, "__field"); + field_identifiers.push(quote_spanned!{ field.member.span() => #value_ident }); + ignore_chances.push(variant.attrs.ignore_chance().unwrap_or(100.0)); initializer }) @@ -625,7 +663,7 @@ fn new_fuzzed_enum_visitor( }) .collect(); - (weights, initializers) + (weights, initializers, ignore_chances) } fn constraints_prelude() -> TokenStream { diff --git a/testsuite/Cargo.toml b/testsuite/Cargo.toml index adf1667..5074fc2 100644 --- a/testsuite/Cargo.toml +++ b/testsuite/Cargo.toml @@ -23,6 +23,3 @@ harness = false [[bench]] name = "benchmark_generating_fuzzed_struct" harness = false - -[profile.release] -debug = true \ No newline at end of file