Skip to content

Commit ebd4b68

Browse files
authored
Update some fuzzing defaults and infrastructure (#1793)
* Update some fuzzing defaults and infrastructure * Update `wasm_smith::Config` to default-enable some stage4+ proposals: `exceptions`, `gc`, `reference_types`, `relaxed_simd`, `simd`, `tail_call`, `threads`. These can still all be disabled via configuration and CLI flags. * All stage4+ proposals are now swarm-enabled through `Arbitrary for Config` * Default generation of modules in wasm-tools's own fuzzing no longer special-cases these proposals since they're all already handled. * The `WasmFeatures` used for validating fuzz-generated modules now starts with a minimal baseline set of features to ensure that all proposals are disabled in the validator if the corresponding wasm-smith configuration flag is disabled. * The `wasm-mutate` crate was updated to return errors instead of panicking for unsupported wasm proposals. All wasm proposals are now enabled when passing to `wasm-mutate`. The primary motivation for this commit was this last point where I'm seeing panics on OSS-Fuzz for Wasmtime using `wasm-mutate` as a mutation hook because `wasm-mutate` is panicking on some GC types. When fixing that I noticed other fuzz-related things I wanted to clean up while I was here. * Fix some feature handling in more places
1 parent 3226b14 commit ebd4b68

10 files changed

Lines changed: 81 additions & 59 deletions

File tree

crates/fuzz-stats/src/bin/failed-instantiations.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ impl State {
9999
let mut config = wasm_smith::Config::arbitrary(&mut u)?;
100100
config.allow_start_export = false;
101101

102+
config.exceptions_enabled = false; // Not implemented by Wasmtime
103+
config.threads_enabled = false; // not enabled by default in Wasmtime
104+
102105
// NB: just added "table64" support to this and wasmtime doesn't
103106
// implement that yet
104107
config.memory64_enabled = false;

crates/wasm-mutate/src/info.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
module::{PrimitiveTypeInfo, TypeInfo},
3-
Result,
3+
Error, Result,
44
};
55
use std::collections::HashSet;
66
use std::ops::Range;
@@ -92,7 +92,7 @@ impl<'a> ModuleInfo<'a> {
9292

9393
// Save function types
9494
for ty in reader.into_iter_err_on_gc_types() {
95-
info.types_map.push(ty?.into());
95+
info.types_map.push(ty?.try_into()?);
9696
}
9797
}
9898
Payload::ImportSection(reader) => {
@@ -107,7 +107,7 @@ impl<'a> ModuleInfo<'a> {
107107
info.imported_functions_count += 1;
108108
}
109109
wasmparser::TypeRef::Global(ty) => {
110-
let ty = PrimitiveTypeInfo::try_from(ty.content_type).unwrap();
110+
let ty = PrimitiveTypeInfo::try_from(ty.content_type)?;
111111
info.global_types.push(ty);
112112
info.imported_globals_count += 1;
113113
}
@@ -162,7 +162,7 @@ impl<'a> ModuleInfo<'a> {
162162
for ty in reader {
163163
let ty = ty?;
164164
// We only need the type of the global, not necessarily if is mutable or not
165-
let ty = PrimitiveTypeInfo::try_from(ty.ty.content_type).unwrap();
165+
let ty = PrimitiveTypeInfo::try_from(ty.ty.content_type)?;
166166
info.global_types.push(ty);
167167
}
168168
}
@@ -209,7 +209,7 @@ impl<'a> ModuleInfo<'a> {
209209
Payload::End(_) => {
210210
break;
211211
}
212-
_ => todo!("{:?} not implemented", payload),
212+
_ => return Err(Error::unsupported(format!("section: {payload:?}"))),
213213
}
214214
wasm = &wasm[consumed..];
215215
}

crates/wasm-mutate/src/module.rs

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::Result;
1+
use crate::{Error, Result};
22
use wasm_encoder::{BlockType, HeapType, RefType, ValType};
33

44
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -25,43 +25,49 @@ pub enum TypeInfo {
2525
// TODO: module linking support will require instance and module types.
2626
}
2727

28-
impl From<wasmparser::ValType> for PrimitiveTypeInfo {
29-
fn from(value: wasmparser::ValType) -> Self {
30-
match value {
28+
impl TryFrom<wasmparser::ValType> for PrimitiveTypeInfo {
29+
type Error = Error;
30+
31+
fn try_from(value: wasmparser::ValType) -> Result<Self> {
32+
Ok(match value {
3133
wasmparser::ValType::I32 => PrimitiveTypeInfo::I32,
3234
wasmparser::ValType::I64 => PrimitiveTypeInfo::I64,
3335
wasmparser::ValType::F32 => PrimitiveTypeInfo::F32,
3436
wasmparser::ValType::F64 => PrimitiveTypeInfo::F64,
3537
wasmparser::ValType::V128 => PrimitiveTypeInfo::V128,
36-
wasmparser::ValType::Ref(t) => t.into(),
37-
}
38+
wasmparser::ValType::Ref(t) => t.try_into()?,
39+
})
3840
}
3941
}
4042

41-
impl From<wasmparser::RefType> for PrimitiveTypeInfo {
42-
fn from(value: wasmparser::RefType) -> Self {
43-
match value {
43+
impl TryFrom<wasmparser::RefType> for PrimitiveTypeInfo {
44+
type Error = Error;
45+
46+
fn try_from(value: wasmparser::RefType) -> Result<Self> {
47+
Ok(match value {
4448
wasmparser::RefType::FUNCREF => PrimitiveTypeInfo::FuncRef,
4549
wasmparser::RefType::EXTERNREF => PrimitiveTypeInfo::ExternRef,
46-
_ => unimplemented!(),
47-
}
50+
other => return Err(Error::unsupported(format!("type {other:?}"))),
51+
})
4852
}
4953
}
5054

51-
impl From<wasmparser::FuncType> for TypeInfo {
52-
fn from(ft: wasmparser::FuncType) -> Self {
53-
TypeInfo::Func(FuncInfo {
55+
impl TryFrom<wasmparser::FuncType> for TypeInfo {
56+
type Error = Error;
57+
58+
fn try_from(ft: wasmparser::FuncType) -> Result<Self> {
59+
Ok(TypeInfo::Func(FuncInfo {
5460
params: ft
5561
.params()
5662
.iter()
57-
.map(|&t| PrimitiveTypeInfo::from(t))
58-
.collect(),
63+
.map(|&t| PrimitiveTypeInfo::try_from(t))
64+
.collect::<Result<_>>()?,
5965
returns: ft
6066
.results()
6167
.iter()
62-
.map(|&t| PrimitiveTypeInfo::from(t))
63-
.collect(),
64-
})
68+
.map(|&t| PrimitiveTypeInfo::try_from(t))
69+
.collect::<Result<_>>()?,
70+
}))
6571
}
6672
}
6773

crates/wasm-mutate/src/mutators/peephole.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl PeepholeMutator {
8989
}
9090
for _ in 0..localsreader.get_count() {
9191
let (count, ty) = localsreader.read()?;
92-
let tymapped = PrimitiveTypeInfo::from(ty);
92+
let tymapped = PrimitiveTypeInfo::try_from(ty)?;
9393
for _ in 0..count {
9494
all_locals.push(tymapped);
9595
}

crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ impl PeepholeMutationAnalysis {
290290
Lang::TableSet(..) => Ok(PrimitiveTypeInfo::Empty),
291291
Lang::TableGet(idx, _) => {
292292
let ty = self.table_types[*idx as usize];
293-
Ok(ty.element_type.into())
293+
ty.element_type.try_into()
294294
}
295295
Lang::I32UseGlobal(_) => Ok(PrimitiveTypeInfo::I32),
296296
Lang::I64UseGlobal(_) => Ok(PrimitiveTypeInfo::I64),

crates/wasm-smith/src/config.rs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ define_config! {
299299
/// Determines whether the exception-handling proposal is enabled for
300300
/// generating instructions.
301301
///
302-
/// Defaults to `false`.
303-
pub exceptions_enabled: bool = false,
302+
/// Defaults to `true`.
303+
pub exceptions_enabled: bool = true,
304304

305305
/// Export all WebAssembly objects in the module. Defaults to false.
306306
///
@@ -310,8 +310,8 @@ define_config! {
310310
/// Determines whether the GC proposal is enabled when generating a Wasm
311311
/// module.
312312
///
313-
/// Defaults to `false`.
314-
pub gc_enabled: bool = false,
313+
/// Defaults to `true`.
314+
pub gc_enabled: bool = true,
315315

316316
/// Determines whether the custom-page-sizes proposal is enabled when
317317
/// generating a Wasm module.
@@ -532,14 +532,14 @@ define_config! {
532532
/// Determines whether the reference types proposal is enabled for
533533
/// generating instructions.
534534
///
535-
/// Defaults to `false`.
536-
pub reference_types_enabled: bool = false,
535+
/// Defaults to `true`.
536+
pub reference_types_enabled: bool = true,
537537

538538
/// Determines whether the Relaxed SIMD proposal is enabled for
539539
/// generating instructions.
540540
///
541-
/// Defaults to `false`.
542-
pub relaxed_simd_enabled: bool = false,
541+
/// Defaults to `true`.
542+
pub relaxed_simd_enabled: bool = true,
543543

544544
/// Determines whether the nontrapping-float-to-int-conversions propsal
545545
/// is enabled.
@@ -555,14 +555,14 @@ define_config! {
555555
/// Determines whether the SIMD proposal is enabled for generating
556556
/// instructions.
557557
///
558-
/// Defaults to `false`.
559-
pub simd_enabled: bool = false,
558+
/// Defaults to `true`.
559+
pub simd_enabled: bool = true,
560560

561561
/// Determines whether the tail calls proposal is enabled for generating
562562
/// instructions.
563563
///
564-
/// Defaults to `false`.
565-
pub tail_call_enabled: bool = false,
564+
/// Defaults to `true`.
565+
pub tail_call_enabled: bool = true,
566566

567567
/// Whether every Wasm table must have a maximum size
568568
/// specified. Defaults to `false`.
@@ -575,8 +575,8 @@ define_config! {
575575
///
576576
/// [threads proposal]: https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md
577577
///
578-
/// Defaults to `false`.
579-
pub threads_enabled: bool = false,
578+
/// Defaults to `true`.
579+
pub threads_enabled: bool = true,
580580

581581
/// Indicates whether wasm-smith is allowed to generate invalid function
582582
/// bodies.
@@ -654,6 +654,7 @@ impl<'a> Arbitrary<'a> for Config {
654654

655655
let reference_types_enabled: bool = u.arbitrary()?;
656656
let max_tables = if reference_types_enabled { 100 } else { 1 };
657+
let simd_enabled: bool = u.arbitrary()?;
657658

658659
Ok(Config {
659660
max_types: u.int_in_range(0..=MAX_MAXIMUM)?,
@@ -679,6 +680,11 @@ impl<'a> Arbitrary<'a> for Config {
679680
max_nesting_depth: u.int_in_range(0..=10)?,
680681
saturating_float_to_int_enabled: u.arbitrary()?,
681682
sign_extension_ops_enabled: u.arbitrary()?,
683+
relaxed_simd_enabled: simd_enabled && u.arbitrary()?,
684+
exceptions_enabled: u.arbitrary()?,
685+
threads_enabled: u.arbitrary()?,
686+
tail_call_enabled: u.arbitrary()?,
687+
gc_enabled: reference_types_enabled && u.arbitrary()?,
682688
allowed_instructions: {
683689
use flagset::Flags;
684690
let mut allowed = Vec::new();
@@ -714,20 +720,17 @@ impl<'a> Arbitrary<'a> for Config {
714720
max_values: 0,
715721
memory_offset_choices: MemoryOffsetChoices::default(),
716722
allow_start_export: true,
717-
relaxed_simd_enabled: false,
718-
exceptions_enabled: false,
719-
memory64_enabled: false,
720723
max_type_size: 1000,
721724
canonicalize_nans: false,
722725
available_imports: None,
723726
exports: None,
724-
threads_enabled: false,
725727
export_everything: false,
726-
tail_call_enabled: false,
727-
gc_enabled: false,
728-
custom_page_sizes_enabled: false,
729728
generate_custom_sections: false,
730729
allow_invalid_funcs: false,
730+
731+
// Proposals that are not stage4+ are disabled by default.
732+
memory64_enabled: false,
733+
custom_page_sizes_enabled: false,
731734
})
732735
}
733736
}

crates/wasm-smith/tests/common/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub fn parser_features_from_config(config: &Config) -> WasmFeatures {
2525
features.set(WasmFeatures::TAIL_CALL, config.tail_call_enabled);
2626
features.set(WasmFeatures::FUNCTION_REFERENCES, config.gc_enabled);
2727
features.set(WasmFeatures::GC, config.gc_enabled);
28+
features.set(WasmFeatures::THREADS, config.threads_enabled);
2829
features.set(
2930
WasmFeatures::CUSTOM_PAGE_SIZES,
3031
config.custom_page_sizes_enabled,

crates/wasm-smith/tests/core.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ fn smoke_can_smith_valid_webassembly_one_point_oh() {
9393
cfg.relaxed_simd_enabled = false;
9494
cfg.exceptions_enabled = false;
9595
cfg.memory64_enabled = false;
96+
cfg.reference_types_enabled = false;
97+
cfg.gc_enabled = false;
9698
cfg.max_memories = 1;
9799
cfg.max_tables = 1;
98100
let features = parser_features_from_config(&cfg);

fuzz/src/lib.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,10 @@ pub fn generate_valid_module(
2323

2424
// These are disabled in the swarm config by default, but we want to test
2525
// them. Use the input data to determine whether these features are enabled.
26-
config.simd_enabled = u.arbitrary()?;
27-
config.relaxed_simd_enabled = config.simd_enabled && u.arbitrary()?;
2826
config.memory64_enabled = u.arbitrary()?;
29-
config.threads_enabled = u.arbitrary()?;
30-
config.exceptions_enabled = u.arbitrary()?;
3127
config.canonicalize_nans = u.arbitrary()?;
32-
config.tail_call_enabled = u.arbitrary()?;
3328
config.custom_page_sizes_enabled = u.arbitrary()?;
3429

35-
config.gc_enabled = u.arbitrary()?;
36-
config.reference_types_enabled = config.reference_types_enabled || config.gc_enabled;
37-
3830
configure(&mut config, u)?;
3931

4032
// Use wasm-smith to generate an arbitrary module and convert it to wasm
@@ -81,7 +73,21 @@ pub fn generate_valid_component(
8173
}
8274

8375
pub fn validator_for_config(config: &Config) -> wasmparser::Validator {
84-
let mut features = WasmFeatures::default();
76+
// Start with the bare-bones set of features that wasm started with. Then
77+
// wasm-smith doesn't have knobs to enable/disable mutable globals so
78+
// unconditionally enable that as well.
79+
let mut features = WasmFeatures::WASM1 | WasmFeatures::MUTABLE_GLOBAL;
80+
81+
// Next conditionally enable/disable features based on `config`.
82+
features.set(
83+
WasmFeatures::SIGN_EXTENSION,
84+
config.sign_extension_ops_enabled,
85+
);
86+
features.set(WasmFeatures::TAIL_CALL, config.tail_call_enabled);
87+
features.set(
88+
WasmFeatures::SATURATING_FLOAT_TO_INT,
89+
config.saturating_float_to_int_enabled,
90+
);
8591
features.set(WasmFeatures::MULTI_VALUE, config.multi_value_enabled);
8692
features.set(WasmFeatures::MULTI_MEMORY, config.max_memories > 1);
8793
features.set(WasmFeatures::BULK_MEMORY, config.bulk_memory_enabled);

fuzz/src/mutate.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
1111

1212
let mut seed = 0;
1313
let mut preserve_semantics = false;
14-
let (wasm, _config) = crate::generate_valid_module(u, |config, u| {
15-
config.exceptions_enabled = false;
16-
config.gc_enabled = false;
14+
let (wasm, _config) = crate::generate_valid_module(u, |_config, u| {
15+
// NB: wasm-mutate is a general-purpose tool so unsupported proposals by
16+
// wasm-mutate are not disabled here. Those must be rejected with a
17+
// first-class error in wasm-mutate instead of panicking.
1718
seed = u.arbitrary()?;
1819
preserve_semantics = u.arbitrary()?;
1920
Ok(())

0 commit comments

Comments
 (0)