diff --git a/crates/wasm-smith/src/config.rs b/crates/wasm-smith/src/config.rs index f7f248ff6c..2d807f192c 100644 --- a/crates/wasm-smith/src/config.rs +++ b/crates/wasm-smith/src/config.rs @@ -737,6 +737,17 @@ define_config! { /// /// Defaults to `true`. pub extended_const_enabled: bool = true, + + /// Fuel limiting factor used when generating constant expressions. + /// + /// Defaults to `50`. + pub const_expr_fuel: u32 = 50, + + /// Whether or not to limit the size of arrays generated in constant + /// expressions. + /// + /// Defaults to `false`. + pub limit_arrays_in_const_exprs: bool = false, } } @@ -847,6 +858,8 @@ impl<'a> Arbitrary<'a> for Config { disallow_traps: u.arbitrary()?, allow_floats: u.arbitrary()?, extended_const_enabled: u.arbitrary()?, + const_expr_fuel: u.int_in_range(0..=100)?, + limit_arrays_in_const_exprs: u.arbitrary()?, // These fields, unlike the ones above, are less useful to set. // They either make weird inputs or are for features not widely diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index 41a8803abf..a7281fba82 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -2044,8 +2044,6 @@ impl Module { u: &mut Unstructured, allow_defined_globals: bool, ) -> Result { - const MAX_CONST_EXPR_RECURSION_DEPTH: usize = 8; - #[derive(Clone, Copy)] enum Choice { GlobalGet(u32), @@ -2163,12 +2161,27 @@ impl Module { .all(|f| type_is_defaultable(f.element_type)) } + fn const_expr_bytes_for_array_length( + module: &mut Module, + u: &mut Unstructured<'_>, + allow_defined_globals: bool, + fuel: &mut u32, + ) -> Result> { + if module.config.limit_arrays_in_const_exprs { + let size = u.int_in_range(0..=*fuel)?; + *fuel -= size; + return Ok(encode_instrs([Instruction::I32Const(size as i32)])); + } + + const_expr_bytes(module, ValType::I32, u, allow_defined_globals, fuel) + } + fn const_expr_bytes( module: &mut Module, ty: ValType, u: &mut Unstructured<'_>, allow_defined_globals: bool, - fuel: usize, + fuel: &mut u32, ) -> Result> { let mut choices = Vec::new(); @@ -2215,7 +2228,7 @@ impl Module { continue; } if can_use_struct_new(module.ty(type_idx)) - && (fuel > 0 + && (*fuel > 0 || module.ty(type_idx).unwrap_struct().fields.is_empty()) { choices.push(Choice::StructNew(type_idx)); @@ -2230,7 +2243,7 @@ impl Module { if !module.ref_type_is_sub_type(produced, ref_ty) { continue; } - if fuel > 0 { + if *fuel > 0 { choices.push(Choice::ArrayNew(type_idx)); choices.push(Choice::ArrayNewFixed(type_idx)); if type_is_defaultable( @@ -2242,13 +2255,13 @@ impl Module { } let produced_i31 = abstract_ref(false, false, AbstractHeapType::I31); - if fuel > 0 && module.ref_type_is_sub_type(produced_i31, ref_ty) { + if *fuel > 0 && module.ref_type_is_sub_type(produced_i31, ref_ty) { choices.push(Choice::RefI31 { shared: false }); } if module.config.shared_everything_threads_enabled { let produced_i31 = abstract_ref(false, true, AbstractHeapType::I31); - if fuel > 0 && module.ref_type_is_sub_type(produced_i31, ref_ty) { + if *fuel > 0 && module.ref_type_is_sub_type(produced_i31, ref_ty) { choices.push(Choice::RefI31 { shared: true }); } } @@ -2257,7 +2270,7 @@ impl Module { HeapType::Abstract { shared, ty: AbstractHeapType::Any, - } if fuel > 0 => { + } if *fuel > 0 => { choices.push(Choice::AnyConvertExtern { nullable: ref_ty.nullable, shared, @@ -2266,7 +2279,7 @@ impl Module { HeapType::Abstract { shared, ty: AbstractHeapType::Extern, - } if fuel > 0 => { + } if *fuel > 0 => { choices.push(Choice::ExternConvertAny { nullable: ref_ty.nullable, shared, @@ -2279,6 +2292,7 @@ impl Module { } let choice = *u.choose(&choices)?; + *fuel = fuel.saturating_sub(1); Ok(match choice { Choice::GlobalGet(i) => encode_instrs([Instruction::GlobalGet(i)]), Choice::I32Const => encode_instrs([Instruction::I32Const(u.arbitrary()?)]), @@ -2308,7 +2322,7 @@ impl Module { field_ty, u, allow_defined_globals, - fuel.saturating_sub(1), + fuel, )?); } bytes.extend(encode_instrs([Instruction::StructNew(type_idx)])); @@ -2325,26 +2339,20 @@ impl Module { elem_ty, u, allow_defined_globals, - fuel.saturating_sub(1), + fuel, )?); - bytes.extend(const_expr_bytes( + bytes.extend(const_expr_bytes_for_array_length( module, - ValType::I32, u, allow_defined_globals, - fuel.saturating_sub(1), + fuel, )?); bytes.extend(encode_instrs([Instruction::ArrayNew(type_idx)])); bytes } Choice::ArrayNewDefault(type_idx) => { - let mut bytes = const_expr_bytes( - module, - ValType::I32, - u, - allow_defined_globals, - fuel.saturating_sub(1), - )?; + let mut bytes = + const_expr_bytes_for_array_length(module, u, allow_defined_globals, fuel)?; bytes.extend(encode_instrs([Instruction::ArrayNewDefault(type_idx)])); bytes } @@ -2359,7 +2367,7 @@ impl Module { elem_ty, u, allow_defined_globals, - fuel.saturating_sub(1), + fuel, )?); } bytes.extend(encode_instrs([Instruction::ArrayNewFixed { @@ -2369,13 +2377,8 @@ impl Module { bytes } Choice::RefI31 { shared } => { - let mut bytes = const_expr_bytes( - module, - ValType::I32, - u, - allow_defined_globals, - fuel.saturating_sub(1), - )?; + let mut bytes = + const_expr_bytes(module, ValType::I32, u, allow_defined_globals, fuel)?; bytes.extend(encode_instrs([if shared { Instruction::RefI31Shared } else { @@ -2389,7 +2392,7 @@ impl Module { ValType::Ref(abstract_ref(nullable, shared, AbstractHeapType::Extern)), u, allow_defined_globals, - fuel.saturating_sub(1), + fuel, )?; bytes.extend(encode_instrs([Instruction::AnyConvertExtern])); bytes @@ -2400,7 +2403,7 @@ impl Module { ValType::Ref(abstract_ref(nullable, shared, AbstractHeapType::Any)), u, allow_defined_globals, - fuel.saturating_sub(1), + fuel, )?; bytes.extend(encode_instrs([Instruction::ExternConvertAny])); bytes @@ -2408,12 +2411,13 @@ impl Module { }) } + let mut fuel = self.config.const_expr_fuel; Ok(ConstExpr::raw(const_expr_bytes( self, ty, u, allow_defined_globals, - MAX_CONST_EXPR_RECURSION_DEPTH, + &mut fuel, )?)) }