1c0bb341dSAlex Crichton #![cfg(not(miri))]
2c0bb341dSAlex Crichton
33862c1f3SAlex Crichton use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
43862c1f3SAlex Crichton use wasmtime::*;
5e32292c9SAlex Crichton use wasmtime_test_macros::wasmtime_test;
63862c1f3SAlex Crichton
73862c1f3SAlex Crichton #[test]
host_always_has_some_stack() -> Result<()>88bc75502SPeter Huene fn host_always_has_some_stack() -> Result<()> {
93862c1f3SAlex Crichton static HITS: AtomicUsize = AtomicUsize::new(0);
10aeaca206SAlex Crichton // assume hosts always have at least 128k of stack
11aeaca206SAlex Crichton const HOST_STACK: usize = 128 * 1024;
123862c1f3SAlex Crichton
1313342b24SAlex Crichton let mut store = if cfg!(target_arch = "x86_64") {
1413342b24SAlex Crichton let mut config = Config::new();
1513342b24SAlex Crichton // Force cranelift-based libcalls to show up by ensuring that platform
1613342b24SAlex Crichton // support is turned off.
1713342b24SAlex Crichton unsafe {
1813342b24SAlex Crichton config.cranelift_flag_set("has_avx", "false");
1913342b24SAlex Crichton config.cranelift_flag_set("has_sse42", "false");
2013342b24SAlex Crichton config.cranelift_flag_set("has_sse41", "false");
2113342b24SAlex Crichton config.cranelift_flag_set("has_ssse3", "false");
2213342b24SAlex Crichton config.cranelift_flag_set("has_sse3", "false");
2313342b24SAlex Crichton }
2413342b24SAlex Crichton Store::new(&Engine::new(&config)?, ())
2513342b24SAlex Crichton } else {
2613342b24SAlex Crichton Store::<()>::default()
2713342b24SAlex Crichton };
283862c1f3SAlex Crichton
293862c1f3SAlex Crichton // Create a module that's infinitely recursive, but calls the host on each
303862c1f3SAlex Crichton // level of wasm stack to always test how much host stack we have left.
3113342b24SAlex Crichton //
3213342b24SAlex Crichton // Each of the function exports of this module calls out to the host in a
3313342b24SAlex Crichton // different way, and each one is tested below to make sure that the way of
3413342b24SAlex Crichton // exiting out to the host is tested thoroughly.
353862c1f3SAlex Crichton let module = Module::new(
3615c68f2cSYury Delendik store.engine(),
373862c1f3SAlex Crichton r#"
383862c1f3SAlex Crichton (module
3913342b24SAlex Crichton (import "" "" (func $host1))
4013342b24SAlex Crichton (import "" "" (func $host2))
4113342b24SAlex Crichton
4213342b24SAlex Crichton ;; exit via wasm-to-native trampoline
4313342b24SAlex Crichton (func $recursive1 (export "f1")
4413342b24SAlex Crichton call $host1
4513342b24SAlex Crichton call $recursive1)
4613342b24SAlex Crichton
4713342b24SAlex Crichton ;; exit via wasm-to-array trampoline
4813342b24SAlex Crichton (func $recursive2 (export "f2")
4913342b24SAlex Crichton call $host2
5013342b24SAlex Crichton call $recursive2)
5113342b24SAlex Crichton
5213342b24SAlex Crichton ;; exit via a wasmtime-based libcall
5313342b24SAlex Crichton (memory 1)
5413342b24SAlex Crichton (func $recursive3 (export "f3")
5513342b24SAlex Crichton (drop (memory.grow (i32.const 0)))
5613342b24SAlex Crichton call $recursive3)
5713342b24SAlex Crichton
5813342b24SAlex Crichton ;; exit via a cranelift-based libcall
5913342b24SAlex Crichton (func $recursive4 (export "f4")
6013342b24SAlex Crichton (drop (call $f32_ceil (f32.const 0)))
6113342b24SAlex Crichton call $recursive4)
6213342b24SAlex Crichton (func $f32_ceil (param f32) (result f32)
6313342b24SAlex Crichton (f32.ceil (local.get 0)))
643862c1f3SAlex Crichton )
653862c1f3SAlex Crichton "#,
663862c1f3SAlex Crichton )?;
6713342b24SAlex Crichton let host1 = Func::wrap(&mut store, test_host_stack);
6813342b24SAlex Crichton let ty = FuncType::new(store.engine(), [], []);
6913342b24SAlex Crichton let host2 = Func::new(&mut store, ty, |_, _, _| {
7013342b24SAlex Crichton test_host_stack();
7113342b24SAlex Crichton Ok(())
7213342b24SAlex Crichton });
7313342b24SAlex Crichton let instance = Instance::new(&mut store, &module, &[host1.into(), host2.into()])?;
7413342b24SAlex Crichton let f1 = instance.get_typed_func::<(), ()>(&mut store, "f1")?;
7513342b24SAlex Crichton let f2 = instance.get_typed_func::<(), ()>(&mut store, "f2")?;
7613342b24SAlex Crichton let f3 = instance.get_typed_func::<(), ()>(&mut store, "f3")?;
7713342b24SAlex Crichton let f4 = instance.get_typed_func::<(), ()>(&mut store, "f4")?;
783862c1f3SAlex Crichton
793862c1f3SAlex Crichton // Make sure that our function traps and the trap says that the call stack
803862c1f3SAlex Crichton // has been exhausted.
8113342b24SAlex Crichton let hits1 = HITS.load(SeqCst);
8213342b24SAlex Crichton let trap = f1.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
832afaac51SAlex Crichton assert_eq!(trap, Trap::StackOverflow);
8413342b24SAlex Crichton let hits2 = HITS.load(SeqCst);
8513342b24SAlex Crichton let trap = f2.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
8613342b24SAlex Crichton assert_eq!(trap, Trap::StackOverflow);
8713342b24SAlex Crichton let hits3 = HITS.load(SeqCst);
8813342b24SAlex Crichton let trap = f3.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
8913342b24SAlex Crichton assert_eq!(trap, Trap::StackOverflow);
9013342b24SAlex Crichton let hits4 = HITS.load(SeqCst);
9113342b24SAlex Crichton let trap = f4.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
9213342b24SAlex Crichton assert_eq!(trap, Trap::StackOverflow);
9313342b24SAlex Crichton let hits5 = HITS.load(SeqCst);
943862c1f3SAlex Crichton
953862c1f3SAlex Crichton // Additionally, however, and this is the crucial test, make sure that the
963862c1f3SAlex Crichton // host function actually completed. If HITS is 1 then we entered but didn't
973862c1f3SAlex Crichton // exit meaning we segfaulted while executing the host, yet still tried to
98192f2fcdSAlex Crichton // recover from it with a jump.
9913342b24SAlex Crichton assert_eq!(hits1, 0);
10013342b24SAlex Crichton assert_eq!(hits2, 0);
10113342b24SAlex Crichton assert_eq!(hits3, 0);
10213342b24SAlex Crichton assert_eq!(hits4, 0);
10313342b24SAlex Crichton assert_eq!(hits5, 0);
1043862c1f3SAlex Crichton
1053862c1f3SAlex Crichton return Ok(());
1063862c1f3SAlex Crichton
1073862c1f3SAlex Crichton fn test_host_stack() {
1083862c1f3SAlex Crichton HITS.fetch_add(1, SeqCst);
1093862c1f3SAlex Crichton assert!(consume_some_stack(0, HOST_STACK) > 0);
1103862c1f3SAlex Crichton HITS.fetch_sub(1, SeqCst);
1113862c1f3SAlex Crichton }
1123862c1f3SAlex Crichton
1133862c1f3SAlex Crichton #[inline(never)]
1143862c1f3SAlex Crichton fn consume_some_stack(ptr: usize, stack: usize) -> usize {
1153862c1f3SAlex Crichton if stack == 0 {
1163862c1f3SAlex Crichton return ptr;
1173862c1f3SAlex Crichton }
1183862c1f3SAlex Crichton let mut space = [0u8; 1024];
1193862c1f3SAlex Crichton consume_some_stack(space.as_mut_ptr() as usize, stack.saturating_sub(1024))
1203862c1f3SAlex Crichton }
1213862c1f3SAlex Crichton }
1228bc75502SPeter Huene
123e32292c9SAlex Crichton #[wasmtime_test]
big_stack_works_ok(config: &mut Config) -> Result<()>124e32292c9SAlex Crichton fn big_stack_works_ok(config: &mut Config) -> Result<()> {
12547bc186dSAlex Crichton // This test takes 1m+ in ASAN and isn't too useful, so prune it.
12647bc186dSAlex Crichton if cfg!(asan) {
12747bc186dSAlex Crichton return Ok(());
12847bc186dSAlex Crichton }
12947bc186dSAlex Crichton
1308bc75502SPeter Huene const N: usize = 10000;
1318bc75502SPeter Huene
1328bc75502SPeter Huene // Build a module with a function that uses a very large amount of stack space,
1338bc75502SPeter Huene // modeled here by calling an i64-returning-function many times followed by
1348bc75502SPeter Huene // adding them all into one i64.
1358bc75502SPeter Huene //
1368bc75502SPeter Huene // This should exercise the ability to consume multi-page stacks and
1378bc75502SPeter Huene // only touch a few internals of it at a time.
1388bc75502SPeter Huene let mut s = String::new();
1398bc75502SPeter Huene s.push_str("(module\n");
1408bc75502SPeter Huene s.push_str("(func (export \"\") (result i64)\n");
1418bc75502SPeter Huene s.push_str("i64.const 0\n");
1428bc75502SPeter Huene for _ in 0..N {
1438bc75502SPeter Huene s.push_str("call $get\n");
1448bc75502SPeter Huene }
1458bc75502SPeter Huene for _ in 0..N {
1468bc75502SPeter Huene s.push_str("i64.add\n");
1478bc75502SPeter Huene }
1488bc75502SPeter Huene s.push_str(")\n");
1498bc75502SPeter Huene s.push_str("(func $get (result i64) i64.const 0)\n");
1508bc75502SPeter Huene s.push_str(")\n");
1518bc75502SPeter Huene
152e32292c9SAlex Crichton // Disable cranelift optimizations to ensure that this test doesn't take too
153e32292c9SAlex Crichton // long in debug mode due to the large size of its code.
154e32292c9SAlex Crichton config.cranelift_opt_level(OptLevel::None);
15545f8b2a7SAlex Crichton config.cranelift_regalloc_algorithm(RegallocAlgorithm::SinglePass);
156e32292c9SAlex Crichton let engine = Engine::new(config)?;
157e32292c9SAlex Crichton
158e32292c9SAlex Crichton let mut store = Store::new(&engine, ());
1598bc75502SPeter Huene let module = Module::new(store.engine(), &s)?;
1608bc75502SPeter Huene let instance = Instance::new(&mut store, &module, &[])?;
1618bc75502SPeter Huene let func = instance.get_typed_func::<(), i64>(&mut store, "")?;
1628bc75502SPeter Huene assert_eq!(func.call(&mut store, ())?, 0);
1638bc75502SPeter Huene Ok(())
1648bc75502SPeter Huene }
165*2283e84fSAlex Crichton
166*2283e84fSAlex Crichton #[test]
infinite_wasm_stack_does_not_panic() -> Result<()>167*2283e84fSAlex Crichton fn infinite_wasm_stack_does_not_panic() -> Result<()> {
168*2283e84fSAlex Crichton let mut config = Config::new();
169*2283e84fSAlex Crichton config.max_wasm_stack(usize::MAX);
170*2283e84fSAlex Crichton config.async_stack_size(usize::MAX);
171*2283e84fSAlex Crichton let engine = Engine::new(&config)?;
172*2283e84fSAlex Crichton // If the store can't get allocated that's probably because we're using
173*2283e84fSAlex Crichton // pulley and can't allocate a usize::MAX stack, ignore that failure.
174*2283e84fSAlex Crichton // This'll still run on cranelift-native platforms.
175*2283e84fSAlex Crichton let Ok(mut store) = Store::try_new(&engine, ()) else {
176*2283e84fSAlex Crichton return Ok(());
177*2283e84fSAlex Crichton };
178*2283e84fSAlex Crichton let module = Module::new(store.engine(), r#"(module (func (export "f")))"#)?;
179*2283e84fSAlex Crichton let instance = Instance::new(&mut store, &module, &[])?;
180*2283e84fSAlex Crichton let func = instance.get_typed_func::<(), ()>(&mut store, "f")?;
181*2283e84fSAlex Crichton func.call(&mut store, ())?;
182*2283e84fSAlex Crichton Ok(())
183*2283e84fSAlex Crichton }
184