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