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 (#8742)
  • Loading branch information
fitzgen authored Jun 6, 2024
1 parent cdb5930 commit 7a37e31
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 10 deletions.
2 changes: 1 addition & 1 deletion cranelift/codegen/src/machinst/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
// Normal instruction: codegen if the instruction is side-effecting
// or any of its outputs is used.
if has_side_effect || value_needed {
trace!("lowering: inst {}: {:?}", inst, self.f.dfg.insts[inst]);
trace!("lowering: inst {}: {}", inst, self.f.dfg.display_inst(inst));
let temp_regs = backend.lower(self, inst).unwrap_or_else(|| {
let ty = if self.num_outputs(inst) > 0 {
Some(self.output_ty(inst, 0))
Expand Down
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
110 changes: 110 additions & 0 deletions crates/fuzzing/src/generators/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,95 @@ 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)]
pub struct MemoryAccesses {
/// The configuration to use with this test case.
pub config: crate::generators::Config,
/// The heap image to use with this test case.
pub image: HeapImage,
/// The offset immediate to encode in the `load{8,16,32,64}` functions'
/// various load instructions.
pub offset: u32,
/// The amount (in pages) to grow the memory.
pub growth: u32,
}

impl<'a> Arbitrary<'a> for MemoryAccesses {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(MemoryAccesses {
config: u.arbitrary()?,
image: u.arbitrary()?,
offset: u.arbitrary()?,
// Don't grow too much, since oss-fuzz/asan get upset if we try,
// even if we allow it to fail.
growth: u.int_in_range(0..=10)?,
})
}
}

/// A memory heap image.
pub struct HeapImage {
/// The minimum size (in pages) of this memory.
pub minimum: u32,
/// The maximum size (in pages) of this memory.
pub maximum: Option<u32>,
/// Whether this memory should be indexed with `i64` (rather than `i32`).
pub memory64: bool,
/// Data segments for this memory.
pub segments: Vec<(u32, Vec<u8>)>,
}

impl std::fmt::Debug for HeapImage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct Segments<'a>(&'a [(u32, Vec<u8>)]);
impl std::fmt::Debug for Segments<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[..; {}]", self.0.len())
}
}

f.debug_struct("HeapImage")
.field("minimum", &self.minimum)
.field("maximum", &self.maximum)
.field("memory64", &self.memory64)
.field("segments", &Segments(&self.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 memory64 = u.arbitrary()?;
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,
memory64,
segments,
})
}
}

/// Configuration for linear memories in Wasmtime.
#[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
pub enum MemoryConfig {
Expand All @@ -27,7 +116,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 +129,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 7a37e31

Please sign in to comment.