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