1 use super::skip_pooling_allocator_tests;
2 use anyhow::Result;
3 use wasmtime::*;
4 
5 #[test]
6 fn successful_instantiation() -> Result<()> {
7     let mut config = Config::new();
8     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
9         strategy: PoolingAllocationStrategy::NextAvailable,
10         instance_limits: InstanceLimits {
11             count: 1,
12             memory_pages: 1,
13             table_elements: 10,
14             ..Default::default()
15         },
16     });
17     config.dynamic_memory_guard_size(0);
18     config.static_memory_guard_size(0);
19     config.static_memory_maximum_size(65536);
20 
21     let engine = Engine::new(&config)?;
22     let module = Module::new(&engine, r#"(module (memory 1) (table 10 funcref))"#)?;
23 
24     // Module should instantiate
25     let mut store = Store::new(&engine, ());
26     Instance::new(&mut store, &module, &[])?;
27 
28     Ok(())
29 }
30 
31 #[test]
32 fn memory_limit() -> Result<()> {
33     let mut config = Config::new();
34     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
35         strategy: PoolingAllocationStrategy::NextAvailable,
36         instance_limits: InstanceLimits {
37             count: 1,
38             memory_pages: 3,
39             table_elements: 10,
40             ..Default::default()
41         },
42     });
43     config.dynamic_memory_guard_size(0);
44     config.static_memory_guard_size(65536);
45     config.static_memory_maximum_size(3 * 65536);
46     config.wasm_multi_memory(true);
47 
48     let engine = Engine::new(&config)?;
49 
50     // Module should fail to instantiate because it has too many memories
51     match Module::new(&engine, r#"(module (memory 1) (memory 1))"#) {
52         Ok(_) => panic!("module instantiation should fail"),
53         Err(e) => assert_eq!(
54             e.to_string(),
55             "defined memories count of 2 exceeds the limit of 1",
56         ),
57     }
58 
59     // Module should fail to instantiate because the minimum is greater than
60     // the configured limit
61     match Module::new(&engine, r#"(module (memory 4))"#) {
62         Ok(_) => panic!("module instantiation should fail"),
63         Err(e) => assert_eq!(
64             e.to_string(),
65             "memory index 0 has a minimum page size of 4 which exceeds the limit of 3",
66         ),
67     }
68 
69     let module = Module::new(
70         &engine,
71         r#"(module (memory (export "m") 0) (func (export "f") (result i32) (memory.grow (i32.const 1))))"#,
72     )?;
73 
74     // Instantiate the module and grow the memory via the `f` function
75     {
76         let mut store = Store::new(&engine, ());
77         let instance = Instance::new(&mut store, &module, &[])?;
78         let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?;
79 
80         assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 0);
81         assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 1);
82         assert_eq!(f.call(&mut store, ()).expect("function should not trap"), 2);
83         assert_eq!(
84             f.call(&mut store, ()).expect("function should not trap"),
85             -1
86         );
87         assert_eq!(
88             f.call(&mut store, ()).expect("function should not trap"),
89             -1
90         );
91     }
92 
93     // Instantiate the module and grow the memory via the Wasmtime API
94     let mut store = Store::new(&engine, ());
95     let instance = Instance::new(&mut store, &module, &[])?;
96 
97     let memory = instance.get_memory(&mut store, "m").unwrap();
98     assert_eq!(memory.size(&store), 0);
99     assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 0);
100     assert_eq!(memory.size(&store), 1);
101     assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 1);
102     assert_eq!(memory.size(&store), 2);
103     assert_eq!(memory.grow(&mut store, 1).expect("memory should grow"), 2);
104     assert_eq!(memory.size(&store), 3);
105     assert!(memory.grow(&mut store, 1).is_err());
106 
107     Ok(())
108 }
109 
110 #[test]
111 fn memory_init() -> Result<()> {
112     let mut config = Config::new();
113     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
114         strategy: PoolingAllocationStrategy::NextAvailable,
115         instance_limits: InstanceLimits {
116             count: 1,
117             memory_pages: 2,
118             table_elements: 0,
119             ..Default::default()
120         },
121     });
122 
123     let engine = Engine::new(&config)?;
124 
125     let module = Module::new(
126         &engine,
127         r#"(module (memory (export "m") 2) (data (i32.const 65530) "this data spans multiple pages") (data (i32.const 10) "hello world"))"#,
128     )?;
129 
130     let mut store = Store::new(&engine, ());
131     let instance = Instance::new(&mut store, &module, &[])?;
132     let memory = instance.get_memory(&mut store, "m").unwrap();
133 
134     assert_eq!(
135         &memory.data(&store)[65530..65560],
136         b"this data spans multiple pages"
137     );
138     assert_eq!(&memory.data(&store)[10..21], b"hello world");
139 
140     Ok(())
141 }
142 
143 #[test]
144 fn memory_guard_page_trap() -> Result<()> {
145     let mut config = Config::new();
146     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
147         strategy: PoolingAllocationStrategy::NextAvailable,
148         instance_limits: InstanceLimits {
149             count: 1,
150             memory_pages: 2,
151             table_elements: 0,
152             ..Default::default()
153         },
154     });
155 
156     let engine = Engine::new(&config)?;
157 
158     let module = Module::new(
159         &engine,
160         r#"(module (memory (export "m") 0) (func (export "f") (param i32) local.get 0 i32.load drop))"#,
161     )?;
162 
163     // Instantiate the module and check for out of bounds trap
164     for _ in 0..10 {
165         let mut store = Store::new(&engine, ());
166         let instance = Instance::new(&mut store, &module, &[])?;
167         let m = instance.get_memory(&mut store, "m").unwrap();
168         let f = instance.get_typed_func::<i32, (), _>(&mut store, "f")?;
169 
170         let trap = f.call(&mut store, 0).expect_err("function should trap");
171         assert!(trap.to_string().contains("out of bounds"));
172 
173         let trap = f.call(&mut store, 1).expect_err("function should trap");
174         assert!(trap.to_string().contains("out of bounds"));
175 
176         m.grow(&mut store, 1).expect("memory should grow");
177         f.call(&mut store, 0).expect("function should not trap");
178 
179         let trap = f.call(&mut store, 65536).expect_err("function should trap");
180         assert!(trap.to_string().contains("out of bounds"));
181 
182         let trap = f.call(&mut store, 65537).expect_err("function should trap");
183         assert!(trap.to_string().contains("out of bounds"));
184 
185         m.grow(&mut store, 1).expect("memory should grow");
186         f.call(&mut store, 65536).expect("function should not trap");
187 
188         m.grow(&mut store, 1)
189             .expect_err("memory should be at the limit");
190     }
191 
192     Ok(())
193 }
194 
195 #[test]
196 fn memory_zeroed() -> Result<()> {
197     if skip_pooling_allocator_tests() {
198         return Ok(());
199     }
200 
201     let mut config = Config::new();
202     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
203         strategy: PoolingAllocationStrategy::NextAvailable,
204         instance_limits: InstanceLimits {
205             count: 1,
206             memory_pages: 1,
207             table_elements: 0,
208             ..Default::default()
209         },
210     });
211     config.dynamic_memory_guard_size(0);
212     config.static_memory_guard_size(0);
213     config.static_memory_maximum_size(65536);
214 
215     let engine = Engine::new(&config)?;
216 
217     let module = Module::new(&engine, r#"(module (memory (export "m") 1))"#)?;
218 
219     // Instantiate the module repeatedly after writing data to the entire memory
220     for _ in 0..10 {
221         let mut store = Store::new(&engine, ());
222         let instance = Instance::new(&mut store, &module, &[])?;
223         let memory = instance.get_memory(&mut store, "m").unwrap();
224 
225         assert_eq!(memory.size(&store,), 1);
226         assert_eq!(memory.data_size(&store), 65536);
227 
228         let ptr = memory.data_mut(&mut store).as_mut_ptr();
229 
230         unsafe {
231             for i in 0..8192 {
232                 assert_eq!(*ptr.cast::<u64>().offset(i), 0);
233             }
234             std::ptr::write_bytes(ptr, 0xFE, memory.data_size(&store));
235         }
236     }
237 
238     Ok(())
239 }
240 
241 #[test]
242 fn table_limit() -> Result<()> {
243     const TABLE_ELEMENTS: u32 = 10;
244     let mut config = Config::new();
245     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
246         strategy: PoolingAllocationStrategy::NextAvailable,
247         instance_limits: InstanceLimits {
248             count: 1,
249             memory_pages: 1,
250             table_elements: TABLE_ELEMENTS,
251             ..Default::default()
252         },
253     });
254     config.dynamic_memory_guard_size(0);
255     config.static_memory_guard_size(0);
256     config.static_memory_maximum_size(65536);
257 
258     let engine = Engine::new(&config)?;
259 
260     // Module should fail to instantiate because it has too many tables
261     match Module::new(&engine, r#"(module (table 1 funcref) (table 1 funcref))"#) {
262         Ok(_) => panic!("module compilation should fail"),
263         Err(e) => assert_eq!(
264             e.to_string(),
265             "defined tables count of 2 exceeds the limit of 1",
266         ),
267     }
268 
269     // Module should fail to instantiate because the minimum is greater than
270     // the configured limit
271     match Module::new(&engine, r#"(module (table 31 funcref))"#) {
272         Ok(_) => panic!("module compilation should fail"),
273         Err(e) => assert_eq!(
274             e.to_string(),
275             "table index 0 has a minimum element size of 31 which exceeds the limit of 10",
276         ),
277     }
278 
279     let module = Module::new(
280         &engine,
281         r#"(module (table (export "t") 0 funcref) (func (export "f") (result i32) (table.grow (ref.null func) (i32.const 1))))"#,
282     )?;
283 
284     // Instantiate the module and grow the table via the `f` function
285     {
286         let mut store = Store::new(&engine, ());
287         let instance = Instance::new(&mut store, &module, &[])?;
288         let f = instance.get_typed_func::<(), i32, _>(&mut store, "f")?;
289 
290         for i in 0..TABLE_ELEMENTS {
291             assert_eq!(
292                 f.call(&mut store, ()).expect("function should not trap"),
293                 i as i32
294             );
295         }
296 
297         assert_eq!(
298             f.call(&mut store, ()).expect("function should not trap"),
299             -1
300         );
301         assert_eq!(
302             f.call(&mut store, ()).expect("function should not trap"),
303             -1
304         );
305     }
306 
307     // Instantiate the module and grow the table via the Wasmtime API
308     let mut store = Store::new(&engine, ());
309     let instance = Instance::new(&mut store, &module, &[])?;
310 
311     let table = instance.get_table(&mut store, "t").unwrap();
312 
313     for i in 0..TABLE_ELEMENTS {
314         assert_eq!(table.size(&store), i);
315         assert_eq!(
316             table
317                 .grow(&mut store, 1, Val::FuncRef(None))
318                 .expect("table should grow"),
319             i
320         );
321     }
322 
323     assert_eq!(table.size(&store), TABLE_ELEMENTS);
324     assert!(table.grow(&mut store, 1, Val::FuncRef(None)).is_err());
325 
326     Ok(())
327 }
328 
329 #[test]
330 fn table_init() -> Result<()> {
331     let mut config = Config::new();
332     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
333         strategy: PoolingAllocationStrategy::NextAvailable,
334         instance_limits: InstanceLimits {
335             count: 1,
336             memory_pages: 0,
337             table_elements: 6,
338             ..Default::default()
339         },
340     });
341 
342     let engine = Engine::new(&config)?;
343 
344     let module = Module::new(
345         &engine,
346         r#"(module (table (export "t") 6 funcref) (elem (i32.const 1) 1 2 3 4) (elem (i32.const 0) 0) (func) (func (param i32)) (func (param i32 i32)) (func (param i32 i32 i32)) (func (param i32 i32 i32 i32)))"#,
347     )?;
348 
349     let mut store = Store::new(&engine, ());
350     let instance = Instance::new(&mut store, &module, &[])?;
351     let table = instance.get_table(&mut store, "t").unwrap();
352 
353     for i in 0..5 {
354         let v = table.get(&mut store, i).expect("table should have entry");
355         let f = v
356             .funcref()
357             .expect("expected funcref")
358             .expect("expected non-null value");
359         assert_eq!(f.ty(&store).params().len(), i as usize);
360     }
361 
362     assert!(
363         table
364             .get(&mut store, 5)
365             .expect("table should have entry")
366             .funcref()
367             .expect("expected funcref")
368             .is_none(),
369         "funcref should be null"
370     );
371 
372     Ok(())
373 }
374 
375 #[test]
376 fn table_zeroed() -> Result<()> {
377     if skip_pooling_allocator_tests() {
378         return Ok(());
379     }
380 
381     let mut config = Config::new();
382     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
383         strategy: PoolingAllocationStrategy::NextAvailable,
384         instance_limits: InstanceLimits {
385             count: 1,
386             memory_pages: 1,
387             table_elements: 10,
388             ..Default::default()
389         },
390     });
391     config.dynamic_memory_guard_size(0);
392     config.static_memory_guard_size(0);
393     config.static_memory_maximum_size(65536);
394 
395     let engine = Engine::new(&config)?;
396 
397     let module = Module::new(&engine, r#"(module (table (export "t") 10 funcref))"#)?;
398 
399     // Instantiate the module repeatedly after filling table elements
400     for _ in 0..10 {
401         let mut store = Store::new(&engine, ());
402         let instance = Instance::new(&mut store, &module, &[])?;
403         let table = instance.get_table(&mut store, "t").unwrap();
404         let f = Func::wrap(&mut store, || {});
405 
406         assert_eq!(table.size(&store), 10);
407 
408         for i in 0..10 {
409             match table.get(&mut store, i).unwrap() {
410                 Val::FuncRef(r) => assert!(r.is_none()),
411                 _ => panic!("expected a funcref"),
412             }
413             table
414                 .set(&mut store, i, Val::FuncRef(Some(f.clone())))
415                 .unwrap();
416         }
417     }
418 
419     Ok(())
420 }
421 
422 #[test]
423 fn instantiation_limit() -> Result<()> {
424     const INSTANCE_LIMIT: u32 = 10;
425     let mut config = Config::new();
426     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
427         strategy: PoolingAllocationStrategy::NextAvailable,
428         instance_limits: InstanceLimits {
429             count: INSTANCE_LIMIT,
430             memory_pages: 1,
431             table_elements: 10,
432             ..Default::default()
433         },
434     });
435     config.dynamic_memory_guard_size(0);
436     config.static_memory_guard_size(0);
437     config.static_memory_maximum_size(65536);
438 
439     let engine = Engine::new(&config)?;
440     let module = Module::new(&engine, r#"(module)"#)?;
441 
442     // Instantiate to the limit
443     {
444         let mut store = Store::new(&engine, ());
445 
446         for _ in 0..INSTANCE_LIMIT {
447             Instance::new(&mut store, &module, &[])?;
448         }
449 
450         match Instance::new(&mut store, &module, &[]) {
451             Ok(_) => panic!("instantiation should fail"),
452             Err(e) => assert_eq!(
453                 e.to_string(),
454                 format!(
455                     "Limit of {} concurrent instances has been reached",
456                     INSTANCE_LIMIT
457                 )
458             ),
459         }
460     }
461 
462     // With the above store dropped, ensure instantiations can be made
463 
464     let mut store = Store::new(&engine, ());
465 
466     for _ in 0..INSTANCE_LIMIT {
467         Instance::new(&mut store, &module, &[])?;
468     }
469 
470     Ok(())
471 }
472 
473 #[test]
474 fn preserve_data_segments() -> Result<()> {
475     let mut config = Config::new();
476     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
477         strategy: PoolingAllocationStrategy::NextAvailable,
478         instance_limits: InstanceLimits {
479             count: 2,
480             memory_pages: 1,
481             table_elements: 10,
482             ..Default::default()
483         },
484     });
485     let engine = Engine::new(&config)?;
486     let m = Module::new(
487         &engine,
488         r#"
489             (module
490                 (memory (export "mem") 1 1)
491                 (data (i32.const 0) "foo"))
492         "#,
493     )?;
494     let mut store = Store::new(&engine, ());
495     let i = Instance::new(&mut store, &m, &[])?;
496 
497     // Drop the module. This should *not* drop the actual data referenced by the
498     // module, especially when uffd is enabled. If uffd is enabled we'll lazily
499     // fault in the memory of the module, which means it better still be alive
500     // after we drop this.
501     drop(m);
502 
503     // Spray some stuff on the heap. If wasm data lived on the heap this should
504     // paper over things and help us catch use-after-free here if it would
505     // otherwise happen.
506     let mut strings = Vec::new();
507     for _ in 0..1000 {
508         let mut string = String::new();
509         for _ in 0..1000 {
510             string.push('g');
511         }
512         strings.push(string);
513     }
514     drop(strings);
515 
516     let mem = i.get_memory(&mut store, "mem").unwrap();
517 
518     // This will segfault with uffd enabled, and then the uffd will lazily
519     // initialize the memory. Hopefully it's still `foo`!
520     assert!(mem.data(&store).starts_with(b"foo"));
521 
522     Ok(())
523 }
524 
525 #[test]
526 fn multi_memory_with_imported_memories() -> Result<()> {
527     // This test checks that the base address for the defined memory is correct for the instance
528     // despite the presence of an imported memory.
529 
530     let mut config = Config::new();
531     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
532         strategy: PoolingAllocationStrategy::NextAvailable,
533         instance_limits: InstanceLimits {
534             count: 1,
535             memories: 2,
536             memory_pages: 1,
537             ..Default::default()
538         },
539     });
540     config.wasm_multi_memory(true);
541 
542     let engine = Engine::new(&config)?;
543     let module = Module::new(
544         &engine,
545         r#"(module (import "" "m1" (memory 0)) (memory (export "m2") 1))"#,
546     )?;
547 
548     let mut store = Store::new(&engine, ());
549 
550     let m1 = Memory::new(&mut store, MemoryType::new(0, None))?;
551     let instance = Instance::new(&mut store, &module, &[m1.into()])?;
552 
553     let m2 = instance.get_memory(&mut store, "m2").unwrap();
554 
555     m2.data_mut(&mut store)[0] = 0x42;
556     assert_eq!(m2.data(&store)[0], 0x42);
557 
558     Ok(())
559 }
560 
561 #[test]
562 fn drop_externref_global_during_module_init() -> Result<()> {
563     struct Limiter;
564 
565     impl ResourceLimiter for Limiter {
566         fn memory_growing(&mut self, _: usize, _: usize, _: Option<usize>) -> bool {
567             false
568         }
569 
570         fn table_growing(&mut self, _: u32, _: u32, _: Option<u32>) -> bool {
571             false
572         }
573     }
574 
575     let mut config = Config::new();
576     config.wasm_reference_types(true);
577     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
578         strategy: PoolingAllocationStrategy::NextAvailable,
579         instance_limits: InstanceLimits {
580             count: 1,
581             ..Default::default()
582         },
583     });
584 
585     let engine = Engine::new(&config)?;
586 
587     let module = Module::new(
588         &engine,
589         r#"
590             (module
591                 (global i32 (i32.const 1))
592                 (global i32 (i32.const 2))
593                 (global i32 (i32.const 3))
594                 (global i32 (i32.const 4))
595                 (global i32 (i32.const 5))
596             )
597         "#,
598     )?;
599 
600     let mut store = Store::new(&engine, Limiter);
601     drop(Instance::new(&mut store, &module, &[])?);
602     drop(store);
603 
604     let module = Module::new(
605         &engine,
606         r#"
607             (module
608                 (memory 1)
609                 (global (mut externref) (ref.null extern))
610             )
611         "#,
612     )?;
613 
614     let mut store = Store::new(&engine, Limiter);
615     store.limiter(|s| s);
616     assert!(Instance::new(&mut store, &module, &[]).is_err());
617 
618     Ok(())
619 }
620 
621 #[test]
622 #[cfg(target_pointer_width = "64")]
623 fn instance_too_large() -> Result<()> {
624     let mut config = Config::new();
625     config.allocation_strategy(InstanceAllocationStrategy::Pooling {
626         strategy: PoolingAllocationStrategy::NextAvailable,
627         instance_limits: InstanceLimits {
628             size: 16,
629             count: 1,
630             ..Default::default()
631         },
632     });
633 
634     let engine = Engine::new(&config)?;
635     let expected = "\
636 instance allocation for this module requires 304 bytes which exceeds the \
637 configured maximum of 16 bytes; breakdown of allocation requirement:
638 
639  * 78.95% - 240 bytes - instance state management
640  * 5.26% - 16 bytes - jit store state
641 ";
642     match Module::new(&engine, "(module)") {
643         Ok(_) => panic!("should have failed to compile"),
644         Err(e) => assert_eq!(e.to_string(), expected),
645     }
646 
647     let mut lots_of_globals = format!("(module");
648     for _ in 0..100 {
649         lots_of_globals.push_str("(global i32 i32.const 0)\n");
650     }
651     lots_of_globals.push_str(")");
652 
653     let expected = "\
654 instance allocation for this module requires 1904 bytes which exceeds the \
655 configured maximum of 16 bytes; breakdown of allocation requirement:
656 
657  * 12.61% - 240 bytes - instance state management
658  * 84.03% - 1600 bytes - defined globals
659 ";
660     match Module::new(&engine, &lots_of_globals) {
661         Ok(_) => panic!("should have failed to compile"),
662         Err(e) => assert_eq!(e.to_string(), expected),
663     }
664 
665     Ok(())
666 }
667