use arbitrary::{Arbitrary, Unstructured};
use core::cmp;
use wasmi::CompilationMode;
#[derive(Debug, Copy, Clone)]
pub struct FuzzWasmiConfig {
pub consume_fuel: bool,
pub validation_mode: ValidationMode,
pub translation_mode: CompilationMode,
}
#[derive(Debug, Copy, Clone)]
pub enum ValidationMode {
Checked,
Unchecked,
}
impl From<FuzzWasmiConfig> for wasmi::Config {
fn from(fuzz: FuzzWasmiConfig) -> Self {
let mut config = wasmi::Config::default();
config.compilation_mode(fuzz.translation_mode);
config.consume_fuel(fuzz.consume_fuel);
config.wasm_custom_page_sizes(true);
config.wasm_wide_arithmetic(true);
config
}
}
impl Arbitrary<'_> for FuzzWasmiConfig {
fn arbitrary(u: &mut Unstructured) -> arbitrary::Result<Self> {
let bits = u8::arbitrary(u)?;
let consume_fuel = (bits & 0x1) != 0;
let validation_mode = match (bits >> 2) & 0x1 {
0 => ValidationMode::Unchecked,
_ => ValidationMode::Checked,
};
let translation_mode = match (bits >> 3) & 0b11 {
0b00 => CompilationMode::Lazy,
0b01 => CompilationMode::LazyTranslation,
_ => CompilationMode::Eager,
};
Ok(Self {
consume_fuel,
validation_mode,
translation_mode,
})
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<u8 as Arbitrary>::size_hint(depth)
}
}
#[derive(Debug)]
pub struct FuzzSmithConfig {
inner: wasm_smith::Config,
}
impl Arbitrary<'_> for FuzzSmithConfig {
fn arbitrary(u: &mut Unstructured) -> arbitrary::Result<Self> {
const MAX_ENTITIES: usize = 1_000;
const MAX_DYN_ENTITIES: usize = 100;
const MAX_BUFFERS: u64 = 1_000_000;
let config = wasm_smith::Config {
max_types: u.int_in_range(0..=MAX_ENTITIES)?,
max_imports: u.int_in_range(0..=MAX_ENTITIES)?,
max_tags: 0, max_funcs: u.int_in_range(0..=MAX_ENTITIES)?,
max_globals: u.int_in_range(0..=MAX_ENTITIES)?,
max_exports: u.int_in_range(0..=MAX_ENTITIES)?,
max_element_segments: u.int_in_range(0..=MAX_ENTITIES)?,
max_elements: u.int_in_range(0..=MAX_ENTITIES)?,
max_data_segments: u.int_in_range(0..=MAX_ENTITIES)?,
max_instructions: u.int_in_range(0..=MAX_ENTITIES)?,
max_memories: u.int_in_range(0..=MAX_DYN_ENTITIES)?,
max_tables: u.int_in_range(0..=MAX_DYN_ENTITIES)?,
max_memory32_bytes: u.int_in_range(0..=MAX_BUFFERS)?,
max_memory64_bytes: u.int_in_range(0..=u128::from(MAX_BUFFERS))?,
min_uleb_size: u.int_in_range(0..=5)?,
max_table_elements: u.int_in_range(0..=MAX_BUFFERS)?,
custom_page_sizes_enabled: true,
bulk_memory_enabled: true,
reference_types_enabled: true,
simd_enabled: true,
relaxed_simd_enabled: true,
multi_value_enabled: true,
memory64_enabled: true,
saturating_float_to_int_enabled: true,
sign_extension_ops_enabled: true,
wide_arithmetic_enabled: true,
exceptions_enabled: false,
threads_enabled: false,
tail_call_enabled: true,
gc_enabled: false,
allow_floats: true,
canonicalize_nans: false,
export_everything: false,
..Default::default()
};
Ok(Self { inner: config })
}
}
impl FuzzSmithConfig {
pub fn allow_execution(&mut self) {
fn clamp_max<T: Ord + Copy>(this: &mut T, max: T) {
*this = (*this).min(max);
}
let Self { inner } = self;
clamp_max(&mut inner.max_table_elements, 100_000);
clamp_max(&mut inner.max_memory32_bytes, 1_000_000);
clamp_max(&mut inner.max_memory64_bytes, 1_000_000);
}
pub fn enable_nan_canonicalization(&mut self) {
self.inner.canonicalize_nans = true;
}
pub fn export_everything(&mut self) {
self.inner.export_everything = true;
}
pub fn disable_multi_memory(&mut self) {
self.inner.multi_value_enabled = false;
self.inner.max_memories = cmp::min(self.inner.max_memories, 1);
}
pub fn disable_custom_page_sizes(&mut self) {
self.inner.custom_page_sizes_enabled = false;
}
pub fn disable_wide_arithmetic(&mut self) {
self.inner.wide_arithmetic_enabled = false;
}
pub fn disable_memory64(&mut self) {
self.inner.memory64_enabled = false;
}
pub fn disable_simd(&mut self) {
self.inner.simd_enabled = false;
}
pub fn disable_relaxed_simd(&mut self) {
self.inner.relaxed_simd_enabled = false;
}
}
impl From<FuzzSmithConfig> for wasm_smith::Config {
fn from(config: FuzzSmithConfig) -> Self {
config.inner
}
}