Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update some fuzzing defaults and infrastructure #1793

Merged
merged 2 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions crates/wasm-mutate/src/info.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
module::{PrimitiveTypeInfo, TypeInfo},
Result,
Error, Result,
};
use std::collections::HashSet;
use std::ops::Range;
Expand Down Expand Up @@ -92,7 +92,7 @@ impl<'a> ModuleInfo<'a> {

// Save function types
for ty in reader.into_iter_err_on_gc_types() {
info.types_map.push(ty?.into());
info.types_map.push(ty?.try_into()?);
}
}
Payload::ImportSection(reader) => {
Expand All @@ -107,7 +107,7 @@ impl<'a> ModuleInfo<'a> {
info.imported_functions_count += 1;
}
wasmparser::TypeRef::Global(ty) => {
let ty = PrimitiveTypeInfo::try_from(ty.content_type).unwrap();
let ty = PrimitiveTypeInfo::try_from(ty.content_type)?;
info.global_types.push(ty);
info.imported_globals_count += 1;
}
Expand Down Expand Up @@ -162,7 +162,7 @@ impl<'a> ModuleInfo<'a> {
for ty in reader {
let ty = ty?;
// We only need the type of the global, not necessarily if is mutable or not
let ty = PrimitiveTypeInfo::try_from(ty.ty.content_type).unwrap();
let ty = PrimitiveTypeInfo::try_from(ty.ty.content_type)?;
info.global_types.push(ty);
}
}
Expand Down Expand Up @@ -209,7 +209,7 @@ impl<'a> ModuleInfo<'a> {
Payload::End(_) => {
break;
}
_ => todo!("{:?} not implemented", payload),
_ => return Err(Error::unsupported(format!("section: {payload:?}"))),
}
wasm = &wasm[consumed..];
}
Expand Down
44 changes: 25 additions & 19 deletions crates/wasm-mutate/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::Result;
use crate::{Error, Result};
use wasm_encoder::{BlockType, HeapType, RefType, ValType};

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

impl From<wasmparser::ValType> for PrimitiveTypeInfo {
fn from(value: wasmparser::ValType) -> Self {
match value {
impl TryFrom<wasmparser::ValType> for PrimitiveTypeInfo {
type Error = Error;

fn try_from(value: wasmparser::ValType) -> Result<Self> {
Ok(match value {
wasmparser::ValType::I32 => PrimitiveTypeInfo::I32,
wasmparser::ValType::I64 => PrimitiveTypeInfo::I64,
wasmparser::ValType::F32 => PrimitiveTypeInfo::F32,
wasmparser::ValType::F64 => PrimitiveTypeInfo::F64,
wasmparser::ValType::V128 => PrimitiveTypeInfo::V128,
wasmparser::ValType::Ref(t) => t.into(),
}
wasmparser::ValType::Ref(t) => t.try_into()?,
})
}
}

impl From<wasmparser::RefType> for PrimitiveTypeInfo {
fn from(value: wasmparser::RefType) -> Self {
match value {
impl TryFrom<wasmparser::RefType> for PrimitiveTypeInfo {
type Error = Error;

fn try_from(value: wasmparser::RefType) -> Result<Self> {
Ok(match value {
wasmparser::RefType::FUNCREF => PrimitiveTypeInfo::FuncRef,
wasmparser::RefType::EXTERNREF => PrimitiveTypeInfo::ExternRef,
_ => unimplemented!(),
}
other => return Err(Error::unsupported(format!("type {other:?}"))),
})
}
}

impl From<wasmparser::FuncType> for TypeInfo {
fn from(ft: wasmparser::FuncType) -> Self {
TypeInfo::Func(FuncInfo {
impl TryFrom<wasmparser::FuncType> for TypeInfo {
type Error = Error;

fn try_from(ft: wasmparser::FuncType) -> Result<Self> {
Ok(TypeInfo::Func(FuncInfo {
params: ft
.params()
.iter()
.map(|&t| PrimitiveTypeInfo::from(t))
.collect(),
.map(|&t| PrimitiveTypeInfo::try_from(t))
.collect::<Result<_>>()?,
returns: ft
.results()
.iter()
.map(|&t| PrimitiveTypeInfo::from(t))
.collect(),
})
.map(|&t| PrimitiveTypeInfo::try_from(t))
.collect::<Result<_>>()?,
}))
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-mutate/src/mutators/peephole.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl PeepholeMutator {
}
for _ in 0..localsreader.get_count() {
let (count, ty) = localsreader.read()?;
let tymapped = PrimitiveTypeInfo::from(ty);
let tymapped = PrimitiveTypeInfo::try_from(ty)?;
for _ in 0..count {
all_locals.push(tymapped);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ impl PeepholeMutationAnalysis {
Lang::TableSet(..) => Ok(PrimitiveTypeInfo::Empty),
Lang::TableGet(idx, _) => {
let ty = self.table_types[*idx as usize];
Ok(ty.element_type.into())
ty.element_type.try_into()
}
Lang::I32UseGlobal(_) => Ok(PrimitiveTypeInfo::I32),
Lang::I64UseGlobal(_) => Ok(PrimitiveTypeInfo::I64),
Expand Down
45 changes: 24 additions & 21 deletions crates/wasm-smith/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ define_config! {
/// Determines whether the exception-handling proposal is enabled for
/// generating instructions.
///
/// Defaults to `false`.
pub exceptions_enabled: bool = false,
/// Defaults to `true`.
pub exceptions_enabled: bool = true,

/// Export all WebAssembly objects in the module. Defaults to false.
///
Expand All @@ -310,8 +310,8 @@ define_config! {
/// Determines whether the GC proposal is enabled when generating a Wasm
/// module.
///
/// Defaults to `false`.
pub gc_enabled: bool = false,
/// Defaults to `true`.
pub gc_enabled: bool = true,

/// Determines whether the custom-page-sizes proposal is enabled when
/// generating a Wasm module.
Expand Down Expand Up @@ -532,14 +532,14 @@ define_config! {
/// Determines whether the reference types proposal is enabled for
/// generating instructions.
///
/// Defaults to `false`.
pub reference_types_enabled: bool = false,
/// Defaults to `true`.
pub reference_types_enabled: bool = true,

/// Determines whether the Relaxed SIMD proposal is enabled for
/// generating instructions.
///
/// Defaults to `false`.
pub relaxed_simd_enabled: bool = false,
/// Defaults to `true`.
pub relaxed_simd_enabled: bool = true,

/// Determines whether the nontrapping-float-to-int-conversions propsal
/// is enabled.
Expand All @@ -555,14 +555,14 @@ define_config! {
/// Determines whether the SIMD proposal is enabled for generating
/// instructions.
///
/// Defaults to `false`.
pub simd_enabled: bool = false,
/// Defaults to `true`.
pub simd_enabled: bool = true,

/// Determines whether the tail calls proposal is enabled for generating
/// instructions.
///
/// Defaults to `false`.
pub tail_call_enabled: bool = false,
/// Defaults to `true`.
pub tail_call_enabled: bool = true,

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

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

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

Ok(Config {
max_types: u.int_in_range(0..=MAX_MAXIMUM)?,
Expand All @@ -679,6 +680,11 @@ impl<'a> Arbitrary<'a> for Config {
max_nesting_depth: u.int_in_range(0..=10)?,
saturating_float_to_int_enabled: u.arbitrary()?,
sign_extension_ops_enabled: u.arbitrary()?,
relaxed_simd_enabled: simd_enabled && u.arbitrary()?,
exceptions_enabled: u.arbitrary()?,
threads_enabled: u.arbitrary()?,
tail_call_enabled: u.arbitrary()?,
gc_enabled: reference_types_enabled && u.arbitrary()?,
allowed_instructions: {
use flagset::Flags;
let mut allowed = Vec::new();
Expand Down Expand Up @@ -714,20 +720,17 @@ impl<'a> Arbitrary<'a> for Config {
max_values: 0,
memory_offset_choices: MemoryOffsetChoices::default(),
allow_start_export: true,
relaxed_simd_enabled: false,
exceptions_enabled: false,
memory64_enabled: false,
max_type_size: 1000,
canonicalize_nans: false,
available_imports: None,
exports: None,
threads_enabled: false,
export_everything: false,
tail_call_enabled: false,
gc_enabled: false,
custom_page_sizes_enabled: false,
generate_custom_sections: false,
allow_invalid_funcs: false,

// Proposals that are not stage4+ are disabled by default.
memory64_enabled: false,
custom_page_sizes_enabled: false,
})
}
}
24 changes: 15 additions & 9 deletions fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,10 @@ pub fn generate_valid_module(

// These are disabled in the swarm config by default, but we want to test
// them. Use the input data to determine whether these features are enabled.
config.simd_enabled = u.arbitrary()?;
config.relaxed_simd_enabled = config.simd_enabled && u.arbitrary()?;
config.memory64_enabled = u.arbitrary()?;
config.threads_enabled = u.arbitrary()?;
config.exceptions_enabled = u.arbitrary()?;
config.canonicalize_nans = u.arbitrary()?;
config.tail_call_enabled = u.arbitrary()?;
config.custom_page_sizes_enabled = u.arbitrary()?;

config.gc_enabled = u.arbitrary()?;
config.reference_types_enabled = config.reference_types_enabled || config.gc_enabled;

configure(&mut config, u)?;

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

pub fn validator_for_config(config: &Config) -> wasmparser::Validator {
let mut features = WasmFeatures::default();
// Start with the bare-bones set of features that wasm started with. Then
// wasm-smith doesn't have knobs to enable/disable mutable globals so
// unconditionally enable that as well.
let mut features = WasmFeatures::WASM1 | WasmFeatures::MUTABLE_GLOBAL;

// Next conditionally enable/disable features based on `config`.
features.set(
WasmFeatures::SIGN_EXTENSION,
config.sign_extension_ops_enabled,
);
features.set(WasmFeatures::TAIL_CALL, config.tail_call_enabled);
features.set(
WasmFeatures::SATURATING_FLOAT_TO_INT,
config.saturating_float_to_int_enabled,
);
features.set(WasmFeatures::MULTI_VALUE, config.multi_value_enabled);
features.set(WasmFeatures::MULTI_MEMORY, config.max_memories > 1);
features.set(WasmFeatures::BULK_MEMORY, config.bulk_memory_enabled);
Expand Down
7 changes: 4 additions & 3 deletions fuzz/src/mutate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {

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