xref: /wasmtime-44.0.1/tests/all/limits.rs (revision 6f6a514b)
1 use wasmtime::*;
2 
3 const WASM_PAGE_SIZE: usize = wasmtime_environ::Memory::DEFAULT_PAGE_SIZE as usize;
4 
5 #[test]
6 #[cfg_attr(miri, ignore)]
7 fn test_limits() -> Result<()> {
8     let engine = Engine::default();
9     let module = Module::new(
10         &engine,
11         r#"(module
12             (memory $m (export "m") 0)
13             (table (export "t") 0 funcref)
14             (func (export "grow") (param i32) (result i32)
15               (memory.grow $m (local.get 0)))
16            )"#,
17     )?;
18 
19     let mut store = Store::new(
20         &engine,
21         StoreLimitsBuilder::new()
22             .memory_size(10 * WASM_PAGE_SIZE)
23             .table_elements(5)
24             .build(),
25     );
26     store.limiter(|s| s as &mut dyn ResourceLimiter);
27 
28     let instance = Instance::new(&mut store, &module, &[])?;
29 
30     // Test instance exports and host objects hitting the limit
31     for memory in IntoIterator::into_iter([
32         instance.get_memory(&mut store, "m").unwrap(),
33         Memory::new(&mut store, MemoryType::new(0, None))?,
34     ]) {
35         memory.grow(&mut store, 3)?;
36         memory.grow(&mut store, 5)?;
37         memory.grow(&mut store, 2)?;
38 
39         assert_eq!(
40             memory
41                 .grow(&mut store, 1)
42                 .map_err(|e| e.to_string())
43                 .unwrap_err(),
44             "failed to grow memory by `1`"
45         );
46     }
47 
48     // Test instance exports and host objects hitting the limit
49     for table in IntoIterator::into_iter([
50         instance.get_table(&mut store, "t").unwrap(),
51         Table::new(
52             &mut store,
53             TableType::new(RefType::FUNCREF, 0, None),
54             Ref::Func(None),
55         )?,
56     ]) {
57         table.grow(&mut store, 2, Ref::Func(None))?;
58         table.grow(&mut store, 1, Ref::Func(None))?;
59         table.grow(&mut store, 2, Ref::Func(None))?;
60 
61         assert_eq!(
62             table
63                 .grow(&mut store, 1, Ref::Func(None))
64                 .map_err(|e| e.to_string())
65                 .unwrap_err(),
66             "failed to grow table by `1`"
67         );
68     }
69 
70     // Make a new store and instance to test memory grow through wasm
71     let mut store = Store::new(
72         &engine,
73         StoreLimitsBuilder::new()
74             .memory_size(10 * WASM_PAGE_SIZE)
75             .table_elements(5)
76             .build(),
77     );
78     store.limiter(|s| s as &mut dyn ResourceLimiter);
79     let instance = Instance::new(&mut store, &module, &[])?;
80     let grow = instance.get_func(&mut store, "grow").unwrap();
81     let grow = grow.typed::<i32, i32>(&store).unwrap();
82 
83     grow.call(&mut store, 3).unwrap();
84     grow.call(&mut store, 5).unwrap();
85     grow.call(&mut store, 2).unwrap();
86 
87     // Wasm grow failure returns -1.
88     assert_eq!(grow.call(&mut store, 1).unwrap(), -1);
89 
90     Ok(())
91 }
92 
93 #[tokio::test]
94 #[cfg_attr(miri, ignore)]
95 async fn test_limits_async() -> Result<()> {
96     let mut config = Config::new();
97     config.async_support(true);
98     let engine = Engine::new(&config).unwrap();
99     let module = Module::new(
100         &engine,
101         r#"(module (memory (export "m") 0) (table (export "t") 0 funcref))"#,
102     )?;
103 
104     struct LimitsAsync {
105         memory_size: usize,
106         table_elements: usize,
107     }
108     #[async_trait::async_trait]
109     impl ResourceLimiterAsync for LimitsAsync {
110         async fn memory_growing(
111             &mut self,
112             _current: usize,
113             desired: usize,
114             _maximum: Option<usize>,
115         ) -> Result<bool> {
116             Ok(desired <= self.memory_size)
117         }
118         async fn table_growing(
119             &mut self,
120             _current: usize,
121             desired: usize,
122             _maximum: Option<usize>,
123         ) -> Result<bool> {
124             Ok(desired <= self.table_elements)
125         }
126     }
127 
128     let mut store = Store::new(
129         &engine,
130         LimitsAsync {
131             memory_size: 10 * WASM_PAGE_SIZE,
132             table_elements: 5,
133         },
134     );
135 
136     store.limiter_async(|s| s as &mut dyn ResourceLimiterAsync);
137 
138     let instance = Instance::new_async(&mut store, &module, &[]).await?;
139 
140     // Test instance exports and host objects hitting the limit
141     for memory in IntoIterator::into_iter([
142         instance.get_memory(&mut store, "m").unwrap(),
143         Memory::new_async(&mut store, MemoryType::new(0, None)).await?,
144     ]) {
145         memory.grow_async(&mut store, 3).await?;
146         memory.grow_async(&mut store, 5).await?;
147         memory.grow_async(&mut store, 2).await?;
148 
149         assert_eq!(
150             memory
151                 .grow_async(&mut store, 1)
152                 .await
153                 .map_err(|e| e.to_string())
154                 .unwrap_err(),
155             "failed to grow memory by `1`"
156         );
157     }
158 
159     // Test instance exports and host objects hitting the limit
160     for table in IntoIterator::into_iter([
161         instance.get_table(&mut store, "t").unwrap(),
162         Table::new_async(
163             &mut store,
164             TableType::new(RefType::FUNCREF, 0, None),
165             Ref::Func(None),
166         )
167         .await?,
168     ]) {
169         table.grow_async(&mut store, 2, Ref::Func(None)).await?;
170         table.grow_async(&mut store, 1, Ref::Func(None)).await?;
171         table.grow_async(&mut store, 2, Ref::Func(None)).await?;
172 
173         assert_eq!(
174             table
175                 .grow_async(&mut store, 1, Ref::Func(None))
176                 .await
177                 .map_err(|e| e.to_string())
178                 .unwrap_err(),
179             "failed to grow table by `1`"
180         );
181     }
182 
183     Ok(())
184 }
185 
186 #[test]
187 fn test_limits_memory_only() -> Result<()> {
188     let engine = Engine::default();
189     let module = Module::new(
190         &engine,
191         r#"(module (memory (export "m") 0) (table (export "t") 0 funcref))"#,
192     )?;
193 
194     let mut store = Store::new(
195         &engine,
196         StoreLimitsBuilder::new()
197             .memory_size(10 * WASM_PAGE_SIZE)
198             .build(),
199     );
200     store.limiter(|s| s as &mut dyn ResourceLimiter);
201 
202     let instance = Instance::new(&mut store, &module, &[])?;
203 
204     // Test instance exports and host objects hitting the limit
205     for memory in IntoIterator::into_iter([
206         instance.get_memory(&mut store, "m").unwrap(),
207         Memory::new(&mut store, MemoryType::new(0, None))?,
208     ]) {
209         memory.grow(&mut store, 3)?;
210         memory.grow(&mut store, 5)?;
211         memory.grow(&mut store, 2)?;
212 
213         assert_eq!(
214             memory
215                 .grow(&mut store, 1)
216                 .map_err(|e| e.to_string())
217                 .unwrap_err(),
218             "failed to grow memory by `1`"
219         );
220     }
221 
222     // Test instance exports and host objects *not* hitting the limit
223     for table in IntoIterator::into_iter([
224         instance.get_table(&mut store, "t").unwrap(),
225         Table::new(
226             &mut store,
227             TableType::new(RefType::FUNCREF, 0, None),
228             Ref::Func(None),
229         )?,
230     ]) {
231         table.grow(&mut store, 2, Ref::Func(None))?;
232         table.grow(&mut store, 1, Ref::Func(None))?;
233         table.grow(&mut store, 2, Ref::Func(None))?;
234         table.grow(&mut store, 1, Ref::Func(None))?;
235     }
236 
237     Ok(())
238 }
239 
240 #[test]
241 fn test_initial_memory_limits_exceeded() -> Result<()> {
242     let engine = Engine::default();
243     let module = Module::new(&engine, r#"(module (memory (export "m") 11))"#)?;
244 
245     let mut store = Store::new(
246         &engine,
247         StoreLimitsBuilder::new()
248             .memory_size(10 * WASM_PAGE_SIZE)
249             .build(),
250     );
251     store.limiter(|s| s as &mut dyn ResourceLimiter);
252 
253     match Instance::new(&mut store, &module, &[]) {
254         Ok(_) => unreachable!(),
255         Err(e) => assert_eq!(
256             e.to_string(),
257             "memory minimum size of 11 pages exceeds memory limits"
258         ),
259     }
260 
261     match Memory::new(&mut store, MemoryType::new(25, None)) {
262         Ok(_) => unreachable!(),
263         Err(e) => assert_eq!(
264             e.to_string(),
265             "memory minimum size of 25 pages exceeds memory limits"
266         ),
267     }
268 
269     Ok(())
270 }
271 
272 #[test]
273 fn test_limits_table_only() -> Result<()> {
274     let engine = Engine::default();
275     let module = Module::new(
276         &engine,
277         r#"(module (memory (export "m") 0) (table (export "t") 0 funcref))"#,
278     )?;
279 
280     let mut store = Store::new(&engine, StoreLimitsBuilder::new().table_elements(5).build());
281     store.limiter(|s| s as &mut dyn ResourceLimiter);
282 
283     let instance = Instance::new(&mut store, &module, &[])?;
284 
285     // Test instance exports and host objects *not* hitting the limit
286     for memory in IntoIterator::into_iter([
287         instance.get_memory(&mut store, "m").unwrap(),
288         Memory::new(&mut store, MemoryType::new(0, None))?,
289     ]) {
290         memory.grow(&mut store, 3)?;
291         memory.grow(&mut store, 5)?;
292         memory.grow(&mut store, 2)?;
293         memory.grow(&mut store, 1)?;
294     }
295 
296     // Test instance exports and host objects hitting the limit
297     for table in IntoIterator::into_iter([
298         instance.get_table(&mut store, "t").unwrap(),
299         Table::new(
300             &mut store,
301             TableType::new(RefType::FUNCREF, 0, None),
302             Ref::Func(None),
303         )?,
304     ]) {
305         table.grow(&mut store, 2, Ref::Func(None))?;
306         table.grow(&mut store, 1, Ref::Func(None))?;
307         table.grow(&mut store, 2, Ref::Func(None))?;
308 
309         assert_eq!(
310             table
311                 .grow(&mut store, 1, Ref::Func(None))
312                 .map_err(|e| e.to_string())
313                 .unwrap_err(),
314             "failed to grow table by `1`"
315         );
316     }
317 
318     Ok(())
319 }
320 
321 #[test]
322 fn test_initial_table_limits_exceeded() -> Result<()> {
323     let engine = Engine::default();
324     let module = Module::new(&engine, r#"(module (table (export "t") 23 funcref))"#)?;
325 
326     let mut store = Store::new(&engine, StoreLimitsBuilder::new().table_elements(4).build());
327     store.limiter(|s| s as &mut dyn ResourceLimiter);
328 
329     match Instance::new(&mut store, &module, &[]) {
330         Ok(_) => unreachable!(),
331         Err(e) => assert_eq!(
332             e.to_string(),
333             "table minimum size of 23 elements exceeds table limits"
334         ),
335     }
336 
337     match Table::new(
338         &mut store,
339         TableType::new(RefType::FUNCREF, 99, None),
340         Ref::Func(None),
341     ) {
342         Ok(_) => unreachable!(),
343         Err(e) => assert_eq!(
344             e.to_string(),
345             "table minimum size of 99 elements exceeds table limits"
346         ),
347     }
348 
349     Ok(())
350 }
351 
352 #[test]
353 fn test_pooling_allocator_initial_limits_exceeded() -> Result<()> {
354     let mut pool = crate::small_pool_config();
355     pool.total_memories(2)
356         .max_memories_per_module(2)
357         .max_memory_size(5 << 16)
358         .memory_protection_keys(Enabled::No);
359     let mut config = Config::new();
360     config.wasm_multi_memory(true);
361     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
362 
363     let engine = Engine::new(&config)?;
364     let module = Module::new(
365         &engine,
366         r#"(module (memory (export "m1") 2) (memory (export "m2") 5))"#,
367     )?;
368 
369     let mut store = Store::new(
370         &engine,
371         StoreLimitsBuilder::new()
372             .memory_size(3 * WASM_PAGE_SIZE)
373             .build(),
374     );
375     store.limiter(|s| s as &mut dyn ResourceLimiter);
376 
377     match Instance::new(&mut store, &module, &[]) {
378         Ok(_) => unreachable!(),
379         Err(e) => assert_eq!(
380             e.to_string(),
381             "memory minimum size of 5 pages exceeds memory limits"
382         ),
383     }
384 
385     // An instance should still be able to be created after the failure above
386     let module = Module::new(&engine, r#"(module (memory (export "m") 2))"#)?;
387 
388     Instance::new(&mut store, &module, &[])?;
389 
390     Ok(())
391 }
392 
393 struct MemoryContext {
394     host_memory_used: usize,
395     wasm_memory_used: usize,
396     memory_limit: usize,
397     limit_exceeded: bool,
398 }
399 
400 impl ResourceLimiter for MemoryContext {
401     fn memory_growing(
402         &mut self,
403         current: usize,
404         desired: usize,
405         maximum: Option<usize>,
406     ) -> Result<bool> {
407         // Check if the desired exceeds a maximum (either from Wasm or from the host)
408         assert!(desired < maximum.unwrap_or(usize::MAX));
409 
410         assert_eq!(current, self.wasm_memory_used);
411 
412         if desired + self.host_memory_used > self.memory_limit {
413             self.limit_exceeded = true;
414             return Ok(false);
415         }
416 
417         self.wasm_memory_used = desired;
418         Ok(true)
419     }
420     fn table_growing(
421         &mut self,
422         _current: usize,
423         _desired: usize,
424         _maximum: Option<usize>,
425     ) -> Result<bool> {
426         Ok(true)
427     }
428 }
429 
430 #[test]
431 #[cfg_attr(miri, ignore)]
432 fn test_custom_memory_limiter() -> Result<()> {
433     let engine = Engine::default();
434     let mut linker = Linker::new(&engine);
435 
436     // This approximates a function that would "allocate" resources that the host tracks.
437     // Here this is a simple function that increments the current host memory "used".
438     linker.func_wrap(
439         "",
440         "alloc",
441         |mut caller: Caller<'_, MemoryContext>, size: u32| -> u32 {
442             let ctx = caller.data_mut();
443             let size = size as usize;
444 
445             if size + ctx.host_memory_used + ctx.wasm_memory_used <= ctx.memory_limit {
446                 ctx.host_memory_used += size;
447                 return 1;
448             }
449 
450             ctx.limit_exceeded = true;
451 
452             0
453         },
454     )?;
455 
456     let module = Module::new(
457         &engine,
458         r#"(module (import "" "alloc" (func $alloc (param i32) (result i32))) (memory (export "m") 0) (func (export "f") (param i32) (result i32) local.get 0 call $alloc))"#,
459     )?;
460 
461     let context = MemoryContext {
462         host_memory_used: 0,
463         wasm_memory_used: 0,
464         memory_limit: 1 << 20, // 16 wasm pages is the limit for both wasm + host memory
465         limit_exceeded: false,
466     };
467 
468     let mut store = Store::new(&engine, context);
469     store.limiter(|s| s as &mut dyn ResourceLimiter);
470     let instance = linker.instantiate(&mut store, &module)?;
471     let memory = instance.get_memory(&mut store, "m").unwrap();
472 
473     // Grow the memory by 640 KiB
474     memory.grow(&mut store, 3)?;
475     memory.grow(&mut store, 5)?;
476     memory.grow(&mut store, 2)?;
477 
478     assert!(!store.data().limit_exceeded);
479 
480     // Grow the host "memory" by 384 KiB
481     let f = instance.get_typed_func::<u32, u32>(&mut store, "f")?;
482 
483     assert_eq!(f.call(&mut store, 1 * 0x10000)?, 1);
484     assert_eq!(f.call(&mut store, 3 * 0x10000)?, 1);
485     assert_eq!(f.call(&mut store, 2 * 0x10000)?, 1);
486 
487     // Memory is at the maximum, but the limit hasn't been exceeded
488     assert!(!store.data().limit_exceeded);
489 
490     // Try to grow the memory again
491     assert_eq!(
492         memory
493             .grow(&mut store, 1)
494             .map_err(|e| e.to_string())
495             .unwrap_err(),
496         "failed to grow memory by `1`"
497     );
498 
499     assert!(store.data().limit_exceeded);
500 
501     // Try to grow the host "memory" again
502     assert_eq!(f.call(&mut store, 1)?, 0);
503 
504     assert!(store.data().limit_exceeded);
505 
506     drop(store);
507 
508     Ok(())
509 }
510 
511 #[async_trait::async_trait]
512 impl ResourceLimiterAsync for MemoryContext {
513     async fn memory_growing(
514         &mut self,
515         current: usize,
516         desired: usize,
517         maximum: Option<usize>,
518     ) -> Result<bool> {
519         // Show we can await in this async context:
520         tokio::time::sleep(std::time::Duration::from_millis(1)).await;
521         // Check if the desired exceeds a maximum (either from Wasm or from the host)
522         assert!(desired < maximum.unwrap_or(usize::MAX));
523 
524         assert_eq!(current, self.wasm_memory_used);
525 
526         if desired + self.host_memory_used > self.memory_limit {
527             self.limit_exceeded = true;
528             return Ok(false);
529         }
530 
531         self.wasm_memory_used = desired;
532         Ok(true)
533     }
534     async fn table_growing(
535         &mut self,
536         _current: usize,
537         _desired: usize,
538         _maximum: Option<usize>,
539     ) -> Result<bool> {
540         Ok(true)
541     }
542 }
543 
544 #[tokio::test]
545 #[cfg_attr(miri, ignore)]
546 async fn test_custom_memory_limiter_async() -> Result<()> {
547     let mut config = Config::new();
548     config.async_support(true);
549     let engine = Engine::new(&config).unwrap();
550     let mut linker = Linker::new(&engine);
551 
552     // This approximates a function that would "allocate" resources that the host tracks.
553     // Here this is a simple function that increments the current host memory "used".
554     linker.func_wrap(
555         "",
556         "alloc",
557         |mut caller: Caller<'_, MemoryContext>, size: u32| -> u32 {
558             let ctx = caller.data_mut();
559             let size = size as usize;
560 
561             if size + ctx.host_memory_used + ctx.wasm_memory_used <= ctx.memory_limit {
562                 ctx.host_memory_used += size;
563                 return 1;
564             }
565 
566             ctx.limit_exceeded = true;
567 
568             0
569         },
570     )?;
571 
572     let module = Module::new(
573         &engine,
574         r#"(module (import "" "alloc" (func $alloc (param i32) (result i32))) (memory (export "m") 0) (func (export "f") (param i32) (result i32) local.get 0 call $alloc))"#,
575     )?;
576 
577     let context = MemoryContext {
578         host_memory_used: 0,
579         wasm_memory_used: 0,
580         memory_limit: 1 << 20, // 16 wasm pages is the limit for both wasm + host memory
581         limit_exceeded: false,
582     };
583 
584     let mut store = Store::new(&engine, context);
585     store.limiter_async(|s| s as &mut dyn ResourceLimiterAsync);
586     let instance = linker.instantiate_async(&mut store, &module).await?;
587     let memory = instance.get_memory(&mut store, "m").unwrap();
588 
589     // Grow the memory by 640 KiB
590     memory.grow_async(&mut store, 3).await?;
591     memory.grow_async(&mut store, 5).await?;
592     memory.grow_async(&mut store, 2).await?;
593 
594     assert!(!store.data().limit_exceeded);
595 
596     // Grow the host "memory" by 384 KiB
597     let f = instance.get_typed_func::<u32, u32>(&mut store, "f")?;
598 
599     assert_eq!(f.call_async(&mut store, 1 * 0x10000).await?, 1);
600     assert_eq!(f.call_async(&mut store, 3 * 0x10000).await?, 1);
601     assert_eq!(f.call_async(&mut store, 2 * 0x10000).await?, 1);
602 
603     // Memory is at the maximum, but the limit hasn't been exceeded
604     assert!(!store.data().limit_exceeded);
605 
606     // Try to grow the memory again
607     assert_eq!(
608         memory
609             .grow_async(&mut store, 1)
610             .await
611             .map_err(|e| e.to_string())
612             .unwrap_err(),
613         "failed to grow memory by `1`"
614     );
615 
616     assert!(store.data().limit_exceeded);
617 
618     // Try to grow the host "memory" again
619     assert_eq!(f.call_async(&mut store, 1).await?, 0);
620 
621     assert!(store.data().limit_exceeded);
622 
623     drop(store);
624 
625     Ok(())
626 }
627 
628 struct TableContext {
629     elements_used: usize,
630     element_limit: usize,
631     limit_exceeded: bool,
632 }
633 
634 impl ResourceLimiter for TableContext {
635     fn memory_growing(
636         &mut self,
637         _current: usize,
638         _desired: usize,
639         _maximum: Option<usize>,
640     ) -> Result<bool> {
641         Ok(true)
642     }
643     fn table_growing(
644         &mut self,
645         current: usize,
646         desired: usize,
647         maximum: Option<usize>,
648     ) -> Result<bool> {
649         // Check if the desired exceeds a maximum (either from Wasm or from the host)
650         assert!(desired < maximum.unwrap_or(usize::MAX));
651         assert_eq!(current, self.elements_used);
652         Ok(if desired > self.element_limit {
653             self.limit_exceeded = true;
654             false
655         } else {
656             self.elements_used = desired;
657             true
658         })
659     }
660 }
661 
662 #[test]
663 fn test_custom_table_limiter() -> Result<()> {
664     let engine = Engine::default();
665     let linker = Linker::new(&engine);
666 
667     let module = Module::new(&engine, r#"(module (table (export "t") 0 funcref))"#)?;
668 
669     let context = TableContext {
670         elements_used: 0,
671         element_limit: 10,
672         limit_exceeded: false,
673     };
674 
675     let mut store = Store::new(&engine, context);
676     store.limiter(|s| s as &mut dyn ResourceLimiter);
677     let instance = linker.instantiate(&mut store, &module)?;
678     let table = instance.get_table(&mut store, "t").unwrap();
679 
680     // Grow the table by 10 elements
681     table.grow(&mut store, 3, Ref::Func(None))?;
682     table.grow(&mut store, 5, Ref::Func(None))?;
683     table.grow(&mut store, 2, Ref::Func(None))?;
684 
685     assert!(!store.data().limit_exceeded);
686 
687     // Table is at the maximum, but the limit hasn't been exceeded
688     assert!(!store.data().limit_exceeded);
689 
690     // Try to grow the memory again
691     assert_eq!(
692         table
693             .grow(&mut store, 1, Ref::Func(None))
694             .map_err(|e| e.to_string())
695             .unwrap_err(),
696         "failed to grow table by `1`"
697     );
698 
699     assert!(store.data().limit_exceeded);
700 
701     Ok(())
702 }
703 
704 #[derive(Default)]
705 struct FailureDetector {
706     /// Arguments of most recent call to memory_growing
707     memory_current: usize,
708     memory_desired: usize,
709     /// Display impl of most recent call to memory_grow_failed
710     memory_error: Option<String>,
711     /// Arguments of most recent call to table_growing
712     table_current: usize,
713     table_desired: usize,
714     /// Display impl of most recent call to table_grow_failed
715     table_error: Option<String>,
716 }
717 
718 impl ResourceLimiter for FailureDetector {
719     fn memory_growing(
720         &mut self,
721         current: usize,
722         desired: usize,
723         _maximum: Option<usize>,
724     ) -> Result<bool> {
725         self.memory_current = current;
726         self.memory_desired = desired;
727         Ok(true)
728     }
729     fn memory_grow_failed(&mut self, err: anyhow::Error) -> Result<()> {
730         self.memory_error = Some(err.to_string());
731         Ok(())
732     }
733     fn table_growing(
734         &mut self,
735         current: usize,
736         desired: usize,
737         _maximum: Option<usize>,
738     ) -> Result<bool> {
739         self.table_current = current;
740         self.table_desired = desired;
741         Ok(true)
742     }
743     fn table_grow_failed(&mut self, err: anyhow::Error) -> Result<()> {
744         self.table_error = Some(err.to_string());
745         Ok(())
746     }
747 }
748 
749 #[test]
750 fn custom_limiter_detect_grow_failure() -> Result<()> {
751     if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
752         return Ok(());
753     }
754     let mut pool = crate::small_pool_config();
755     pool.max_memory_size(10 << 16).table_elements(10);
756     let mut config = Config::new();
757     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
758     let engine = Engine::new(&config).unwrap();
759     let linker = Linker::new(&engine);
760 
761     let module = Module::new(
762         &engine,
763         r#"(module (memory (export "m") 0) (table (export "t") 0 funcref))"#,
764     )?;
765 
766     let context = FailureDetector::default();
767 
768     let mut store = Store::new(&engine, context);
769     store.limiter(|s| s as &mut dyn ResourceLimiter);
770     let instance = linker.instantiate(&mut store, &module)?;
771     let memory = instance.get_memory(&mut store, "m").unwrap();
772 
773     // Grow the memory by 640 KiB (10 pages)
774     memory.grow(&mut store, 10)?;
775 
776     assert!(store.data().memory_error.is_none());
777     assert_eq!(store.data().memory_current, 0);
778     assert_eq!(store.data().memory_desired, 10 * 64 * 1024);
779 
780     // Grow past the static limit set by ModuleLimits.
781     // The ResourceLimiter will permit this, but the grow will fail.
782     assert_eq!(
783         memory.grow(&mut store, 1).unwrap_err().to_string(),
784         "failed to grow memory by `1`"
785     );
786 
787     assert_eq!(store.data().memory_current, 10 * 64 * 1024);
788     assert_eq!(store.data().memory_desired, 11 * 64 * 1024);
789     assert_eq!(
790         store.data().memory_error.as_ref().unwrap(),
791         "Memory maximum size exceeded"
792     );
793 
794     let table = instance.get_table(&mut store, "t").unwrap();
795     // Grow the table 10 elements
796     table.grow(&mut store, 10, Ref::Func(None))?;
797 
798     assert!(store.data().table_error.is_none());
799     assert_eq!(store.data().table_current, 0);
800     assert_eq!(store.data().table_desired, 10);
801 
802     // Grow past the static limit set by ModuleLimits.
803     // The ResourceLimiter will permit this, but the grow will fail.
804     assert_eq!(
805         table
806             .grow(&mut store, 1, Ref::Func(None))
807             .unwrap_err()
808             .to_string(),
809         "failed to grow table by `1`"
810     );
811 
812     assert_eq!(store.data().table_current, 10);
813     assert_eq!(store.data().table_desired, 11);
814     assert_eq!(
815         store.data().table_error.as_ref().unwrap(),
816         "Table maximum size exceeded"
817     );
818 
819     drop(store);
820 
821     Ok(())
822 }
823 
824 #[async_trait::async_trait]
825 impl ResourceLimiterAsync for FailureDetector {
826     async fn memory_growing(
827         &mut self,
828         current: usize,
829         desired: usize,
830         _maximum: Option<usize>,
831     ) -> Result<bool> {
832         // Show we can await in this async context:
833         tokio::time::sleep(std::time::Duration::from_millis(1)).await;
834         self.memory_current = current;
835         self.memory_desired = desired;
836         Ok(true)
837     }
838     fn memory_grow_failed(&mut self, err: anyhow::Error) -> Result<()> {
839         self.memory_error = Some(err.to_string());
840         Ok(())
841     }
842 
843     async fn table_growing(
844         &mut self,
845         current: usize,
846         desired: usize,
847         _maximum: Option<usize>,
848     ) -> Result<bool> {
849         self.table_current = current;
850         self.table_desired = desired;
851         Ok(true)
852     }
853     fn table_grow_failed(&mut self, err: anyhow::Error) -> Result<()> {
854         self.table_error = Some(err.to_string());
855         Ok(())
856     }
857 }
858 
859 #[tokio::test]
860 #[cfg_attr(miri, ignore)]
861 async fn custom_limiter_async_detect_grow_failure() -> Result<()> {
862     if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
863         return Ok(());
864     }
865     let mut pool = crate::small_pool_config();
866     pool.max_memory_size(10 << 16).table_elements(10);
867     let mut config = Config::new();
868     config.async_support(true);
869     config.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
870     let engine = Engine::new(&config).unwrap();
871     let linker = Linker::<FailureDetector>::new(&engine);
872 
873     let module = Module::new(
874         &engine,
875         r#"(module (memory (export "m") 0) (table (export "t") 0 funcref))"#,
876     )?;
877 
878     let context = FailureDetector::default();
879 
880     let mut store = Store::new(&engine, context);
881     store.limiter_async(|s| s as &mut dyn ResourceLimiterAsync);
882     let instance = linker.instantiate_async(&mut store, &module).await?;
883     let memory = instance.get_memory(&mut store, "m").unwrap();
884 
885     // Grow the memory by 640 KiB (10 pages)
886     memory.grow_async(&mut store, 10).await?;
887 
888     assert!(store.data().memory_error.is_none());
889     assert_eq!(store.data().memory_current, 0);
890     assert_eq!(store.data().memory_desired, 10 * 64 * 1024);
891 
892     // Grow past the static limit set by ModuleLimits.
893     // The ResourcLimiterAsync will permit this, but the grow will fail.
894     assert_eq!(
895         memory
896             .grow_async(&mut store, 1)
897             .await
898             .unwrap_err()
899             .to_string(),
900         "failed to grow memory by `1`"
901     );
902 
903     assert_eq!(store.data().memory_current, 10 * 64 * 1024);
904     assert_eq!(store.data().memory_desired, 11 * 64 * 1024);
905     assert_eq!(
906         store.data().memory_error.as_ref().unwrap(),
907         "Memory maximum size exceeded"
908     );
909 
910     let table = instance.get_table(&mut store, "t").unwrap();
911     // Grow the table 10 elements
912     table.grow_async(&mut store, 10, Ref::Func(None)).await?;
913 
914     assert!(store.data().table_error.is_none());
915     assert_eq!(store.data().table_current, 0);
916     assert_eq!(store.data().table_desired, 10);
917 
918     // Grow past the static limit set by ModuleLimits.
919     // The ResourceLimiter will permit this, but the grow will fail.
920     assert_eq!(
921         table
922             .grow_async(&mut store, 1, Ref::Func(None))
923             .await
924             .unwrap_err()
925             .to_string(),
926         "failed to grow table by `1`"
927     );
928 
929     assert_eq!(store.data().table_current, 10);
930     assert_eq!(store.data().table_desired, 11);
931     assert_eq!(
932         store.data().table_error.as_ref().unwrap(),
933         "Table maximum size exceeded"
934     );
935 
936     drop(store);
937 
938     Ok(())
939 }
940 
941 struct Panic;
942 
943 impl ResourceLimiter for Panic {
944     fn memory_growing(
945         &mut self,
946         _current: usize,
947         _desired: usize,
948         _maximum: Option<usize>,
949     ) -> Result<bool> {
950         panic!("resource limiter memory growing");
951     }
952     fn table_growing(
953         &mut self,
954         _current: usize,
955         _desired: usize,
956         _maximum: Option<usize>,
957     ) -> Result<bool> {
958         panic!("resource limiter table growing");
959     }
960 }
961 #[async_trait::async_trait]
962 impl ResourceLimiterAsync for Panic {
963     async fn memory_growing(
964         &mut self,
965         _current: usize,
966         _desired: usize,
967         _maximum: Option<usize>,
968     ) -> Result<bool> {
969         panic!("async resource limiter memory growing");
970     }
971     async fn table_growing(
972         &mut self,
973         _current: usize,
974         _desired: usize,
975         _maximum: Option<usize>,
976     ) -> Result<bool> {
977         panic!("async resource limiter table growing");
978     }
979 }
980 
981 #[test]
982 #[should_panic(expected = "resource limiter memory growing")]
983 fn panic_in_memory_limiter() {
984     let engine = Engine::default();
985     let linker = Linker::new(&engine);
986 
987     let module = Module::new(&engine, r#"(module (memory (export "m") 0))"#).unwrap();
988 
989     let mut store = Store::new(&engine, Panic);
990     store.limiter(|s| s as &mut dyn ResourceLimiter);
991     let instance = linker.instantiate(&mut store, &module).unwrap();
992     let memory = instance.get_memory(&mut store, "m").unwrap();
993 
994     // Grow the memory, which should panic
995     memory.grow(&mut store, 3).unwrap();
996 }
997 
998 #[test]
999 #[should_panic(expected = "resource limiter memory growing")]
1000 #[cfg_attr(miri, ignore)]
1001 fn panic_in_memory_limiter_wasm_stack() {
1002     // Like the test above, except the memory.grow happens in wasm code
1003     // instead of a host function call.
1004     let engine = Engine::default();
1005     let linker = Linker::new(&engine);
1006 
1007     let module = Module::new(
1008         &engine,
1009         r#"
1010     (module
1011       (memory $m (export "m") 0)
1012       (func (export "grow") (param i32) (result i32)
1013         (memory.grow $m (local.get 0)))
1014     )"#,
1015     )
1016     .unwrap();
1017 
1018     let mut store = Store::new(&engine, Panic);
1019     store.limiter(|s| s as &mut dyn ResourceLimiter);
1020     let instance = linker.instantiate(&mut store, &module).unwrap();
1021     let grow = instance.get_func(&mut store, "grow").unwrap();
1022     let grow = grow.typed::<i32, i32>(&store).unwrap();
1023 
1024     // Grow the memory, which should panic
1025     grow.call(&mut store, 3).unwrap();
1026 }
1027 
1028 #[test]
1029 #[should_panic(expected = "resource limiter table growing")]
1030 fn panic_in_table_limiter() {
1031     let engine = Engine::default();
1032     let linker = Linker::new(&engine);
1033 
1034     let module = Module::new(&engine, r#"(module (table (export "t") 0 funcref))"#).unwrap();
1035 
1036     let mut store = Store::new(&engine, Panic);
1037     store.limiter(|s| s as &mut dyn ResourceLimiter);
1038     let instance = linker.instantiate(&mut store, &module).unwrap();
1039     let table = instance.get_table(&mut store, "t").unwrap();
1040 
1041     // Grow the table, which should panic
1042     table.grow(&mut store, 3, Ref::Func(None)).unwrap();
1043 }
1044 
1045 #[tokio::test]
1046 #[should_panic(expected = "async resource limiter memory growing")]
1047 #[cfg_attr(miri, ignore)]
1048 async fn panic_in_async_memory_limiter() {
1049     let mut config = Config::new();
1050     config.async_support(true);
1051     let engine = Engine::new(&config).unwrap();
1052     let linker = Linker::<Panic>::new(&engine);
1053 
1054     let module = Module::new(&engine, r#"(module (memory (export "m") 0))"#).unwrap();
1055 
1056     let mut store = Store::new(&engine, Panic);
1057     store.limiter_async(|s| s as &mut dyn ResourceLimiterAsync);
1058     let instance = linker.instantiate_async(&mut store, &module).await.unwrap();
1059     let memory = instance.get_memory(&mut store, "m").unwrap();
1060 
1061     // Grow the memory, which should panic
1062     memory.grow_async(&mut store, 3).await.unwrap();
1063 }
1064 
1065 #[tokio::test]
1066 #[should_panic(expected = "async resource limiter memory growing")]
1067 #[cfg_attr(miri, ignore)]
1068 async fn panic_in_async_memory_limiter_wasm_stack() {
1069     // Like the test above, except the memory.grow happens in
1070     // wasm code instead of a host function call.
1071     let mut config = Config::new();
1072     config.async_support(true);
1073     let engine = Engine::new(&config).unwrap();
1074     let linker = Linker::<Panic>::new(&engine);
1075 
1076     let module = Module::new(
1077         &engine,
1078         r#"
1079     (module
1080       (memory $m (export "m") 0)
1081       (func (export "grow") (param i32) (result i32)
1082         (memory.grow $m (local.get 0)))
1083     )"#,
1084     )
1085     .unwrap();
1086 
1087     let mut store = Store::new(&engine, Panic);
1088     store.limiter_async(|s| s as &mut dyn ResourceLimiterAsync);
1089     let instance = linker.instantiate_async(&mut store, &module).await.unwrap();
1090     let grow = instance.get_func(&mut store, "grow").unwrap();
1091     let grow = grow.typed::<i32, i32>(&store).unwrap();
1092 
1093     // Grow the memory, which should panic
1094     grow.call_async(&mut store, 3).await.unwrap();
1095 }
1096 
1097 #[tokio::test]
1098 #[should_panic(expected = "async resource limiter table growing")]
1099 #[cfg_attr(miri, ignore)]
1100 async fn panic_in_async_table_limiter() {
1101     let mut config = Config::new();
1102     config.async_support(true);
1103     let engine = Engine::new(&config).unwrap();
1104     let linker = Linker::<Panic>::new(&engine);
1105 
1106     let module = Module::new(&engine, r#"(module (table (export "t") 0 funcref))"#).unwrap();
1107 
1108     let mut store = Store::new(&engine, Panic);
1109     store.limiter_async(|s| s as &mut dyn ResourceLimiterAsync);
1110     let instance = linker.instantiate_async(&mut store, &module).await.unwrap();
1111     let table = instance.get_table(&mut store, "t").unwrap();
1112 
1113     // Grow the table, which should panic
1114     table
1115         .grow_async(&mut store, 3, Ref::Func(None))
1116         .await
1117         .unwrap();
1118 }
1119 
1120 #[test]
1121 #[cfg_attr(miri, ignore)]
1122 fn growth_trap() -> Result<()> {
1123     let engine = Engine::default();
1124     let module = Module::new(
1125         &engine,
1126         r#"(module
1127             (memory $m (export "m") 0)
1128             (table (export "t") 0 funcref)
1129             (func (export "grow") (param i32) (result i32)
1130               (memory.grow $m (local.get 0)))
1131            )"#,
1132     )?;
1133 
1134     let mut store = Store::new(
1135         &engine,
1136         StoreLimitsBuilder::new()
1137             .memory_size(WASM_PAGE_SIZE)
1138             .table_elements(1)
1139             .trap_on_grow_failure(true)
1140             .build(),
1141     );
1142     store.limiter(|s| s as &mut dyn ResourceLimiter);
1143 
1144     let instance = Instance::new(&mut store, &module, &[])?;
1145 
1146     // Test instance exports and host objects hitting the limit
1147     for memory in [
1148         instance.get_memory(&mut store, "m").unwrap(),
1149         Memory::new(&mut store, MemoryType::new(0, None))?,
1150     ] {
1151         memory.grow(&mut store, 1)?;
1152         assert!(memory.grow(&mut store, 1).is_err());
1153     }
1154 
1155     // Test instance exports and host objects hitting the limit
1156     for table in [
1157         instance.get_table(&mut store, "t").unwrap(),
1158         Table::new(
1159             &mut store,
1160             TableType::new(RefType::FUNCREF, 0, None),
1161             Ref::Func(None),
1162         )?,
1163     ] {
1164         table.grow(&mut store, 1, Ref::Func(None))?;
1165         assert!(table.grow(&mut store, 1, Ref::Func(None)).is_err());
1166     }
1167 
1168     let mut store = Store::new(&engine, store.data().clone());
1169     store.limiter(|s| s as &mut dyn ResourceLimiter);
1170     let instance = Instance::new(&mut store, &module, &[])?;
1171     let grow = instance.get_func(&mut store, "grow").unwrap();
1172     let grow = grow.typed::<i32, i32>(&store).unwrap();
1173     grow.call(&mut store, 1)?;
1174     assert!(grow.call(&mut store, 1).is_err());
1175 
1176     Ok(())
1177 }
1178