Skip to content

Commit

Permalink
Add a fuzz target for exercising bounds checks with various memory co…
Browse files Browse the repository at this point in the history
…nfigs
  • Loading branch information
fitzgen committed Jun 4, 2024
1 parent 38bc915 commit 3019137
Show file tree
Hide file tree
Showing 7 changed files with 432 additions and 9 deletions.
5 changes: 4 additions & 1 deletion crates/fuzzing/src/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ pub use codegen_settings::CodegenSettings;
pub use config::CompilerStrategy;
pub use config::{Config, WasmtimeConfig};
pub use instance_allocation_strategy::InstanceAllocationStrategy;
pub use memory::{MemoryConfig, NormalMemoryConfig, UnalignedMemory, UnalignedMemoryCreator};
pub use memory::{
HeapImage, MemoryAccesses, MemoryConfig, NormalMemoryConfig, UnalignedMemory,
UnalignedMemoryCreator,
};
pub use module::ModuleConfig;
pub use pooling_config::PoolingAllocationConfig;
pub use single_inst_module::SingleInstModule;
Expand Down
14 changes: 6 additions & 8 deletions crates/fuzzing/src/generators/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ impl Config {
.cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime())
.consume_fuel(self.wasmtime.consume_fuel)
.epoch_interruption(self.wasmtime.epoch_interruption)
.memory_init_cow(self.wasmtime.memory_init_cow)
.memory_guaranteed_dense_image_size(std::cmp::min(
// Clamp this at 16MiB so we don't get huge in-memory
// images during fuzzing.
Expand Down Expand Up @@ -259,27 +258,26 @@ impl Config {
static_memory_maximum_size: Some(4 << 30), // 4 GiB
static_memory_guard_size: Some(2 << 30), // 2 GiB
dynamic_memory_guard_size: Some(0),
dynamic_memory_reserved_for_growth: Some(0),
guard_before_linear_memory: false,
memory_init_cow: true,
})
} else {
self.wasmtime.memory_config.clone()
};

match &memory_config {
MemoryConfig::Normal(memory_config) => {
cfg.static_memory_maximum_size(
memory_config.static_memory_maximum_size.unwrap_or(0),
)
.static_memory_guard_size(memory_config.static_memory_guard_size.unwrap_or(0))
.dynamic_memory_guard_size(memory_config.dynamic_memory_guard_size.unwrap_or(0))
.guard_before_linear_memory(memory_config.guard_before_linear_memory);
memory_config.apply_to(&mut cfg);
}
MemoryConfig::CustomUnaligned => {
cfg.with_host_memory(Arc::new(UnalignedMemoryCreator))
.static_memory_maximum_size(0)
.dynamic_memory_guard_size(0)
.dynamic_memory_reserved_for_growth(0)
.static_memory_guard_size(0)
.guard_before_linear_memory(false);
.guard_before_linear_memory(false)
.memory_init_cow(false);
}
}
}
Expand Down
90 changes: 90 additions & 0 deletions crates/fuzzing/src/generators/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,75 @@ use arbitrary::{Arbitrary, Unstructured};
use std::ops::Range;
use wasmtime::{LinearMemory, MemoryCreator, MemoryType};

/// A description of a memory config, image, etc... that can be used to test
/// memory accesses.
#[derive(Debug)]
#[allow(missing_docs)]
pub struct MemoryAccesses {
pub memory_config: NormalMemoryConfig,
pub image: HeapImage,
pub offset: u32,
pub growth: u32,
}

impl<'a> Arbitrary<'a> for MemoryAccesses {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(MemoryAccesses {
memory_config: u.arbitrary()?,
image: u.arbitrary()?,
offset: u.arbitrary()?,
growth: u.arbitrary()?,
})
}
}

/// A memory heap image.
#[allow(missing_docs)]
pub struct HeapImage {
pub minimum: u32,
pub maximum: Option<u32>,
pub segments: Vec<(u32, Vec<u8>)>,
}

impl std::fmt::Debug for HeapImage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HeapImage")
.field("minimum", &self.minimum)
.field("maximum", &self.maximum)
.field("segments", &"..")
.finish()
}
}

impl<'a> Arbitrary<'a> for HeapImage {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let minimum = u.int_in_range(0..=4)?;
let maximum = if u.arbitrary()? {
Some(u.int_in_range(minimum..=10)?)
} else {
None
};
let mut segments = vec![];
if minimum > 0 {
for _ in 0..u.int_in_range(0..=4)? {
const WASM_PAGE_SIZE: u32 = 65536;
let last_addressable = WASM_PAGE_SIZE * minimum - 1;
let offset = u.int_in_range(0..=last_addressable)?;
let max_len =
std::cmp::min(u.len(), usize::try_from(last_addressable - offset).unwrap());
let len = u.int_in_range(0..=max_len)?;
let data = u.bytes(len)?.to_vec();
segments.push((offset, data));
}
}
Ok(HeapImage {
minimum,
maximum,
segments,
})
}
}

/// Configuration for linear memories in Wasmtime.
#[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
pub enum MemoryConfig {
Expand All @@ -27,7 +96,9 @@ pub struct NormalMemoryConfig {
pub static_memory_maximum_size: Option<u64>,
pub static_memory_guard_size: Option<u64>,
pub dynamic_memory_guard_size: Option<u64>,
pub dynamic_memory_reserved_for_growth: Option<u64>,
pub guard_before_linear_memory: bool,
pub memory_init_cow: bool,
}

impl<'a> Arbitrary<'a> for NormalMemoryConfig {
Expand All @@ -38,17 +109,36 @@ impl<'a> Arbitrary<'a> for NormalMemoryConfig {
static_memory_maximum_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
static_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
dynamic_memory_guard_size: <Option<u32> as Arbitrary>::arbitrary(u)?.map(Into::into),
dynamic_memory_reserved_for_growth: <Option<u32> as Arbitrary>::arbitrary(u)?
.map(Into::into),
guard_before_linear_memory: u.arbitrary()?,
memory_init_cow: u.arbitrary()?,
};

if let Some(dynamic) = ret.dynamic_memory_guard_size {
let statik = ret.static_memory_guard_size.unwrap_or(2 << 30);
ret.static_memory_guard_size = Some(statik.max(dynamic));
}

Ok(ret)
}
}

impl NormalMemoryConfig {
/// Apply this memory configuration to the given `wasmtime::Config`.
pub fn apply_to(&self, config: &mut wasmtime::Config) {
config
.static_memory_maximum_size(self.static_memory_maximum_size.unwrap_or(0))
.static_memory_guard_size(self.static_memory_guard_size.unwrap_or(0))
.dynamic_memory_guard_size(self.dynamic_memory_guard_size.unwrap_or(0))
.dynamic_memory_reserved_for_growth(
self.dynamic_memory_reserved_for_growth.unwrap_or(0),
)
.guard_before_linear_memory(self.guard_before_linear_memory)
.memory_init_cow(self.memory_init_cow);
}
}

/// A custom "linear memory allocator" for wasm which only works with the
/// "dynamic" mode of configuration where wasm always does explicit bounds
/// checks.
Expand Down
1 change: 1 addition & 0 deletions crates/fuzzing/src/oracles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod diff_wasmi;
pub mod diff_wasmtime;
pub mod dummy;
pub mod engine;
pub mod memory;
mod stacks;

use self::diff_wasmtime::WasmtimeInstance;
Expand Down
Loading

0 comments on commit 3019137

Please sign in to comment.