Path: blob/main/crates/fuzzing/src/generators/memory.rs
1693 views
//! Generate various kinds of Wasm memory.12use arbitrary::{Arbitrary, Unstructured};34/// A description of a memory config, image, etc... that can be used to test5/// memory accesses.6#[derive(Debug)]7pub struct MemoryAccesses {8/// The configuration to use with this test case.9pub config: crate::generators::Config,10/// The heap image to use with this test case.11pub image: HeapImage,12/// The offset immediate to encode in the `load{8,16,32,64}` functions'13/// various load instructions.14pub offset: u32,15/// The amount (in pages) to grow the memory.16pub growth: u32,17}1819impl<'a> Arbitrary<'a> for MemoryAccesses {20fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {21let image = HeapImage::arbitrary(u)?;2223// Don't grow too much, since oss-fuzz/asan get upset if we try,24// even if we allow it to fail.25let one_mib = 1 << 20; // 1 MiB26let max_growth = one_mib / (1 << image.page_size_log2.unwrap_or(16));27let mut growth: u32 = u.int_in_range(0..=max_growth)?;2829// Occasionally, round to a power of two, since these tend to be30// interesting numbers that overlap with the host page size and things31// like that.32if growth > 0 && u.ratio(1, 20)? {33growth = (growth - 1).next_power_of_two();34}3536Ok(MemoryAccesses {37config: u.arbitrary()?,38image,39offset: u.arbitrary()?,40growth,41})42}43}4445/// A memory heap image.46pub struct HeapImage {47/// The minimum size (in pages) of this memory.48pub minimum: u32,49/// The maximum size (in pages) of this memory.50pub maximum: Option<u32>,51/// Whether this memory should be indexed with `i64` (rather than `i32`).52pub memory64: bool,53/// The log2 of the page size for this memory.54pub page_size_log2: Option<u32>,55/// Data segments for this memory.56pub segments: Vec<(u32, Vec<u8>)>,57}5859impl std::fmt::Debug for HeapImage {60fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {61struct Segments<'a>(&'a [(u32, Vec<u8>)]);62impl std::fmt::Debug for Segments<'_> {63fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {64write!(f, "[..; {}]", self.0.len())65}66}6768f.debug_struct("HeapImage")69.field("minimum", &self.minimum)70.field("maximum", &self.maximum)71.field("memory64", &self.memory64)72.field("page_size_log2", &self.page_size_log2)73.field("segments", &Segments(&self.segments))74.finish()75}76}7778impl<'a> Arbitrary<'a> for HeapImage {79fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {80let minimum = u.int_in_range(0..=4)?;81let maximum = if u.arbitrary()? {82Some(u.int_in_range(minimum..=10)?)83} else {84None85};86let memory64 = u.arbitrary()?;87let page_size_log2 = match u.int_in_range(0..=2)? {880 => None,891 => Some(0),902 => Some(16),91_ => unreachable!(),92};93let mut segments = vec![];94if minimum > 0 {95for _ in 0..u.int_in_range(0..=4)? {96let last_addressable = (1u32 << page_size_log2.unwrap_or(16)) * minimum - 1;97let offset = u.int_in_range(0..=last_addressable)?;98let max_len =99std::cmp::min(u.len(), usize::try_from(last_addressable - offset).unwrap());100let len = u.int_in_range(0..=max_len)?;101let data = u.bytes(len)?.to_vec();102segments.push((offset, data));103}104}105Ok(HeapImage {106minimum,107maximum,108memory64,109page_size_log2,110segments,111})112}113}114115/// Represents a normal memory configuration for Wasmtime with the given116/// static and dynamic memory sizes.117#[derive(Clone, Debug, Eq, Hash, PartialEq)]118#[expect(missing_docs, reason = "self-describing fields")]119pub struct MemoryConfig {120pub memory_reservation: Option<u64>,121pub memory_guard_size: Option<u64>,122pub memory_reservation_for_growth: Option<u64>,123pub guard_before_linear_memory: bool,124pub cranelift_enable_heap_access_spectre_mitigations: Option<bool>,125pub memory_init_cow: bool,126}127128impl<'a> Arbitrary<'a> for MemoryConfig {129fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {130Ok(Self {131// Allow up to 8GiB reservations of the virtual address space for132// the initial memory reservation.133memory_reservation: interesting_virtual_memory_size(u, 33)?,134135// Allow up to 4GiB guard page reservations to be made.136memory_guard_size: interesting_virtual_memory_size(u, 32)?,137138// Allow up up to 1GiB extra memory to grow into for dynamic139// memories.140memory_reservation_for_growth: interesting_virtual_memory_size(u, 30)?,141142guard_before_linear_memory: u.arbitrary()?,143cranelift_enable_heap_access_spectre_mitigations: u.arbitrary()?,144memory_init_cow: u.arbitrary()?,145})146}147}148149/// Helper function to generate "interesting numbers" for virtual memory150/// configuration options that `Config` supports.151fn interesting_virtual_memory_size(152u: &mut Unstructured<'_>,153max_log2: u32,154) -> arbitrary::Result<Option<u64>> {155// Most of the time return "none" meaning "use the default settings".156if u.ratio(3, 4)? {157return Ok(None);158}159160// Otherwise do a split between various strategies.161#[derive(Arbitrary)]162enum Interesting {163Zero,164PowerOfTwo,165Arbitrary,166}167168let size = match u.arbitrary()? {169Interesting::Zero => 0,170Interesting::PowerOfTwo => 1 << u.int_in_range(0..=max_log2)?,171Interesting::Arbitrary => u.int_in_range(0..=1 << max_log2)?,172};173Ok(Some(size))174}175176impl MemoryConfig {177/// Apply this memory configuration to the given config.178pub fn configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions) {179cfg.opts.memory_reservation = self.memory_reservation;180cfg.opts.memory_guard_size = self.memory_guard_size;181cfg.opts.memory_reservation_for_growth = self.memory_reservation_for_growth;182cfg.opts.guard_before_linear_memory = Some(self.guard_before_linear_memory);183cfg.opts.memory_init_cow = Some(self.memory_init_cow);184185if let Some(enable) = self.cranelift_enable_heap_access_spectre_mitigations {186cfg.codegen.cranelift.push((187"enable_heap_access_spectre_mitigation".to_string(),188Some(enable.to_string()),189));190}191}192}193194195