wasmtime_fuzzing/oracles/
memory.rs1use crate::generators::{HeapImage, MemoryAccesses};
4use wasmtime::*;
5
6pub fn check_memory_accesses(input: MemoryAccesses) {
9 crate::init_fuzzing();
10 log::info!("Testing memory accesses: {input:#x?}");
11
12 let offset = input.offset;
13 let growth = input.growth;
14 let wasm = build_wasm(&input.image, offset);
15 crate::oracles::log_wasm(&wasm);
16 let offset = u64::from(offset);
17
18 let mut config = input.config.to_wasmtime();
19
20 if input.image.memory64 {
22 config.wasm_memory64(true);
23 }
24 if input.image.page_size_log2.is_some() {
25 config.wasm_custom_page_sizes(true);
26 }
27
28 let engine = Engine::new(&config).unwrap();
29 let module = match Module::new(&engine, &wasm) {
30 Ok(m) => m,
31 Err(e) => {
32 let e = format!("{e:?}");
33 log::info!("Failed to create `Module`: {e}");
34 assert!(
35 e.contains("bytes which exceeds the configured maximum of")
36 || e.contains("exceeds the limit of"),
37 "bad module compilation error: {e:?}",
38 );
39 return;
40 }
41 };
42
43 let limits = super::StoreLimits::new();
44 let mut store = Store::new(&engine, limits);
45 input.config.configure_store(&mut store);
46
47 if input.config.wasmtime.consume_fuel {
49 store.set_fuel(u64::MAX).unwrap();
50 }
51
52 let instance = match Instance::new(&mut store, &module, &[]) {
53 Ok(x) => x,
54 Err(e) => {
55 log::info!("Failed to instantiate: {e:?}");
56 assert!(
57 format!("{e:?}").contains("Cannot allocate memory"),
58 "bad error: {e:?}",
59 );
60 return;
61 }
62 };
63
64 let memory = instance.get_memory(&mut store, "memory").unwrap();
65 let load8 = instance
66 .get_typed_func::<u64, u32>(&mut store, "load8")
67 .unwrap();
68 let load16 = instance
69 .get_typed_func::<u64, u32>(&mut store, "load16")
70 .unwrap();
71 let load32 = instance
72 .get_typed_func::<u64, u32>(&mut store, "load32")
73 .unwrap();
74 let load64 = instance
75 .get_typed_func::<u64, u64>(&mut store, "load64")
76 .unwrap();
77
78 let do_accesses = |store: &mut Store<_>, msg: &str| {
79 let len = memory.data_size(&mut *store);
80 let len = u64::try_from(len).unwrap();
81
82 if let Some(n) = len.checked_sub(8).and_then(|n| n.checked_sub(offset)) {
83 for i in 0..=7 {
85 let addr = n + i;
86 assert!(addr + offset + 1 <= len);
87 let result = load8.call(&mut *store, addr);
88 assert!(
89 result.is_ok(),
90 "{msg}: len={len:#x}, offset={offset:#x}, load8({n:#x} + {i:#x} = {addr:#x}) \
91 should be in bounds, got {result:?}"
92 );
93 }
94 for i in 0..=6 {
95 let addr = n + offset + i;
96 assert!(addr + 2 <= len);
97 let result = load16.call(&mut *store, n + i);
98 assert!(
99 result.is_ok(),
100 "{msg}: len={len:#x}, offset={offset:#x}, load16({n:#x} + {i:#x} = {addr:#x}) \
101 should be in bounds, got {result:?}"
102 );
103 }
104 for i in 0..=4 {
105 let addr = n + offset + i;
106 assert!(addr + 4 <= len);
107 let result = load32.call(&mut *store, n + i);
108 assert!(
109 result.is_ok(),
110 "{msg}: len={len:#x}, offset={offset:#x}, load32({n:#x} + {i:#x} = {addr:#x}) \
111 should be in bounds, got {result:?}"
112 );
113 }
114 assert!(n + offset + 8 <= len);
115 let result = load64.call(&mut *store, n);
116 assert!(
117 result.is_ok(),
118 "{msg}: len={len:#x}, offset={offset:#x}, load64({n:#x}) should be in bounds, \
119 got {result:?}"
120 );
121
122 for i in 1..2 {
124 let addr = len - i;
125 assert!(addr + offset + 2 > len);
126 let result = load16.call(&mut *store, addr);
127 assert!(
128 result.is_err(),
129 "{msg}: len={len:#x}, offset={offset:#x}, load16({len:#x} - {i:#x} = {addr:#x}) \
130 should trap, got {result:?}"
131 );
132 }
133 for i in 1..4 {
134 let addr = len - i;
135 assert!(addr + offset + 4 > len);
136 let result = load32.call(&mut *store, addr);
137 assert!(
138 result.is_err(),
139 "{msg}: len={len:#x}, offset={offset:#x}, load32({len:#x} - {i:#x} = {addr:#x}) \
140 should trap, got {result:?}"
141 );
142 }
143 for i in 1..8 {
144 let addr = len - i;
145 assert!(addr + offset + 8 > len);
146 let result = load64.call(&mut *store, addr);
147 assert!(
148 result.is_err(),
149 "{msg}: len={len:#x}, offset={offset:#x}, load64({len:#x} - {i:#x} = {addr:#x}) \
150 should trap, got {result:?}"
151 );
152 }
153 }
154
155 if let Some(n) = len.checked_sub(offset) {
157 for i in 0..=1 {
158 let addr = n + i;
159 assert!(addr + offset + 1 > len);
160 let result = load8.call(&mut *store, addr);
161 assert!(
162 result.is_err(),
163 "{msg}: len={len:#x}, offset={offset:#x}, load8({n:#x} + {i:#x} = {addr:#x}) \
164 should trap, got {result:?}"
165 );
166 assert!(addr + offset + 2 > len);
167 let result = load16.call(&mut *store, addr);
168 assert!(
169 result.is_err(),
170 "{msg}: len={len:#x}, offset={offset:#x}, load16({n:#x} + {i:#x} = {addr:#x}) \
171 should trap, got {result:?}"
172 );
173 assert!(addr + offset + 4 > len);
174 let result = load32.call(&mut *store, addr);
175 assert!(
176 result.is_err(),
177 "{msg}: len={len:#x}, offset={offset:#x}, load32({n:#x} + {i:#x} = {addr:#x}) \
178 should trap, got {result:?}"
179 );
180 assert!(addr + offset + 8 > len);
181 let result = load64.call(&mut *store, addr);
182 assert!(
183 result.is_err(),
184 "{msg}: len={len:#x}, offset={offset:#x}, load64({n:#x} + {i:#x} = {addr:#x}) \
185 should trap, got {result:?}"
186 );
187 }
188 }
189
190 let len_is_4gib = len == u64::from(u32::MAX) + 1;
193 let end_delta = (input.image.memory64 && len_is_4gib) as u64;
194 let max = if input.image.memory64 {
195 u64::MAX
196 } else {
197 u64::from(u32::MAX)
198 };
199 for i in 0..(1 - end_delta) {
200 let addr = max - i;
201 let result = load8.call(&mut *store, addr);
202 assert!(
203 result.is_err(),
204 "{msg}: len={len:#x}, offset={offset:#x}, load8({max:#x} - {i:#x} = {addr:#x}) \
205 should trap, got {result:?}"
206 );
207 }
208 for i in 0..(2 - end_delta) {
209 let addr = max - i;
210 let result = load16.call(&mut *store, addr);
211 assert!(
212 result.is_err(),
213 "{msg}: len={len:#x}, offset={offset:#x}, load16({max:#x} - {i:#x} = {addr:#x}) \
214 should trap, got {result:?}"
215 );
216 }
217 for i in 0..(4 - end_delta) {
218 let addr = max - i;
219 let result = load32.call(&mut *store, addr);
220 assert!(
221 result.is_err(),
222 "{msg}: len={len:#x}, offset={offset:#x}, load32({max:#x} - {i:#x} = {addr:#x}) \
223 should trap, got {result:?}"
224 );
225 }
226 for i in 0..(8 - end_delta) {
227 let addr = max - i;
228 let result = load64.call(&mut *store, addr);
229 assert!(
230 result.is_err(),
231 "{msg}: len={len:#x}, offset={offset:#x}, load64({max:#x} - {i:#x} = {addr:#x}) \
232 should trap, got {result:?}"
233 );
234 }
235 };
236
237 do_accesses(&mut store, "initial size");
238 let res = memory.grow(&mut store, u64::from(growth));
239 log::debug!("grow {growth} -> {res:?}");
240 do_accesses(&mut store, "after growing");
241}
242
243fn build_wasm(image: &HeapImage, offset: u32) -> Vec<u8> {
259 let mut module = wasm_encoder::Module::new();
260
261 {
262 let mut types = wasm_encoder::TypeSection::new();
263 types
264 .ty()
265 .function([wasm_encoder::ValType::I64], [wasm_encoder::ValType::I32]);
266 types
267 .ty()
268 .function([wasm_encoder::ValType::I64], [wasm_encoder::ValType::I64]);
269 module.section(&types);
270 }
271
272 {
273 let mut funcs = wasm_encoder::FunctionSection::new();
274 funcs.function(0);
275 funcs.function(0);
276 funcs.function(0);
277 funcs.function(1);
278 module.section(&funcs);
279 }
280
281 {
282 let mut memories = wasm_encoder::MemorySection::new();
283 memories.memory(wasm_encoder::MemoryType {
284 minimum: u64::from(image.minimum),
285 maximum: image.maximum.map(Into::into),
286 memory64: image.memory64,
287 shared: false,
288 page_size_log2: image.page_size_log2,
289 });
290 module.section(&memories);
291 }
292
293 {
294 let mut exports = wasm_encoder::ExportSection::new();
295 exports.export("memory", wasm_encoder::ExportKind::Memory, 0);
296 exports.export("load8", wasm_encoder::ExportKind::Func, 0);
297 exports.export("load16", wasm_encoder::ExportKind::Func, 1);
298 exports.export("load32", wasm_encoder::ExportKind::Func, 2);
299 exports.export("load64", wasm_encoder::ExportKind::Func, 3);
300 module.section(&exports);
301 }
302
303 {
304 let mut code = wasm_encoder::CodeSection::new();
305 {
306 let mut func = wasm_encoder::Function::new([]);
307 func.instruction(&wasm_encoder::Instruction::LocalGet(0));
308 if !image.memory64 {
309 func.instruction(&wasm_encoder::Instruction::I32WrapI64);
310 }
311 func.instruction(&wasm_encoder::Instruction::I32Load8U(
312 wasm_encoder::MemArg {
313 offset: u64::from(offset),
314 align: 0,
315 target: Default::default(),
316 ordering: None,
317 },
318 ));
319 func.instruction(&wasm_encoder::Instruction::End);
320 code.function(&func);
321 }
322 {
323 let mut func = wasm_encoder::Function::new([]);
324 func.instruction(&wasm_encoder::Instruction::LocalGet(0));
325 if !image.memory64 {
326 func.instruction(&wasm_encoder::Instruction::I32WrapI64);
327 }
328 func.instruction(&wasm_encoder::Instruction::I32Load16U(
329 wasm_encoder::MemArg {
330 offset: u64::from(offset),
331 align: 0,
332 target: Default::default(),
333 ordering: None,
334 },
335 ));
336 func.instruction(&wasm_encoder::Instruction::End);
337 code.function(&func);
338 }
339 {
340 let mut func = wasm_encoder::Function::new([]);
341 func.instruction(&wasm_encoder::Instruction::LocalGet(0));
342 if !image.memory64 {
343 func.instruction(&wasm_encoder::Instruction::I32WrapI64);
344 }
345 func.instruction(&wasm_encoder::Instruction::I32Load(wasm_encoder::MemArg {
346 offset: u64::from(offset),
347 align: 0,
348 target: Default::default(),
349 ordering: None,
350 }));
351 func.instruction(&wasm_encoder::Instruction::End);
352 code.function(&func);
353 }
354 {
355 let mut func = wasm_encoder::Function::new([]);
356 func.instruction(&wasm_encoder::Instruction::LocalGet(0));
357 if !image.memory64 {
358 func.instruction(&wasm_encoder::Instruction::I32WrapI64);
359 }
360 func.instruction(&wasm_encoder::Instruction::I64Load(wasm_encoder::MemArg {
361 offset: u64::from(offset),
362 align: 0,
363 target: Default::default(),
364 ordering: None,
365 }));
366 func.instruction(&wasm_encoder::Instruction::End);
367 code.function(&func);
368 }
369 module.section(&code);
370 }
371
372 {
373 let mut datas = wasm_encoder::DataSection::new();
374 for (offset, data) in image.segments.iter() {
375 datas.segment(wasm_encoder::DataSegment {
376 mode: wasm_encoder::DataSegmentMode::Active {
377 memory_index: 0,
378 offset: &if image.memory64 {
379 wasm_encoder::ConstExpr::i64_const(*offset as i64)
380 } else {
381 wasm_encoder::ConstExpr::i32_const(*offset as i32)
382 },
383 },
384 data: data.iter().copied(),
385 });
386 }
387 module.section(&datas);
388 }
389
390 module.finish()
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::test::test_n_times;
397
398 #[test]
399 fn smoke_test_memory_access() {
400 test_n_times(50, |input: MemoryAccesses, _u| {
401 check_memory_accesses(input);
402 Ok(())
403 })
404 }
405}