xref: /wasmtime-44.0.1/tests/all/gc.rs (revision 2cd48828)
1 use super::ref_types_module;
2 use std::sync::Arc;
3 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
4 use wasmtime::*;
5 
6 struct SetFlagOnDrop(Arc<AtomicBool>);
7 
8 impl Drop for SetFlagOnDrop {
drop(&mut self)9     fn drop(&mut self) {
10         self.0.store(true, SeqCst);
11     }
12 }
13 
14 #[test]
15 #[cfg_attr(miri, ignore)]
smoke_test_gc_no_epochs() -> Result<()>16 fn smoke_test_gc_no_epochs() -> Result<()> {
17     smoke_test_gc_impl(false)
18 }
19 
20 #[test]
21 #[cfg_attr(miri, ignore)]
smoke_test_gc_yes_epochs() -> Result<()>22 fn smoke_test_gc_yes_epochs() -> Result<()> {
23     smoke_test_gc_impl(true)
24 }
25 
smoke_test_gc_impl(use_epochs: bool) -> Result<()>26 fn smoke_test_gc_impl(use_epochs: bool) -> Result<()> {
27     let (mut store, module) = ref_types_module(
28         use_epochs,
29         r#"
30             (module
31                 (import "" "" (func $do_gc))
32                 (func $recursive (export "func") (param i32 externref) (result externref)
33                     local.get 0
34                     i32.eqz
35                     if (result externref)
36                         call $do_gc
37                         local.get 1
38                     else
39                         local.get 0
40                         i32.const 1
41                         i32.sub
42                         local.get 1
43                         call $recursive
44                     end
45                 )
46             )
47         "#,
48     )?;
49 
50     let do_gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| {
51         // Do a GC with `externref`s on the stack in Wasm frames.
52         caller.gc(None)
53     });
54     let instance = Instance::new(&mut store, &module, &[do_gc.into()])?;
55     let func = instance.get_func(&mut store, "func").unwrap();
56 
57     let inner_dropped = Arc::new(AtomicBool::new(false));
58 
59     {
60         let mut scope = RootScope::new(&mut store);
61 
62         let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
63         {
64             let args = [Val::I32(5), Val::ExternRef(Some(r))];
65             func.call(&mut scope, &args, &mut [Val::I32(0)])?;
66         }
67 
68         // Doing a GC should see that there aren't any `externref`s on the stack in
69         // Wasm frames anymore.
70         scope.as_context_mut().gc(None)?;
71 
72         // But the scope should still be rooting `r`.
73         assert!(!inner_dropped.load(SeqCst));
74     }
75 
76     // Exiting the scope and unrooting `r` should have dropped the inner
77     // `SetFlagOnDrop` value.
78     assert!(inner_dropped.load(SeqCst));
79 
80     Ok(())
81 }
82 
83 struct CountDrops(Arc<AtomicUsize>);
84 
85 impl Drop for CountDrops {
drop(&mut self)86     fn drop(&mut self) {
87         self.0.fetch_add(1, SeqCst);
88     }
89 }
90 
91 #[test]
92 #[cfg_attr(miri, ignore)]
wasm_dropping_refs() -> Result<()>93 fn wasm_dropping_refs() -> Result<()> {
94     let (mut store, module) = ref_types_module(
95         false,
96         r#"
97             (module
98                 (func (export "drop_ref") (param externref)
99                     nop
100                 )
101             )
102         "#,
103     )?;
104 
105     let instance = Instance::new(&mut store, &module, &[])?;
106     let drop_ref = instance.get_func(&mut store, "drop_ref").unwrap();
107 
108     let num_refs_dropped = Arc::new(AtomicUsize::new(0));
109 
110     for _ in 0..4096 {
111         let mut scope = RootScope::new(&mut store);
112         let r = ExternRef::new(&mut scope, CountDrops(num_refs_dropped.clone()))?;
113         let args = [Val::ExternRef(Some(r))];
114         drop_ref.call(&mut scope, &args, &mut [])?;
115     }
116 
117     // After doing a GC, all the refs should have been dropped.
118     store.gc(None)?;
119     assert_eq!(num_refs_dropped.load(SeqCst), 4096);
120 
121     return Ok(());
122 }
123 
124 #[test]
125 #[cfg_attr(miri, ignore)]
many_live_refs() -> Result<()>126 fn many_live_refs() -> Result<()> {
127     let mut wat = r#"
128         (module
129             ;; Make new `externref`s.
130             (import "" "make_ref" (func $make_ref (result externref)))
131 
132             ;; Observe an `externref` so it is kept live.
133             (import "" "observe_ref" (func $observe_ref (param externref)))
134 
135             (func (export "many_live_refs")
136     "#
137     .to_string();
138 
139     // This is more than the initial `VMExternRefActivationsTable` capacity, so
140     // it will need to allocate additional bump chunks.
141     const NUM_LIVE_REFS: usize = 1024;
142 
143     // Push `externref`s onto the stack.
144     for _ in 0..NUM_LIVE_REFS {
145         wat.push_str("(call $make_ref)\n");
146     }
147 
148     // Pop `externref`s from the stack. Because we pass each of them to a
149     // function call here, they are all live references for the duration of
150     // their lifetimes.
151     for _ in 0..NUM_LIVE_REFS {
152         wat.push_str("(call $observe_ref)\n");
153     }
154 
155     wat.push_str(
156         "
157             ) ;; func
158         ) ;; module
159         ",
160     );
161 
162     let (mut store, module) = ref_types_module(false, &wat)?;
163 
164     let live_refs = Arc::new(AtomicUsize::new(0));
165 
166     let make_ref = Func::wrap(&mut store, {
167         let live_refs = live_refs.clone();
168         move |mut caller: Caller<'_, _>| {
169             Ok(Some(ExternRef::new(
170                 &mut caller,
171                 CountLiveRefs::new(live_refs.clone()),
172             )?))
173         }
174     });
175 
176     let observe_ref = Func::wrap(
177         &mut store,
178         |caller: Caller<'_, _>, r: Option<Rooted<ExternRef>>| {
179             let r = r
180                 .unwrap()
181                 .data(&caller)
182                 .unwrap()
183                 .unwrap()
184                 .downcast_ref::<CountLiveRefs>()
185                 .unwrap();
186             assert!(r.live_refs.load(SeqCst) > 0);
187         },
188     );
189 
190     let instance = Instance::new(&mut store, &module, &[make_ref.into(), observe_ref.into()])?;
191     let many_live_refs = instance.get_func(&mut store, "many_live_refs").unwrap();
192 
193     many_live_refs.call(&mut store, &[], &mut [])?;
194 
195     store.as_context_mut().gc(None)?;
196     assert_eq!(live_refs.load(SeqCst), 0);
197 
198     return Ok(());
199 
200     struct CountLiveRefs {
201         live_refs: Arc<AtomicUsize>,
202     }
203 
204     impl CountLiveRefs {
205         fn new(live_refs: Arc<AtomicUsize>) -> Self {
206             live_refs.fetch_add(1, SeqCst);
207             Self { live_refs }
208         }
209     }
210 
211     impl Drop for CountLiveRefs {
212         fn drop(&mut self) {
213             self.live_refs.fetch_sub(1, SeqCst);
214         }
215     }
216 }
217 
218 #[test]
219 #[cfg_attr(miri, ignore)]
drop_externref_via_table_set() -> Result<()>220 fn drop_externref_via_table_set() -> Result<()> {
221     let (mut store, module) = ref_types_module(
222         false,
223         r#"
224             (module
225                 (table $t 1 externref)
226 
227                 (func (export "table-set") (param externref)
228                   (table.set $t (i32.const 0) (local.get 0))
229                 )
230             )
231         "#,
232     )?;
233 
234     let instance = Instance::new(&mut store, &module, &[])?;
235     let table_set = instance.get_func(&mut store, "table-set").unwrap();
236 
237     let foo_is_dropped = Arc::new(AtomicBool::new(false));
238     let bar_is_dropped = Arc::new(AtomicBool::new(false));
239 
240     {
241         let mut scope = RootScope::new(&mut store);
242 
243         let foo = ExternRef::new(&mut scope, SetFlagOnDrop(foo_is_dropped.clone()))?;
244         let bar = ExternRef::new(&mut scope, SetFlagOnDrop(bar_is_dropped.clone()))?;
245 
246         {
247             let args = vec![Val::ExternRef(Some(foo))];
248             table_set.call(&mut scope, &args, &mut [])?;
249         }
250 
251         scope.as_context_mut().gc(None)?;
252         assert!(!foo_is_dropped.load(SeqCst));
253         assert!(!bar_is_dropped.load(SeqCst));
254 
255         {
256             let args = vec![Val::ExternRef(Some(bar))];
257             table_set.call(&mut scope, &args, &mut [])?;
258         }
259     }
260 
261     store.gc(None)?;
262     assert!(foo_is_dropped.load(SeqCst));
263     assert!(!bar_is_dropped.load(SeqCst));
264 
265     table_set.call(&mut store, &[Val::ExternRef(None)], &mut [])?;
266     assert!(foo_is_dropped.load(SeqCst));
267     assert!(bar_is_dropped.load(SeqCst));
268 
269     Ok(())
270 }
271 
272 #[test]
273 #[cfg_attr(miri, ignore)]
global_drops_externref() -> Result<()>274 fn global_drops_externref() -> Result<()> {
275     let _ = env_logger::try_init();
276     test_engine(&Engine::default())?;
277 
278     let mut config = Config::new();
279     config.allocation_strategy(crate::small_pool_config());
280     test_engine(&Engine::new(&config)?)?;
281 
282     return Ok(());
283 
284     fn test_engine(engine: &Engine) -> Result<()> {
285         let mut store = Store::new(&engine, ());
286         let flag = Arc::new(AtomicBool::new(false));
287         let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
288         Global::new(
289             &mut store,
290             GlobalType::new(ValType::EXTERNREF, Mutability::Const),
291             externref.into(),
292         )?;
293         drop(store);
294         assert!(flag.load(SeqCst));
295 
296         let mut store = Store::new(&engine, ());
297         let module = Module::new(
298             &engine,
299             r#"
300                 (module
301                     (global (mut externref) (ref.null extern))
302 
303                     (func (export "run") (param externref)
304                         local.get 0
305                         global.set 0
306                     )
307                 )
308             "#,
309         )?;
310         let instance = Instance::new(&mut store, &module, &[])?;
311         let run = instance.get_typed_func::<Option<Rooted<ExternRef>>, ()>(&mut store, "run")?;
312         let flag = Arc::new(AtomicBool::new(false));
313         let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
314         run.call(&mut store, Some(externref))?;
315         drop(store);
316         assert!(flag.load(SeqCst));
317         Ok(())
318     }
319 }
320 
321 #[test]
322 #[cfg_attr(miri, ignore)]
table_drops_externref() -> Result<()>323 fn table_drops_externref() -> Result<()> {
324     let _ = env_logger::try_init();
325     test_engine(&Engine::default())?;
326 
327     let mut config = Config::new();
328     config.allocation_strategy(crate::small_pool_config());
329     test_engine(&Engine::new(&config)?)?;
330 
331     return Ok(());
332 
333     fn test_engine(engine: &Engine) -> Result<()> {
334         let mut store = Store::new(&engine, ());
335         let flag = Arc::new(AtomicBool::new(false));
336         let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
337         Table::new(
338             &mut store,
339             TableType::new(RefType::EXTERNREF, 1, None),
340             externref.into(),
341         )?;
342         drop(store);
343         assert!(flag.load(SeqCst));
344 
345         let mut store = Store::new(&engine, ());
346         let module = Module::new(
347             &engine,
348             r#"
349             (module
350                 (table 1 externref)
351 
352                 (func (export "run") (param externref)
353                     i32.const 0
354                     local.get 0
355                     table.set 0
356                 )
357             )
358         "#,
359         )?;
360         let instance = Instance::new(&mut store, &module, &[])?;
361         let run = instance.get_typed_func::<Option<Rooted<ExternRef>>, ()>(&mut store, "run")?;
362         let flag = Arc::new(AtomicBool::new(false));
363         let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
364         run.call(&mut store, Some(externref))?;
365         drop(store);
366         assert!(flag.load(SeqCst));
367         Ok(())
368     }
369 }
370 
371 #[test]
global_init_no_leak() -> Result<()>372 fn global_init_no_leak() -> Result<()> {
373     let (mut store, module) = ref_types_module(
374         false,
375         r#"
376             (module
377                 (import "" "" (global externref))
378                 (global externref (global.get 0))
379             )
380         "#,
381     )?;
382 
383     let flag = Arc::new(AtomicBool::new(false));
384     let externref = ExternRef::new(&mut store, SetFlagOnDrop(flag.clone()))?;
385     let global = Global::new(
386         &mut store,
387         GlobalType::new(ValType::EXTERNREF, Mutability::Const),
388         externref.into(),
389     )?;
390     Instance::new(&mut store, &module, &[global.into()])?;
391     drop(store);
392     assert!(flag.load(SeqCst));
393 
394     Ok(())
395 }
396 
397 #[test]
398 #[cfg_attr(miri, ignore)]
no_gc_middle_of_args() -> Result<()>399 fn no_gc_middle_of_args() -> Result<()> {
400     let (mut store, module) = ref_types_module(
401         false,
402         r#"
403             (module
404                 (import "" "return_some" (func $return (result externref externref externref)))
405                 (import "" "take_some" (func $take (param externref externref externref)))
406                 (func (export "run")
407                     (local i32)
408                     i32.const 1000
409                     local.set 0
410                     loop
411                         call $return
412                         call $take
413                         local.get 0
414                         i32.const -1
415                         i32.add
416                         local.tee 0
417                         br_if 0
418                     end
419                 )
420             )
421         "#,
422     )?;
423 
424     let mut linker = Linker::new(store.engine());
425     linker.func_wrap("", "return_some", |mut caller: Caller<'_, _>| {
426         let a = Some(ExternRef::new(&mut caller, String::from("a"))?);
427         let b = Some(ExternRef::new(&mut caller, String::from("b"))?);
428         let c = Some(ExternRef::new(&mut caller, String::from("c"))?);
429         Ok((a, b, c))
430     })?;
431     linker.func_wrap(
432         "",
433         "take_some",
434         |caller: Caller<'_, _>,
435          a: Option<Rooted<ExternRef>>,
436          b: Option<Rooted<ExternRef>>,
437          c: Option<Rooted<ExternRef>>| {
438             let a = a.unwrap();
439             let b = b.unwrap();
440             let c = c.unwrap();
441             assert_eq!(
442                 a.data(&caller)
443                     .expect("rooted")
444                     .expect("host data")
445                     .downcast_ref::<String>()
446                     .expect("is string"),
447                 "a"
448             );
449             assert_eq!(
450                 b.data(&caller)
451                     .expect("rooted")
452                     .expect("host data")
453                     .downcast_ref::<String>()
454                     .expect("is string"),
455                 "b"
456             );
457             assert_eq!(
458                 c.data(&caller)
459                     .expect("rooted")
460                     .expect("host data")
461                     .downcast_ref::<String>()
462                     .expect("is string"),
463                 "c"
464             );
465         },
466     )?;
467 
468     let instance = linker.instantiate(&mut store, &module)?;
469     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
470     func.call(&mut store, ())?;
471 
472     Ok(())
473 }
474 
475 #[test]
476 #[cfg_attr(miri, ignore)]
gc_and_tail_calls_and_stack_arguments() -> Result<()>477 fn gc_and_tail_calls_and_stack_arguments() -> Result<()> {
478     // Test that GC refs in tail-calls' stack arguments get properly accounted
479     // for in stack maps.
480     //
481     // What we do _not_ want to happen is for tail callers to be responsible for
482     // including stack arguments in their stack maps (and therefore whether or
483     // not they get marked at runtime). If that was the case, then we could have
484     // the following scenario:
485     //
486     // * `f` calls `g` without any stack arguments,
487     // * `g` tail calls `h` with GC ref stack arguments,
488     // * and then `h` triggers a GC.
489     //
490     // Because `g`, who is responsible for including the GC refs in its stack
491     // map in this hypothetical scenario, is no longer on the stack, we never
492     // see its stack map, and therefore never mark the GC refs, and then we
493     // collect them too early, and then we can get user-after-free bugs. Not
494     // good! Note also that `f`, which is the frame that `h` will return to,
495     // _cannot_ be responsible for including these stack arguments in its stack
496     // map, because it has no idea what frame will be returning to it, and it
497     // could be any number of different functions using that frame for long (and
498     // indirect!) tail-call chains.
499     //
500     // In Cranelift we avoid this scenario because stack arguments are eagerly
501     // loaded into virtual registers, and then when we insert a GC safe point,
502     // we spill these virtual registers to the callee stack frame, and the stack
503     // map includes entries for these stack slots.
504     //
505     // Nonetheless, this test exercises the above scenario just in case we do
506     // something in the future like lazily load stack arguments into virtual
507     // registers, to make sure that everything shows up in stack maps like they
508     // are supposed to.
509 
510     let (mut store, module) = ref_types_module(
511         false,
512         r#"
513             (module
514                 (import "" "make_some" (func $make (result externref externref externref)))
515                 (import "" "take_some" (func $take (param externref externref externref)))
516                 (import "" "gc" (func $gc))
517 
518                 (func $stack_args (param externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref externref)
519                   call $gc
520                   ;; Make sure all these GC refs are live, so that they need to
521                   ;; be put into the stack map.
522                   local.get 0
523                   local.get 1
524                   local.get 2
525                   call $take
526                   local.get 3
527                   local.get 4
528                   local.get 5
529                   call $take
530                   local.get 6
531                   local.get 7
532                   local.get 8
533                   call $take
534                   local.get 9
535                   local.get 10
536                   local.get 11
537                   call $take
538                   local.get 12
539                   local.get 13
540                   local.get 14
541                   call $take
542                   local.get 15
543                   local.get 16
544                   local.get 17
545                   call $take
546                   local.get 18
547                   local.get 19
548                   local.get 20
549                   call $take
550                   local.get 21
551                   local.get 22
552                   local.get 23
553                   call $take
554                   local.get 24
555                   local.get 25
556                   local.get 26
557                   call $take
558                   local.get 27
559                   local.get 28
560                   local.get 29
561                   call $take
562                 )
563 
564                 (func $no_stack_args
565                   call $make
566                   call $make
567                   call $make
568                   call $make
569                   call $make
570                   call $make
571                   call $make
572                   call $make
573                   call $make
574                   call $make
575                   return_call $stack_args
576                 )
577 
578                 (func (export "run")
579                     (local i32)
580                     i32.const 1000
581                     local.set 0
582                     loop
583                         call $no_stack_args
584                         local.get 0
585                         i32.const -1
586                         i32.add
587                         local.tee 0
588                         br_if 0
589                     end
590                 )
591             )
592         "#,
593     )?;
594 
595     let mut linker = Linker::new(store.engine());
596     linker.func_wrap("", "make_some", |mut caller: Caller<'_, _>| {
597         Ok((
598             Some(ExternRef::new(&mut caller, "a".to_string())?),
599             Some(ExternRef::new(&mut caller, "b".to_string())?),
600             Some(ExternRef::new(&mut caller, "c".to_string())?),
601         ))
602     })?;
603     linker.func_wrap(
604         "",
605         "take_some",
606         |caller: Caller<'_, _>,
607          a: Option<Rooted<ExternRef>>,
608          b: Option<Rooted<ExternRef>>,
609          c: Option<Rooted<ExternRef>>| {
610             let a = a.unwrap();
611             let b = b.unwrap();
612             let c = c.unwrap();
613             assert_eq!(
614                 a.data(&caller)
615                     .unwrap()
616                     .unwrap()
617                     .downcast_ref::<String>()
618                     .unwrap(),
619                 "a"
620             );
621             assert_eq!(
622                 b.data(&caller)
623                     .unwrap()
624                     .unwrap()
625                     .downcast_ref::<String>()
626                     .unwrap(),
627                 "b"
628             );
629             assert_eq!(
630                 c.data(&caller)
631                     .unwrap()
632                     .unwrap()
633                     .downcast_ref::<String>()
634                     .unwrap(),
635                 "c"
636             );
637         },
638     )?;
639     linker.func_wrap("", "gc", |mut caller: Caller<()>| caller.gc(None))?;
640 
641     let instance = linker.instantiate(&mut store, &module)?;
642     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
643     func.call(&mut store, ())?;
644 
645     Ok(())
646 }
647 
648 #[test]
649 #[cfg_attr(miri, ignore)]
no_leak_with_global_get_elem_segment() -> wasmtime::Result<()>650 fn no_leak_with_global_get_elem_segment() -> wasmtime::Result<()> {
651     let dropped = Arc::new(AtomicBool::new(false));
652 
653     let engine = Engine::default();
654     let mut store = Store::new(&engine, ());
655     let module = Module::new(
656         &engine,
657         r#"
658             (module
659                 (import "" "" (global $init externref))
660                 (start $f)
661                 (table $t 1 externref)
662                 (elem $e externref (global.get $init))
663 
664                 (func $f
665                     i32.const 0
666                     i32.const 0
667                     i32.const 1
668                     table.init $t $e
669 
670                     i32.const 0
671                     i32.const 0
672                     i32.const 1
673                     table.init $t $e
674                 )
675             )
676         "#,
677     )?;
678 
679     let externref = ExternRef::new(&mut store, SetFlagOnDrop(dropped.clone()))?;
680     let global = Global::new(
681         &mut store,
682         GlobalType::new(ValType::EXTERNREF, Mutability::Const),
683         externref.into(),
684     )?;
685 
686     Instance::new(&mut store, &module, &[global.into()])?;
687 
688     drop(store);
689 
690     assert!(dropped.load(SeqCst));
691     Ok(())
692 }
693 
694 #[test]
695 #[cfg_attr(miri, ignore)]
table_init_with_externref_global_get() -> wasmtime::Result<()>696 fn table_init_with_externref_global_get() -> wasmtime::Result<()> {
697     let dropped = Arc::new(AtomicBool::new(false));
698 
699     let mut config = Config::new();
700     config.wasm_function_references(true);
701     let engine = Engine::new(&config)?;
702     let mut store = Store::new(&engine, ());
703     let module = Module::new(
704         &engine,
705         r#"
706             (module
707                 (import "" "" (global $init externref))
708                 (table $t 1 externref (global.get $init))
709             )
710         "#,
711     )?;
712 
713     let externref = ExternRef::new(&mut store, SetFlagOnDrop(dropped.clone()))?;
714     let global = Global::new(
715         &mut store,
716         GlobalType::new(ValType::EXTERNREF, Mutability::Const),
717         externref.into(),
718     )?;
719 
720     Instance::new(&mut store, &module, &[global.into()])?;
721 
722     drop(store);
723 
724     assert!(dropped.load(SeqCst));
725     Ok(())
726 }
727 
728 #[test]
rooted_gets_collected_after_scope_exit() -> Result<()>729 fn rooted_gets_collected_after_scope_exit() -> Result<()> {
730     let mut store = Store::<()>::default();
731     let flag = Arc::new(AtomicBool::new(false));
732 
733     {
734         let mut scope = RootScope::new(&mut store);
735         let _externref = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
736 
737         scope.as_context_mut().gc(None)?;
738         assert!(!flag.load(SeqCst), "not dropped when still rooted");
739     }
740 
741     store.as_context_mut().gc(None)?;
742     assert!(flag.load(SeqCst), "dropped after being unrooted");
743 
744     Ok(())
745 }
746 
747 #[test]
owned_rooted_gets_collected_after_unrooting() -> Result<()>748 fn owned_rooted_gets_collected_after_unrooting() -> Result<()> {
749     let mut store = Store::<()>::default();
750     let flag = Arc::new(AtomicBool::new(false));
751 
752     let externref = {
753         let mut scope = RootScope::new(&mut store);
754         ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?.to_owned_rooted(&mut scope)?
755     };
756 
757     store.gc(None)?;
758     assert!(!flag.load(SeqCst), "not dropped when still rooted");
759 
760     drop(externref);
761     store.gc(None)?;
762     assert!(flag.load(SeqCst), "dropped after being unrooted");
763 
764     Ok(())
765 }
766 
767 #[test]
768 #[cfg_attr(miri, ignore)]
round_trip_gc_ref_through_typed_wasm_func() -> Result<()>769 fn round_trip_gc_ref_through_typed_wasm_func() -> Result<()> {
770     let mut store = Store::<()>::default();
771     let module = Module::new(
772         store.engine(),
773         r#"
774             (module
775                 (import "" "" (func $gc))
776                 (func (export "f") (param externref) (result externref)
777                     call $gc
778                     local.get 0
779                 )
780             )
781         "#,
782     )?;
783     let gc = Func::wrap(&mut store, |mut caller: Caller<'_, _>| caller.gc(None));
784     let instance = Instance::new(&mut store, &module, &[gc.into()])?;
785     let f = instance
786         .get_typed_func::<Option<Rooted<ExternRef>>, Option<Rooted<ExternRef>>>(&mut store, "f")?;
787     let x1 = ExternRef::new(&mut store, 1234)?;
788     let x2 = f.call(&mut store, Some(x1))?.unwrap();
789     assert!(Rooted::ref_eq(&store, &x1, &x2)?);
790     Ok(())
791 }
792 
793 #[test]
794 #[cfg_attr(miri, ignore)]
round_trip_gc_ref_through_func_wrap() -> Result<()>795 fn round_trip_gc_ref_through_func_wrap() -> Result<()> {
796     let mut store = Store::<()>::default();
797     let f = Func::wrap(
798         &mut store,
799         |mut caller: Caller<'_, _>, x: Rooted<ExternRef>| {
800             caller.gc(None)?;
801             Ok(x)
802         },
803     );
804     let f = f.typed::<Rooted<ExternRef>, Rooted<ExternRef>>(&store)?;
805     let x1 = ExternRef::new(&mut store, 1234)?;
806     let x2 = f.call(&mut store, x1)?;
807     assert!(Rooted::ref_eq(&store, &x1, &x2)?);
808     Ok(())
809 }
810 
811 #[test]
to_raw_from_raw_doesnt_leak() -> Result<()>812 fn to_raw_from_raw_doesnt_leak() -> Result<()> {
813     let mut store = Store::<()>::default();
814     let flag = Arc::new(AtomicBool::new(false));
815 
816     {
817         let mut scope = RootScope::new(&mut store);
818         let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
819         let raw = x.to_raw(&mut scope)?;
820         let _x = ExternRef::from_raw(&mut scope, raw);
821     }
822 
823     store.gc(None)?;
824     assert!(flag.load(SeqCst));
825     Ok(())
826 }
827 
828 #[test]
table_fill_doesnt_leak() -> Result<()>829 fn table_fill_doesnt_leak() -> Result<()> {
830     let _ = env_logger::try_init();
831 
832     let mut store = Store::<()>::default();
833     let flag = Arc::new(AtomicBool::new(false));
834 
835     {
836         let mut scope = RootScope::new(&mut store);
837         let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
838         let table = Table::new(
839             &mut scope,
840             TableType::new(RefType::EXTERNREF, 10, Some(10)),
841             x.into(),
842         )?;
843         table.fill(&mut scope, 0, Ref::Extern(None), 10)?;
844     }
845 
846     store.gc(None)?;
847     assert!(flag.load(SeqCst));
848     Ok(())
849 }
850 
851 #[test]
852 #[cfg_attr(miri, ignore)]
table_copy_doesnt_leak() -> Result<()>853 fn table_copy_doesnt_leak() -> Result<()> {
854     let _ = env_logger::try_init();
855 
856     let mut store = Store::<()>::default();
857     let flag = Arc::new(AtomicBool::new(false));
858 
859     {
860         let mut scope = RootScope::new(&mut store);
861         let table = Table::new(
862             &mut scope,
863             TableType::new(RefType::EXTERNREF, 10, Some(10)),
864             Ref::Extern(None),
865         )?;
866 
867         let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
868         table.fill(&mut scope, 2, x.into(), 3)?;
869 
870         Table::copy(&mut scope, &table, 0, &table, 5, 5)?;
871     }
872 
873     store.gc(None)?;
874     assert!(flag.load(SeqCst));
875     Ok(())
876 }
877 
878 #[test]
879 #[cfg_attr(miri, ignore)]
table_set_doesnt_leak() -> Result<()>880 fn table_set_doesnt_leak() -> Result<()> {
881     let _ = env_logger::try_init();
882 
883     let mut store = Store::<()>::default();
884     let flag = Arc::new(AtomicBool::new(false));
885 
886     {
887         let mut scope = RootScope::new(&mut store);
888         let table = Table::new(
889             &mut scope,
890             TableType::new(RefType::EXTERNREF, 10, Some(10)),
891             Ref::Extern(None),
892         )?;
893 
894         let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
895         table.set(&mut scope, 2, x.into())?;
896         table.set(&mut scope, 2, x.into())?;
897         table.set(&mut scope, 2, Ref::Extern(None))?;
898     }
899 
900     store.gc(None)?;
901     assert!(flag.load(SeqCst));
902     Ok(())
903 }
904 
905 #[test]
906 #[cfg_attr(miri, ignore)]
table_grow_doesnt_leak() -> Result<()>907 fn table_grow_doesnt_leak() -> Result<()> {
908     let _ = env_logger::try_init();
909 
910     let mut store = Store::<()>::default();
911     let flag = Arc::new(AtomicBool::new(false));
912 
913     {
914         let mut scope = RootScope::new(&mut store);
915         let table = Table::new(
916             &mut scope,
917             TableType::new(RefType::EXTERNREF, 10, Some(10)),
918             Ref::Extern(None),
919         )?;
920 
921         let x = ExternRef::new(&mut scope, SetFlagOnDrop(flag.clone()))?;
922         table.grow(&mut scope, 0, x.into())?;
923     }
924 
925     store.gc(None)?;
926     assert!(flag.load(SeqCst));
927     Ok(())
928 }
929 
930 // This is a test that the argument to `table.init` is properly handled w.r.t.
931 // write barriers and such. This doesn't use `SetFlagOnDrop` because that would
932 // require initializing a table with an element initialized from a global and
933 // the global keeps the externref alive regardless. Instead this has a small GC
934 // heap and we continuously make more garbage than is in the heap and expect
935 // this to work as when it triggers a GC everything prior should get cleaned up.
936 #[test]
937 #[cfg_attr(miri, ignore)]
table_init_doesnt_leak() -> Result<()>938 fn table_init_doesnt_leak() -> Result<()> {
939     const SIZE: u64 = 64 << 10;
940 
941     let _ = env_logger::try_init();
942 
943     let mut config = Config::new();
944     config.wasm_gc(true);
945     config.wasm_function_references(true);
946     config.memory_may_move(false);
947     config.memory_reservation(SIZE);
948     config.memory_reservation_for_growth(0);
949     let engine = Engine::new(&config)?;
950     let mut store = Store::new(&engine, ());
951 
952     let module = Module::new(
953         store.engine(),
954         r#"
955             (module
956                 (table 1 arrayref)
957 
958                 (type $a (array i31ref))
959 
960                 (func (export "run")
961                     i32.const 0
962                     i32.const 0
963                     i32.const 1
964                     table.init $e)
965                 (elem $e arrayref (array.new $a (ref.i31 (i32.const 0)) (i32.const 200)))
966             )
967         "#,
968     )?;
969 
970     let instance = Instance::new(&mut store, &module, &[])?;
971     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
972     for _ in 0..200 {
973         func.call(&mut store, ())?;
974     }
975 
976     Ok(())
977 }
978 
979 #[test]
ref_matches() -> Result<()>980 fn ref_matches() -> Result<()> {
981     let mut store = Store::<()>::default();
982     let engine = store.engine().clone();
983 
984     let func_ty = FuncType::new(&engine, None, None);
985     let func_ref_ty = RefType::new(true, HeapType::ConcreteFunc(func_ty.clone()));
986     let f = Func::new(&mut store, func_ty, |_, _, _| Ok(()));
987 
988     let pre = StructRefPre::new(&mut store, StructType::new(&engine, [])?);
989     let s = StructRef::new(&mut store, &pre, &[])?.to_anyref();
990 
991     let pre = ArrayRefPre::new(
992         &mut store,
993         ArrayType::new(&engine, FieldType::new(Mutability::Const, StorageType::I8)),
994     );
995     let a = ArrayRef::new(&mut store, &pre, &Val::I32(0), 0)?.to_anyref();
996 
997     let i31 = AnyRef::from_i31(&mut store, I31::wrapping_i32(1234));
998 
999     let e = ExternRef::new(&mut store, "hello")?;
1000 
1001     for (val, ty, expected) in [
1002         // nulls to nullexternref
1003         (Ref::Extern(None), RefType::NULLEXTERNREF, true),
1004         (Ref::Any(None), RefType::NULLEXTERNREF, false),
1005         (Ref::Func(None), RefType::NULLEXTERNREF, false),
1006         // nulls to externref
1007         (Ref::Extern(None), RefType::EXTERNREF, true),
1008         (Ref::Any(None), RefType::EXTERNREF, false),
1009         (Ref::Func(None), RefType::EXTERNREF, false),
1010         // nulls to nullref
1011         (Ref::Extern(None), RefType::NULLREF, false),
1012         (Ref::Any(None), RefType::NULLREF, true),
1013         (Ref::Func(None), RefType::NULLREF, false),
1014         // nulls to structref
1015         (Ref::Extern(None), RefType::STRUCTREF, false),
1016         (Ref::Any(None), RefType::STRUCTREF, true),
1017         (Ref::Func(None), RefType::STRUCTREF, false),
1018         // nulls to arrayref
1019         (Ref::Extern(None), RefType::ARRAYREF, false),
1020         (Ref::Any(None), RefType::ARRAYREF, true),
1021         (Ref::Func(None), RefType::ARRAYREF, false),
1022         // nulls to i31ref
1023         (Ref::Extern(None), RefType::I31REF, false),
1024         (Ref::Any(None), RefType::I31REF, true),
1025         (Ref::Func(None), RefType::I31REF, false),
1026         // nulls to eqref
1027         (Ref::Extern(None), RefType::EQREF, false),
1028         (Ref::Any(None), RefType::EQREF, true),
1029         (Ref::Func(None), RefType::EQREF, false),
1030         // nulls to anyref
1031         (Ref::Extern(None), RefType::ANYREF, false),
1032         (Ref::Any(None), RefType::ANYREF, true),
1033         (Ref::Func(None), RefType::ANYREF, false),
1034         // non-null structref
1035         (Ref::Any(Some(s)), RefType::NULLFUNCREF, false),
1036         (Ref::Any(Some(s)), func_ref_ty.clone(), false),
1037         (Ref::Any(Some(s)), RefType::FUNCREF, false),
1038         (Ref::Any(Some(s)), RefType::NULLEXTERNREF, false),
1039         (Ref::Any(Some(s)), RefType::EXTERNREF, false),
1040         (Ref::Any(Some(s)), RefType::NULLREF, false),
1041         (Ref::Any(Some(s)), RefType::STRUCTREF, true),
1042         (Ref::Any(Some(s)), RefType::ARRAYREF, false),
1043         (Ref::Any(Some(s)), RefType::I31REF, false),
1044         (Ref::Any(Some(s)), RefType::EQREF, true),
1045         (Ref::Any(Some(s)), RefType::ANYREF, true),
1046         // non-null arrayref
1047         (Ref::Any(Some(a)), RefType::NULLFUNCREF, false),
1048         (Ref::Any(Some(a)), func_ref_ty.clone(), false),
1049         (Ref::Any(Some(a)), RefType::FUNCREF, false),
1050         (Ref::Any(Some(a)), RefType::NULLEXTERNREF, false),
1051         (Ref::Any(Some(a)), RefType::EXTERNREF, false),
1052         (Ref::Any(Some(a)), RefType::NULLREF, false),
1053         (Ref::Any(Some(a)), RefType::STRUCTREF, false),
1054         (Ref::Any(Some(a)), RefType::ARRAYREF, true),
1055         (Ref::Any(Some(a)), RefType::I31REF, false),
1056         (Ref::Any(Some(a)), RefType::EQREF, true),
1057         (Ref::Any(Some(a)), RefType::ANYREF, true),
1058         // non-null i31ref
1059         (Ref::Any(Some(i31)), RefType::NULLFUNCREF, false),
1060         (Ref::Any(Some(i31)), func_ref_ty.clone(), false),
1061         (Ref::Any(Some(i31)), RefType::FUNCREF, false),
1062         (Ref::Any(Some(i31)), RefType::NULLEXTERNREF, false),
1063         (Ref::Any(Some(i31)), RefType::EXTERNREF, false),
1064         (Ref::Any(Some(i31)), RefType::NULLREF, false),
1065         (Ref::Any(Some(i31)), RefType::STRUCTREF, false),
1066         (Ref::Any(Some(i31)), RefType::ARRAYREF, false),
1067         (Ref::Any(Some(i31)), RefType::I31REF, true),
1068         (Ref::Any(Some(i31)), RefType::EQREF, true),
1069         (Ref::Any(Some(i31)), RefType::ANYREF, true),
1070         // non-null funcref
1071         (Ref::Func(Some(f)), RefType::NULLFUNCREF, false),
1072         (Ref::Func(Some(f)), func_ref_ty.clone(), true),
1073         (Ref::Func(Some(f)), RefType::FUNCREF, true),
1074         (Ref::Func(Some(f)), RefType::NULLEXTERNREF, false),
1075         (Ref::Func(Some(f)), RefType::EXTERNREF, false),
1076         (Ref::Func(Some(f)), RefType::NULLREF, false),
1077         (Ref::Func(Some(f)), RefType::STRUCTREF, false),
1078         (Ref::Func(Some(f)), RefType::ARRAYREF, false),
1079         (Ref::Func(Some(f)), RefType::I31REF, false),
1080         (Ref::Func(Some(f)), RefType::EQREF, false),
1081         (Ref::Func(Some(f)), RefType::ANYREF, false),
1082         // non-null externref
1083         (Ref::Extern(Some(e)), RefType::NULLFUNCREF, false),
1084         (Ref::Extern(Some(e)), func_ref_ty.clone(), false),
1085         (Ref::Extern(Some(e)), RefType::FUNCREF, false),
1086         (Ref::Extern(Some(e)), RefType::NULLEXTERNREF, false),
1087         (Ref::Extern(Some(e)), RefType::EXTERNREF, true),
1088         (Ref::Extern(Some(e)), RefType::NULLREF, false),
1089         (Ref::Extern(Some(e)), RefType::STRUCTREF, false),
1090         (Ref::Extern(Some(e)), RefType::ARRAYREF, false),
1091         (Ref::Extern(Some(e)), RefType::I31REF, false),
1092         (Ref::Extern(Some(e)), RefType::EQREF, false),
1093         (Ref::Extern(Some(e)), RefType::ANYREF, false),
1094     ] {
1095         let actual = val.matches_ty(&mut store, &ty)?;
1096         assert_eq!(
1097             actual, expected,
1098             "{val:?} matches {ty:?}? expected {expected}, got {actual}"
1099         );
1100     }
1101 
1102     Ok(())
1103 }
1104 
1105 #[test]
1106 #[cfg_attr(miri, ignore)]
issue_9669() -> Result<()>1107 fn issue_9669() -> Result<()> {
1108     let _ = env_logger::try_init();
1109 
1110     let mut config = Config::new();
1111     config.wasm_function_references(true);
1112     config.wasm_gc(true);
1113     config.collector(Collector::DeferredReferenceCounting);
1114 
1115     let engine = Engine::new(&config)?;
1116 
1117     let module = Module::new(
1118         &engine,
1119         r#"
1120             (module
1121                 (type $empty (struct))
1122                 (type $thing (struct
1123                     (field $field1 (ref $empty))
1124                     (field $field2 (ref $empty))
1125                 ))
1126 
1127                 (func (export "run")
1128                     (local $object (ref $thing))
1129 
1130                     struct.new $empty
1131                     struct.new $empty
1132                     struct.new $thing
1133 
1134                     local.tee $object
1135                     struct.get $thing $field1
1136                     drop
1137 
1138                     local.get $object
1139                     struct.get $thing $field2
1140                     drop
1141                 )
1142             )
1143         "#,
1144     )?;
1145 
1146     let mut store = Store::new(&engine, ());
1147     let instance = Instance::new(&mut store, &module, &[])?;
1148 
1149     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1150     func.call(&mut store, ())?;
1151 
1152     Ok(())
1153 }
1154 
1155 #[test]
drc_transitive_drop_cons_list() -> Result<()>1156 fn drc_transitive_drop_cons_list() -> Result<()> {
1157     let _ = env_logger::try_init();
1158 
1159     let mut config = Config::new();
1160     config.wasm_function_references(true);
1161     config.wasm_gc(true);
1162     config.collector(Collector::DeferredReferenceCounting);
1163 
1164     let engine = Engine::new(&config)?;
1165 
1166     // Define a module that defines a recursive type (a `cons` list of
1167     // `externref`s) and exports a global of that type so that we can access it
1168     // from the host, because we don't yet have host APIs for defining recursive
1169     // types or rec groups.
1170     let module = Module::new(
1171         &engine,
1172         r#"
1173             (module
1174                 (type $cons (struct (field externref) (field (ref null $cons))))
1175                 (global (export "g") (ref null $cons) (ref.null $cons))
1176             )
1177         "#,
1178     )?;
1179 
1180     let export = module.exports().nth(0).unwrap().ty();
1181     let global = export.unwrap_global();
1182     let ref_ty = global.content().unwrap_ref();
1183     let struct_ty = ref_ty.heap_type().unwrap_concrete_struct();
1184 
1185     let mut store = Store::new(&engine, ());
1186 
1187     let pre = StructRefPre::new(&mut store, struct_ty.clone());
1188     let num_refs_dropped = Arc::new(AtomicUsize::new(0));
1189 
1190     let len = if cfg!(miri) { 2 } else { 100 };
1191     {
1192         let mut store = RootScope::new(&mut store);
1193 
1194         let mut cdr = None;
1195         for _ in 0..len {
1196             let externref = ExternRef::new(&mut store, CountDrops(num_refs_dropped.clone()))?;
1197             let cons = StructRef::new(&mut store, &pre, &[externref.into(), cdr.into()])?;
1198             cdr = Some(cons);
1199         }
1200 
1201         // Still holding the cons list alive at this point.
1202         assert_eq!(num_refs_dropped.load(SeqCst), 0);
1203     }
1204 
1205     // Not holding the cons list alive anymore; should transitively drop
1206     // everything we created.
1207     store.gc(None)?;
1208     assert_eq!(num_refs_dropped.load(SeqCst), len);
1209 
1210     Ok(())
1211 }
1212 
1213 #[test]
drc_transitive_drop_nested_arrays_tree() -> Result<()>1214 fn drc_transitive_drop_nested_arrays_tree() -> Result<()> {
1215     let _ = env_logger::try_init();
1216 
1217     let mut config = Config::new();
1218     config.wasm_function_references(true);
1219     config.wasm_gc(true);
1220     config.collector(Collector::DeferredReferenceCounting);
1221 
1222     let engine = Engine::new(&config)?;
1223 
1224     let array_ty = ArrayType::new(
1225         &engine,
1226         FieldType::new(
1227             Mutability::Var,
1228             StorageType::ValType(ValType::Ref(RefType::ANYREF)),
1229         ),
1230     );
1231 
1232     let mut store = Store::new(&engine, ());
1233     let pre = ArrayRefPre::new(&mut store, array_ty);
1234     let num_refs_dropped = Arc::new(AtomicUsize::new(0));
1235     let mut expected = 0;
1236 
1237     fn recursively_build_tree(
1238         mut store: &mut RootScope<&mut Store<()>>,
1239         pre: &ArrayRefPre,
1240         num_refs_dropped: &Arc<AtomicUsize>,
1241         expected: &mut usize,
1242         depth: u32,
1243     ) -> Result<Rooted<AnyRef>> {
1244         let max = if cfg!(miri) { 1 } else { 3 };
1245         if depth >= max {
1246             *expected += 1;
1247             let e = ExternRef::new(&mut store, CountDrops(num_refs_dropped.clone()))?;
1248             AnyRef::convert_extern(&mut store, e)
1249         } else {
1250             let left = recursively_build_tree(store, pre, num_refs_dropped, expected, depth + 1)?;
1251             let right = recursively_build_tree(store, pre, num_refs_dropped, expected, depth + 1)?;
1252             let arr = ArrayRef::new_fixed(store, pre, &[left.into(), right.into()])?;
1253             Ok(arr.to_anyref())
1254         }
1255     }
1256 
1257     {
1258         let mut store = RootScope::new(&mut store);
1259         let _tree = recursively_build_tree(&mut store, &pre, &num_refs_dropped, &mut expected, 0)?;
1260 
1261         // Still holding the tree alive at this point.
1262         assert_eq!(num_refs_dropped.load(SeqCst), 0);
1263     }
1264 
1265     // Not holding the tree alive anymore; should transitively drop everything
1266     // we created.
1267     store.gc(None)?;
1268     assert_eq!(num_refs_dropped.load(SeqCst), expected);
1269 
1270     Ok(())
1271 }
1272 
1273 #[test]
1274 #[cfg_attr(miri, ignore)]
drc_traces_the_correct_number_of_gc_refs_in_arrays() -> Result<()>1275 fn drc_traces_the_correct_number_of_gc_refs_in_arrays() -> Result<()> {
1276     let _ = env_logger::try_init();
1277 
1278     let mut config = Config::new();
1279     config.wasm_function_references(true);
1280     config.wasm_gc(true);
1281     config.collector(Collector::DeferredReferenceCounting);
1282 
1283     let engine = Engine::new(&config)?;
1284     let mut store = Store::new(&engine, ());
1285 
1286     // The DRC collector was mistakenly reporting that arrays of GC refs had
1287     // `size_of(elems)` outgoing edges, rather than `len(elems)` edges. None of
1288     // our existing tests happened to trigger this bug because although we were
1289     // tricking the collector into tracing unallocated GC heap memory, it was
1290     // all zeroed out and was treated as null GC references. We can avoid that
1291     // in this regression test by first painting the heap with a large poison
1292     // value before we start allocating arrays, so that if the GC tries tracing
1293     // bogus heap memory, it finds very large GC ref heap indices and ultimately
1294     // tries to follow them outside the bounds of the GC heap, which (before
1295     // this bug was fixed) would lead to a panic.
1296 
1297     let array_i8_ty = ArrayType::new(&engine, FieldType::new(Mutability::Var, StorageType::I8));
1298     let array_i8_pre = ArrayRefPre::new(&mut store, array_i8_ty);
1299 
1300     {
1301         let mut store = RootScope::new(&mut store);
1302 
1303         // Spray a poison pattern across the heap.
1304         let len = 1_000_000;
1305         let _poison = ArrayRef::new(&mut store, &array_i8_pre, &Val::I32(-1), len);
1306     }
1307 
1308     // Make sure the poison array is collected.
1309     store.gc(None)?;
1310 
1311     // Allocate and then collect an array of GC refs from Wasm. This should not
1312     // trick the collector into tracing any poison and panicking.
1313     let module = Module::new(
1314         &engine,
1315         r#"
1316             (module
1317                 (type $ty (array (mut anyref)))
1318                 (start $f)
1319                 (func $f
1320                     (drop (array.new $ty (ref.null any) (i32.const 1_000)))
1321                 )
1322             )
1323         "#,
1324     )?;
1325     let _instance = Instance::new(&mut store, &module, &[])?;
1326     store.gc(None)?;
1327 
1328     Ok(())
1329 }
1330 
1331 // Test that we can completely fill the GC heap until we get an OOM. This
1332 // exercises growing the GC heap and that we configure compilation tunables and
1333 // runtime memories backing GC heaps correctly.
1334 #[test]
1335 #[cfg_attr(any(miri, not(target_pointer_width = "64")), ignore)]
gc_heap_oom() -> Result<()>1336 fn gc_heap_oom() -> Result<()> {
1337     if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
1338         return Ok(());
1339     }
1340 
1341     let _ = env_logger::try_init();
1342 
1343     for heap_size in [
1344         // Very small heap.
1345         1 << 16,
1346         // Bigger heap: 4 GiB
1347         1 << 32,
1348     ] {
1349         for pooling in [true, false] {
1350             let mut config = Config::new();
1351             config.wasm_function_references(true);
1352             config.wasm_gc(true);
1353             config.collector(Collector::Null);
1354             config.memory_reservation(heap_size);
1355             config.memory_reservation_for_growth(0);
1356             config.memory_guard_size(0);
1357             config.memory_may_move(false);
1358 
1359             if pooling {
1360                 let mut pooling = crate::small_pool_config();
1361                 pooling.max_memory_size(heap_size.try_into().unwrap());
1362                 config.allocation_strategy(InstanceAllocationStrategy::Pooling(pooling));
1363             }
1364 
1365             let engine = Engine::new(&config)?;
1366 
1367             let module = Module::new(
1368                 &engine,
1369                 r#"
1370                 (module
1371                     (type $s (struct))
1372                     (global $g (export "g") (mut i32) (i32.const 0))
1373                     (func (export "run")
1374                       loop
1375                         struct.new $s
1376 
1377                         global.get $g
1378                         i32.const 1
1379                         i32.add
1380                         global.set $g
1381 
1382                         br 0
1383                       end
1384                     )
1385                 )
1386             "#,
1387             )?;
1388 
1389             let mut store = Store::new(&engine, ());
1390             let instance = Instance::new(&mut store, &module, &[])?;
1391 
1392             let run = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1393             let err = run.call(&mut store, ()).expect_err("should oom");
1394             assert!(err.is::<Trap>(), "should get trap, got: {err:?}");
1395             let trap = err.downcast::<Trap>().unwrap();
1396             assert_eq!(trap, Trap::AllocationTooLarge);
1397 
1398             let g = instance.get_global(&mut store, "g").unwrap();
1399             const SIZE_OF_NULL_GC_HEADER: u64 = 8;
1400             const FUDGE: u64 = 2;
1401             let actual = g.get(&mut store).unwrap_i32() as u64;
1402             let expected = heap_size / SIZE_OF_NULL_GC_HEADER;
1403             assert!(
1404                 actual.abs_diff(expected) <= FUDGE,
1405                 "actual approx= expected failed: \
1406                  actual = {actual}, expected = {expected}, FUDGE={FUDGE}"
1407             );
1408         }
1409     }
1410     Ok(())
1411 }
1412 
1413 #[test]
1414 #[cfg_attr(miri, ignore)]
issue_10772() -> Result<()>1415 fn issue_10772() -> Result<()> {
1416     let mut store = crate::gc_store()?;
1417     let engine = store.engine().clone();
1418 
1419     let module = Module::new(
1420         &engine,
1421         r#"
1422             (module
1423                 (type $empty (struct))
1424                 (type $tuple-concrete (struct (field (ref $empty))))
1425                 (type $tuple-abstract (struct (field (ref struct))))
1426                 (func (export "abstract") (param $t (ref $tuple-abstract))
1427                     (drop (ref.cast (ref $tuple-concrete) (local.get $t)))
1428                 )
1429             )
1430         "#,
1431     )?;
1432 
1433     let linker = Linker::new(&engine);
1434 
1435     let instance = linker.instantiate(&mut store, &module)?;
1436     let abstract_ = instance.get_func(&mut store, "abstract").unwrap();
1437     let empty_pre = StructRefPre::new(&mut store, StructType::new(&engine, [])?);
1438     let empty_struct = StructRef::new(&mut store, &empty_pre, &[])?;
1439     let tuple_pre = StructRefPre::new(
1440         &mut store,
1441         StructType::new(
1442             &engine,
1443             [FieldType::new(
1444                 Mutability::Const,
1445                 StorageType::ValType(ValType::Ref(RefType::new(false, HeapType::Struct))),
1446             )],
1447         )?,
1448     );
1449     let tuple_struct = StructRef::new(&mut store, &tuple_pre, &[empty_struct.into()])?;
1450     let tuple_any = Val::from(tuple_struct);
1451 
1452     match abstract_.call(store, &[tuple_any], &mut []) {
1453         Ok(()) => panic!("should have trapped on cast failure"),
1454         Err(e) => {
1455             let trap = e.downcast::<Trap>().expect("should fail with a trap");
1456             assert_eq!(trap, Trap::CastFailure);
1457         }
1458     }
1459 
1460     Ok(())
1461 }
1462 
1463 #[test]
drc_gc_inbetween_host_calls() -> Result<()>1464 fn drc_gc_inbetween_host_calls() -> Result<()> {
1465     let _ = env_logger::try_init();
1466 
1467     let mut config = Config::new();
1468     config.wasm_function_references(true);
1469     config.wasm_gc(true);
1470     config.collector(Collector::DeferredReferenceCounting);
1471 
1472     let engine = Engine::new(&config)?;
1473 
1474     let mut store = Store::new(&engine, ());
1475     let func = Func::wrap(&mut store, |_: Option<Rooted<ExternRef>>| {});
1476 
1477     let mut invoke_func = || {
1478         let inner_dropped = Arc::new(AtomicBool::new(false));
1479         {
1480             let mut scope = RootScope::new(&mut store);
1481             let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1482             func.call(&mut scope, &[r.into()], &mut [])?;
1483         }
1484 
1485         assert!(!inner_dropped.load(SeqCst));
1486         store.gc(None)?;
1487         assert!(inner_dropped.load(SeqCst));
1488         wasmtime::error::Ok(())
1489     };
1490 
1491     invoke_func()?;
1492     invoke_func()?;
1493 
1494     Ok(())
1495 }
1496 
1497 #[test]
owned_rooted() -> Result<()>1498 fn owned_rooted() -> Result<()> {
1499     let _ = env_logger::try_init();
1500 
1501     let mut config = Config::new();
1502     config.wasm_function_references(true);
1503     config.wasm_gc(true);
1504     config.collector(Collector::DeferredReferenceCounting);
1505 
1506     let engine = Engine::new(&config)?;
1507 
1508     let mut store = Store::new(&engine, ());
1509     let inner_dropped = Arc::new(AtomicBool::new(false));
1510     let r = {
1511         let mut scope = RootScope::new(&mut store);
1512         let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1513         r.to_owned_rooted(&mut scope)?
1514     };
1515     assert!(!inner_dropped.load(SeqCst));
1516     store.gc(None)?;
1517     assert!(!inner_dropped.load(SeqCst));
1518     let r2 = r.clone();
1519     store.gc(None)?;
1520     assert!(!inner_dropped.load(SeqCst));
1521     drop(r);
1522     store.gc(None)?;
1523     assert!(!inner_dropped.load(SeqCst));
1524     drop(r2);
1525     store.gc(None)?;
1526     assert!(inner_dropped.load(SeqCst));
1527 
1528     Ok(())
1529 }
1530 
1531 #[test]
1532 #[cfg_attr(miri, ignore)]
owned_rooted_lots_of_root_creation() -> Result<()>1533 fn owned_rooted_lots_of_root_creation() -> Result<()> {
1534     let mut config = Config::new();
1535     config.wasm_function_references(true);
1536     config.wasm_gc(true);
1537     config.collector(Collector::DeferredReferenceCounting);
1538 
1539     let engine = Engine::new(&config)?;
1540 
1541     let mut store = Store::new(&engine, ());
1542     let inner_dropped = Arc::new(AtomicBool::new(false));
1543     let r = {
1544         let mut scope = RootScope::new(&mut store);
1545         let r = ExternRef::new(&mut scope, SetFlagOnDrop(inner_dropped.clone()))?;
1546         r.to_owned_rooted(&mut scope)?
1547     };
1548     assert!(!inner_dropped.load(SeqCst));
1549     store.gc(None)?;
1550 
1551     for _ in 0..100_000 {
1552         let mut scope = RootScope::new(&mut store);
1553         // Go through a LIFO root then back to an owned root to create
1554         // a distinct root.
1555         let r2 = r.to_rooted(&mut scope);
1556         let r3 = r2.to_owned_rooted(&mut scope);
1557         drop(r3);
1558     }
1559 
1560     store.gc(None)?;
1561     assert!(!inner_dropped.load(SeqCst));
1562 
1563     drop(r);
1564     store.gc(None)?;
1565     assert!(inner_dropped.load(SeqCst));
1566 
1567     Ok(())
1568 }
1569 
1570 #[test]
1571 #[cfg_attr(miri, ignore)]
runtime_table_init_oom() -> Result<()>1572 fn runtime_table_init_oom() -> Result<()> {
1573     let mut config = Config::new();
1574     config.wasm_gc(true);
1575     config.wasm_function_references(true);
1576     config.memory_may_move(false);
1577     config.memory_reservation(64 << 10);
1578     config.memory_reservation_for_growth(0);
1579     let engine = Engine::new(&config)?;
1580     let mut store = Store::new(&engine, ());
1581 
1582     let module = Module::new(
1583         store.engine(),
1584         r#"
1585             (module
1586                 (table 100 arrayref)
1587 
1588                 (type $a (array i31ref))
1589 
1590                 (func (export "run")
1591                     i32.const 0
1592                     i32.const 0
1593                     i32.const 5
1594                     table.init $e)
1595                 (elem $e arrayref
1596                     (array.new_default $a (i32.const 100))
1597                     (array.new_default $a (i32.const 10000))
1598                     (array.new_default $a (i32.const 10000))
1599                     (array.new_default $a (i32.const 10000))
1600                     (array.new_default $a (i32.const 1000000))
1601                 )
1602             )
1603         "#,
1604     )?;
1605 
1606     let instance = Instance::new(&mut store, &module, &[])?;
1607     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1608     func.call(&mut store, ())
1609         .unwrap_err()
1610         .downcast::<GcHeapOutOfMemory<()>>()?;
1611 
1612     Ok(())
1613 }
1614 
1615 #[test]
1616 #[cfg_attr(miri, ignore)]
instantiate_table_init_oom() -> Result<()>1617 fn instantiate_table_init_oom() -> Result<()> {
1618     let mut config = Config::new();
1619     config.wasm_gc(true);
1620     config.wasm_function_references(true);
1621     config.memory_may_move(false);
1622     config.memory_reservation(64 << 10);
1623     config.memory_reservation_for_growth(0);
1624     let engine = Engine::new(&config)?;
1625     let mut store = Store::new(&engine, ());
1626 
1627     let module = Module::new(
1628         store.engine(),
1629         r#"
1630             (module
1631                 (table 100 arrayref)
1632 
1633                 (type $a (array i31ref))
1634 
1635                 (elem (i32.const 0) arrayref
1636                     (array.new_default $a (i32.const 100))
1637                     (array.new_default $a (i32.const 10000))
1638                     (array.new_default $a (i32.const 10000))
1639                     (array.new_default $a (i32.const 10000))
1640                     (array.new_default $a (i32.const 1000000))
1641                 )
1642             )
1643         "#,
1644     )?;
1645 
1646     Instance::new(&mut store, &module, &[])
1647         .unwrap_err()
1648         .downcast::<GcHeapOutOfMemory<()>>()?;
1649 
1650     Ok(())
1651 }
1652 
1653 #[test]
1654 #[cfg_attr(miri, ignore)]
instantiate_table_init_expr_oom() -> Result<()>1655 fn instantiate_table_init_expr_oom() -> Result<()> {
1656     let mut config = Config::new();
1657     config.wasm_gc(true);
1658     config.wasm_function_references(true);
1659     config.memory_may_move(false);
1660     config.memory_reservation(64 << 10);
1661     config.memory_reservation_for_growth(0);
1662     let engine = Engine::new(&config)?;
1663     let mut store = Store::new(&engine, ());
1664 
1665     let module = Module::new(
1666         store.engine(),
1667         r#"
1668             (module
1669                 (type $a (array i31ref))
1670                 (table 100 (ref $a) (array.new_default $a (i32.const 100000)))
1671             )
1672         "#,
1673     )?;
1674 
1675     Instance::new(&mut store, &module, &[])
1676         .unwrap_err()
1677         .downcast::<GcHeapOutOfMemory<()>>()?;
1678 
1679     Ok(())
1680 }
1681 
1682 #[test]
1683 #[cfg_attr(miri, ignore)]
instantiate_global_init_oom() -> Result<()>1684 fn instantiate_global_init_oom() -> Result<()> {
1685     let mut config = Config::new();
1686     config.wasm_gc(true);
1687     config.wasm_function_references(true);
1688     config.memory_may_move(false);
1689     config.memory_reservation(64 << 10);
1690     config.memory_reservation_for_growth(0);
1691     let engine = Engine::new(&config)?;
1692     let mut store = Store::new(&engine, ());
1693 
1694     let module = Module::new(
1695         store.engine(),
1696         r#"
1697             (module
1698                 (table 100 arrayref)
1699                 (type $a (array i31ref))
1700                 (global (ref $a) (array.new_default $a (i32.const 10000000)))
1701             )
1702         "#,
1703     )?;
1704 
1705     Instance::new(&mut store, &module, &[])
1706         .unwrap_err()
1707         .downcast::<GcHeapOutOfMemory<()>>()?;
1708 
1709     Ok(())
1710 }
1711 
1712 #[test]
1713 #[cfg_attr(miri, ignore)]
array_new_elem_oom() -> Result<()>1714 fn array_new_elem_oom() -> Result<()> {
1715     let mut config = Config::new();
1716     config.wasm_gc(true);
1717     config.wasm_function_references(true);
1718     config.memory_may_move(false);
1719     config.memory_reservation(64 << 10);
1720     config.memory_reservation_for_growth(0);
1721     let engine = Engine::new(&config)?;
1722     let mut store = Store::new(&engine, ());
1723 
1724     let module = Module::new(
1725         store.engine(),
1726         r#"
1727             (module
1728                 (type $a (array (mut arrayref)))
1729                 (type $i (array i31ref))
1730 
1731                 (func (export "run")
1732                     i32.const 0
1733                     i32.const 5
1734                     array.new_elem $a $e
1735                     drop)
1736 
1737                 (elem $e arrayref
1738                     (array.new_default $i (i32.const 100))
1739                     (array.new_default $i (i32.const 10000))
1740                     (array.new_default $i (i32.const 10000))
1741                     (array.new_default $i (i32.const 10000))
1742                     (array.new_default $i (i32.const 1000000))
1743                 )
1744             )
1745         "#,
1746     )?;
1747 
1748     let instance = Instance::new(&mut store, &module, &[])?;
1749     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1750     func.call(&mut store, ())
1751         .unwrap_err()
1752         .downcast::<GcHeapOutOfMemory<()>>()?;
1753 
1754     Ok(())
1755 }
1756 
1757 #[test]
1758 #[cfg_attr(miri, ignore)]
array_init_elem_oom() -> Result<()>1759 fn array_init_elem_oom() -> Result<()> {
1760     let mut config = Config::new();
1761     config.wasm_gc(true);
1762     config.wasm_function_references(true);
1763     config.memory_may_move(false);
1764     config.memory_reservation(64 << 10);
1765     config.memory_reservation_for_growth(0);
1766     let engine = Engine::new(&config)?;
1767     let mut store = Store::new(&engine, ());
1768 
1769     let module = Module::new(
1770         store.engine(),
1771         r#"
1772             (module
1773                 (type $a (array (mut arrayref)))
1774                 (type $i (array i31ref))
1775 
1776                 (func (export "run")
1777                     i32.const 5
1778                     array.new_default $a
1779                     i32.const 0
1780                     i32.const 0
1781                     i32.const 5
1782                     array.init_elem $a $e)
1783 
1784                 (elem $e arrayref
1785                     (array.new_default $i (i32.const 100))
1786                     (array.new_default $i (i32.const 10000))
1787                     (array.new_default $i (i32.const 10000))
1788                     (array.new_default $i (i32.const 10000))
1789                     (array.new_default $i (i32.const 1000000))
1790                 )
1791             )
1792         "#,
1793     )?;
1794 
1795     let instance = Instance::new(&mut store, &module, &[])?;
1796     let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
1797     func.call(&mut store, ())
1798         .unwrap_err()
1799         .downcast::<GcHeapOutOfMemory<()>>()?;
1800 
1801     Ok(())
1802 }
1803 
1804 // The result of a `select` instruction with a GC reference type should be
1805 // declared as needing a stack map.
1806 #[test]
1807 #[cfg_attr(miri, ignore)]
select_gc_ref_stack_map() -> Result<()>1808 fn select_gc_ref_stack_map() -> Result<()> {
1809     let _ = env_logger::try_init();
1810 
1811     let mut config = Config::new();
1812     config.wasm_function_references(true);
1813     config.wasm_gc(true);
1814     // Use pooling allocator with a tiny GC heap so that GC is triggered
1815     // frequently and freed memory is reused quickly.
1816     let mut pool = crate::small_pool_config();
1817     pool.max_memory_size(1 << 16); // 64 KiB
1818     config.allocation_strategy(pool);
1819 
1820     let engine = Engine::new(&config)?;
1821     let module = Module::new(
1822         &engine,
1823         r#"
1824             (module
1825                 (type $pair (struct (field (mut i32))))
1826                 (type $arr (array (mut i8)))
1827 
1828                 ;; Allocate many objects to fill the GC heap and trigger
1829                 ;; collection. After GC frees everything, subsequent
1830                 ;; allocations reuse the freed memory.
1831                 (func $force_gc
1832                     (local i32)
1833                     (local.set 0 (i32.const 64))
1834                     (loop $l
1835                         (drop (array.new $arr (i32.const 0) (i32.const 1024)))
1836                         (local.set 0 (i32.sub (local.get 0) (i32.const 1)))
1837                         (br_if $l (local.get 0))
1838                     )
1839                 )
1840 
1841                 (func (export "test") (param $cond i32) (result i32)
1842                     ;; The select result stays on the Wasm operand stack (never
1843                     ;; stored in a local variable). If the new SSA value created
1844                     ;; by select is not declared for stack maps, the GC will
1845                     ;; free it.
1846                     (select (result (ref null $pair))
1847                         (struct.new $pair (i32.const 111))
1848                         (ref.null $pair)
1849                         (local.get $cond)
1850                     )
1851 
1852                     ;; This call is a safepoint. The select result is live
1853                     ;; across it. The called function triggers GC which will
1854                     ;; free the struct if it is incorrectly omitted from the
1855                     ;; safepoint's stack map, and the new allocations would
1856                     ;; overwrite the freed memory.
1857                     (call $force_gc)
1858 
1859                     ;; Use the select result. If it was incorrectly freed, then
1860                     ;; this will have the wrong value.
1861                     (struct.get $pair 0)
1862                 )
1863             )
1864         "#,
1865     )?;
1866 
1867     let mut store = Store::new(&engine, ());
1868     let instance = Instance::new(&mut store, &module, &[])?;
1869     let test = instance.get_typed_func::<(i32,), i32>(&mut store, "test")?;
1870 
1871     // Run multiple times to increase chance of triggering GC at the right
1872     // moment.
1873     for _ in 0..30 {
1874         let result = test.call(&mut store, (1,))?;
1875         assert_eq!(result, 111);
1876     }
1877 
1878     Ok(())
1879 }
1880