wasmtime_fuzzing/generators/
memory.rs1use anyhow::Result;
4use arbitrary::{Arbitrary, Unstructured};
5use wasmtime::{LinearMemory, MemoryCreator, MemoryType};
6
7#[derive(Debug)]
10pub struct MemoryAccesses {
11 pub config: crate::generators::Config,
13 pub image: HeapImage,
15 pub offset: u32,
18 pub growth: u32,
20}
21
22impl<'a> Arbitrary<'a> for MemoryAccesses {
23 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
24 let image = HeapImage::arbitrary(u)?;
25
26 let one_mib = 1 << 20; let max_growth = one_mib / (1 << image.page_size_log2.unwrap_or(16));
30 let mut growth: u32 = u.int_in_range(0..=max_growth)?;
31
32 if growth > 0 && u.ratio(1, 20)? {
36 growth = (growth - 1).next_power_of_two();
37 }
38
39 Ok(MemoryAccesses {
40 config: u.arbitrary()?,
41 image,
42 offset: u.arbitrary()?,
43 growth,
44 })
45 }
46}
47
48pub struct HeapImage {
50 pub minimum: u32,
52 pub maximum: Option<u32>,
54 pub memory64: bool,
56 pub page_size_log2: Option<u32>,
58 pub segments: Vec<(u32, Vec<u8>)>,
60}
61
62impl std::fmt::Debug for HeapImage {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 struct Segments<'a>(&'a [(u32, Vec<u8>)]);
65 impl std::fmt::Debug for Segments<'_> {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(f, "[..; {}]", self.0.len())
68 }
69 }
70
71 f.debug_struct("HeapImage")
72 .field("minimum", &self.minimum)
73 .field("maximum", &self.maximum)
74 .field("memory64", &self.memory64)
75 .field("page_size_log2", &self.page_size_log2)
76 .field("segments", &Segments(&self.segments))
77 .finish()
78 }
79}
80
81impl<'a> Arbitrary<'a> for HeapImage {
82 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
83 let minimum = u.int_in_range(0..=4)?;
84 let maximum = if u.arbitrary()? {
85 Some(u.int_in_range(minimum..=10)?)
86 } else {
87 None
88 };
89 let memory64 = u.arbitrary()?;
90 let page_size_log2 = match u.int_in_range(0..=2)? {
91 0 => None,
92 1 => Some(0),
93 2 => Some(16),
94 _ => unreachable!(),
95 };
96 let mut segments = vec![];
97 if minimum > 0 {
98 for _ in 0..u.int_in_range(0..=4)? {
99 let last_addressable = (1u32 << page_size_log2.unwrap_or(16)) * minimum - 1;
100 let offset = u.int_in_range(0..=last_addressable)?;
101 let max_len =
102 std::cmp::min(u.len(), usize::try_from(last_addressable - offset).unwrap());
103 let len = u.int_in_range(0..=max_len)?;
104 let data = u.bytes(len)?.to_vec();
105 segments.push((offset, data));
106 }
107 }
108 Ok(HeapImage {
109 minimum,
110 maximum,
111 memory64,
112 page_size_log2,
113 segments,
114 })
115 }
116}
117
118#[derive(Arbitrary, Clone, Debug, Eq, Hash, PartialEq)]
120pub enum MemoryConfig {
121 Normal(NormalMemoryConfig),
125
126 CustomUnaligned,
130}
131
132#[derive(Clone, Debug, Eq, Hash, PartialEq)]
135#[expect(missing_docs, reason = "self-describing fields")]
136pub struct NormalMemoryConfig {
137 pub memory_reservation: Option<u64>,
138 pub memory_guard_size: Option<u64>,
139 pub memory_reservation_for_growth: Option<u64>,
140 pub guard_before_linear_memory: bool,
141 pub cranelift_enable_heap_access_spectre_mitigations: Option<bool>,
142 pub memory_init_cow: bool,
143}
144
145impl<'a> Arbitrary<'a> for NormalMemoryConfig {
146 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
147 Ok(Self {
148 memory_reservation: interesting_virtual_memory_size(u, 33)?,
151
152 memory_guard_size: interesting_virtual_memory_size(u, 32)?,
154
155 memory_reservation_for_growth: interesting_virtual_memory_size(u, 30)?,
158
159 guard_before_linear_memory: u.arbitrary()?,
160 cranelift_enable_heap_access_spectre_mitigations: u.arbitrary()?,
161 memory_init_cow: u.arbitrary()?,
162 })
163 }
164}
165
166fn interesting_virtual_memory_size(
169 u: &mut Unstructured<'_>,
170 max_log2: u32,
171) -> arbitrary::Result<Option<u64>> {
172 if u.ratio(3, 4)? {
174 return Ok(None);
175 }
176
177 #[derive(Arbitrary)]
179 enum Interesting {
180 Zero,
181 PowerOfTwo,
182 Arbitrary,
183 }
184
185 let size = match u.arbitrary()? {
186 Interesting::Zero => 0,
187 Interesting::PowerOfTwo => 1 << u.int_in_range(0..=max_log2)?,
188 Interesting::Arbitrary => u.int_in_range(0..=1 << max_log2)?,
189 };
190 Ok(Some(size))
191}
192
193impl NormalMemoryConfig {
194 pub fn configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions) {
196 cfg.opts.memory_reservation = self.memory_reservation;
197 cfg.opts.memory_guard_size = self.memory_guard_size;
198 cfg.opts.memory_reservation_for_growth = self.memory_reservation_for_growth;
199 cfg.opts.guard_before_linear_memory = Some(self.guard_before_linear_memory);
200 cfg.opts.memory_init_cow = Some(self.memory_init_cow);
201
202 if let Some(enable) = self.cranelift_enable_heap_access_spectre_mitigations {
203 cfg.codegen.cranelift.push((
204 "enable_heap_access_spectre_mitigation".to_string(),
205 Some(enable.to_string()),
206 ));
207 }
208 }
209}
210
211pub struct UnalignedMemory {
221 src: Vec<u8>,
224}
225
226unsafe impl LinearMemory for UnalignedMemory {
227 fn byte_size(&self) -> usize {
228 self.src.len() - 1
231 }
232
233 fn byte_capacity(&self) -> usize {
234 self.src.capacity() - 1
235 }
236
237 fn grow_to(&mut self, new_size: usize) -> Result<()> {
238 self.src.resize(new_size + 1, 0);
240 Ok(())
241 }
242
243 fn as_ptr(&self) -> *mut u8 {
244 self.src[1..].as_ptr() as *mut _
247 }
248}
249
250pub struct UnalignedMemoryCreator;
252
253unsafe impl MemoryCreator for UnalignedMemoryCreator {
254 fn new_memory(
255 &self,
256 _ty: MemoryType,
257 minimum: usize,
258 _maximum: Option<usize>,
259 reserved_size_in_bytes: Option<usize>,
260 guard_size_in_bytes: usize,
261 ) -> Result<Box<dyn LinearMemory>, String> {
262 assert_eq!(guard_size_in_bytes, 0);
263 assert!(reserved_size_in_bytes.is_none() || reserved_size_in_bytes == Some(0));
264 Ok(Box::new(UnalignedMemory {
265 src: vec![0; minimum + 1],
266 }))
267 }
268}