From 7c6bd2e890dc9a15bec4e1420a66b83bbcbff1b1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 1 May 2023 09:05:23 -0700 Subject: [PATCH 1/2] Run some tests in MIRI on CI This commit is an implementation of getting at least chunks of Wasmtime to run in MIRI on CI. The full test suite is not possible to run in MIRI because MIRI cannot run Cranelift-produced code at runtime (aka it doesn't support JITs). Running MIRI, however, is still quite valuable if we can manage it because it would have trivially detected GHSA-ch89-5g45-qwc7, our most recent security advisory. The goal of this PR is to select a subset of the test suite to execute in CI under MIRI and boost our confidence in the copious amount of `unsafe` code in Wasmtime's runtime. Under MIRI's default settings, which is to use the [Stacked Borrows][stacked] model, much of the code in `Instance` and `VMContext` is considered invalid. Under the optional [Tree Borrows][tree] model, however, this same code is accepted. After some [extremely helpful discussion][discuss] on the Rust Zulip my current conclusion is that what we're doing is not fundamentally un-sound but we need to model it in a different way. This PR, however, uses the Tree Borrows model for MIRI to get something onto CI sooner rather than later, and I hope to follow this up with something that passed Stacked Borrows. Additionally that'll hopefully make this diff smaller and easier to digest. Given all that, the end result of this PR is to get 131 separate unit tests executing on CI. These unit tests largely exercise the embedding API where wasm function compilation is not involved. Some tests compile wasm functions but don't run them, but compiling wasm through Cranelift in MIRI is so slow that it doesn't seem worth it at this time. This does mean that there's a pretty big hole in MIRI's test coverage, but that's to be expected as we're a JIT compiler after all. To get tests working in MIRI this PR uses a number of strategies: * When platform-specific code is involved there's now `#[cfg(miri)]` for MIRI's version. For example there's a custom-built "mmap" just for MIRI now. Many of these are simple noops, some are `unimplemented!()` as they shouldn't be reached, and some are slightly nontrivial implementations such as mmaps and trap handling (for native-to-native function calls). * Many test modules are simply excluded via `#![cfg(not(miri))]` at the top of the file. This excludes the entire module's worth of tests from MIRI. Other modules have `#[cfg_attr(miri, ignore)]` annotations to ignore tests by default on MIRI. The latter form is used in modules where some tests work and some don't. This means that all future test additions will need to be effectively annotated whether they work in MIRI or not. My hope though is that there's enough precedent in the test suite of what to do to not cause too much burden. * A number of locations are fixed with respect to MIRI's analysis. For example `ComponentInstance`, the component equivalent of `wasmtime_runtime::Instance`, was actually left out from the fix for the CVE by accident. MIRI dutifully highlighted the issues here and I've fixed them locally. Some locations fixed for MIRI are changed to something that looks similar but is subtly different. For example retaining items in a `Store` is now done with a Wasmtime-specific `StoreBox` type. This is because, with MIRI's analyses, moving a `Box` invalidates all pointers derived from this `Box`. We don't want these semantics, so we effectively have a custom `Box` to suit our needs in this regard. * Some default configuration is different under MIRI. For example most linear memories are dynamic with no guards and no space reserved for growth. Settings such as parallel compilation are disabled. These are applied to make MIRI "work by default" in more places ideally. Some tests which perform N iterations of something perform fewer iterations on MIRI to not take quite so long. This PR is not intended to be a one-and-done-we-never-look-at-it-again kind of thing. Instead this is intended to lay the groundwork to continuously run MIRI in CI to catch any soundness issues. This feels, to me, overdue given the amount of `unsafe` code inside of Wasmtime. My hope is that over time we can figure out how to run Wasm in MIRI but that may take quite some time. Regardless this will be adding nontrivial maintenance work to contributors to Wasmtime. MIRI will be run on CI for merges, MIRI will have test failures when everything else passes, MIRI's errors will need to be deciphered by those who have probably never run MIRI before, things like that. Despite all this to me it seems worth the cost at this time. Just getting this running caught two possible soundness bugs in the component implementation that could have had a real-world impact in the future! [stacked]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md [tree]: https://perso.crans.org/vanille/treebor/ [discuss]: https://rust-lang.zulipchat.com/#narrow/stream/269128-miri/topic/Tree.20vs.20Stacked.20Borrows.20.26.20a.20debugging.20question --- .github/workflows/main.yml | 46 +++++++++ build.rs | 2 + cranelift/codegen/src/isa/x64/mod.rs | 2 +- crates/environ/src/tunables.rs | 56 ++++++----- crates/jit-icache-coherence/src/lib.rs | 3 + crates/jit-icache-coherence/src/miri.rs | 10 ++ crates/jit/src/unwind.rs | 3 + crates/jit/src/unwind/miri.rs | 15 +++ crates/runtime/src/component.rs | 98 ++++++++++++++----- crates/runtime/src/cow.rs | 32 +++--- crates/runtime/src/debug_builtins.rs | 3 + .../runtime/src/instance/allocator/pooling.rs | 70 +++++++------ .../allocator/pooling/index_allocator.rs | 5 +- .../src/instance/allocator/pooling/unix.rs | 16 +-- crates/runtime/src/lib.rs | 2 + crates/runtime/src/mmap.rs | 3 + crates/runtime/src/mmap/miri.rs | 93 ++++++++++++++++++ crates/runtime/src/parking_spot.rs | 3 +- crates/runtime/src/store_box.rs | 35 +++++++ crates/runtime/src/traphandlers.rs | 58 +++++++++-- crates/runtime/src/traphandlers/unix.rs | 7 ++ .../src/vmcontext/vm_host_func_context.rs | 21 ++-- crates/wasmtime/src/config.rs | 2 +- crates/wasmtime/src/engine.rs | 1 + crates/wasmtime/src/engine/serialization.rs | 1 + crates/wasmtime/src/func.rs | 25 +++-- crates/wasmtime/src/memory.rs | 2 +- crates/wasmtime/src/module/registry.rs | 1 + crates/wasmtime/src/store.rs | 6 +- crates/wasmtime/src/store/func_refs.rs | 8 +- crates/wasmtime/src/trampoline/func.rs | 6 +- crates/wasmtime/src/trampoline/global.rs | 49 +++++----- src/commands/compile.rs | 2 +- tests/all/async_functions.rs | 2 + tests/all/call_hook.rs | 2 + tests/all/cli_tests.rs | 2 + tests/all/component_model.rs | 1 + tests/all/component_model/aot.rs | 1 + tests/all/component_model/async.rs | 2 + tests/all/component_model/bindgen.rs | 2 + tests/all/component_model/dynamic.rs | 2 + tests/all/component_model/func.rs | 2 + tests/all/component_model/import.rs | 2 + tests/all/component_model/macros.rs | 2 + tests/all/component_model/nested.rs | 2 + tests/all/component_model/post_return.rs | 2 + tests/all/component_model/strings.rs | 2 + tests/all/custom_signal_handler.rs | 1 + tests/all/epoch_interruption.rs | 2 + tests/all/externals.rs | 1 + tests/all/fuel.rs | 4 + tests/all/func.rs | 27 +++++ tests/all/funcref.rs | 3 + tests/all/gc.rs | 9 ++ tests/all/host_funcs.rs | 8 ++ tests/all/iloop.rs | 2 + tests/all/import_calling_export.rs | 2 + tests/all/import_indexes.rs | 2 + tests/all/instance.rs | 2 + tests/all/invoke_func_via_table.rs | 2 + tests/all/limits.rs | 10 ++ tests/all/linker.rs | 8 ++ tests/all/main.rs | 2 + tests/all/memory.rs | 5 + tests/all/memory_creator.rs | 2 +- tests/all/module.rs | 4 +- tests/all/module_serialize.rs | 4 + tests/all/name.rs | 2 + tests/all/pooling_allocator.rs | 50 ++++++++-- tests/all/relocs.rs | 2 + tests/all/stack_overflow.rs | 2 + tests/all/table.rs | 1 + tests/all/threads.rs | 4 + tests/all/traps.rs | 2 + tests/all/wait_notify.rs | 2 + tests/all/wasi_testsuite.rs | 2 + tests/host_segfault.rs | 3 + tests/rlimited-memory.rs | 1 + 78 files changed, 706 insertions(+), 177 deletions(-) create mode 100644 crates/jit-icache-coherence/src/miri.rs create mode 100644 crates/jit/src/unwind/miri.rs create mode 100644 crates/runtime/src/mmap/miri.rs create mode 100644 crates/runtime/src/store_box.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f2b6c7e8fde6..4e7b94a77757 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -588,6 +588,51 @@ jobs: env: GH_TOKEN: ${{ github.token }} + # Run a subset of tests under MIRI on CI to help check the `unsafe` code in + # Wasmtime to make sure it's at least not obviously incorrect for basic usage. + # Note that this doesn't run the full test suite since MIRI can't actually run + # WebAssembly itself at this time (aka it doesn't support a JIT). There are a + # number of annotations throughout the code which gates some tests on MIRI not + # being run. + # + # Note that `cargo nextest` is used here additionally to get parallel test + # execution by default to help cut down on the time in CI. + miri: + needs: determine + if: needs.determine.outputs.run-full + name: Miri + runs-on: ubuntu-latest + env: + CARGO_NEXTEST_VERSION: 0.9.51 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: ./.github/actions/install-rust + with: + toolchain: nightly-2023-03-20 + - run: rustup component add rust-src miri + - uses: actions/cache@v3 + with: + path: ${{ runner.tool_cache }}/cargo-nextest + key: cargo-nextest-bin-${{ env.CARGO_NEXTEST_VERSION }} + - run: echo "${{ runner.tool_cache }}/cargo-nextest/bin" >> $GITHUB_PATH + - run: cargo install --root ${{ runner.tool_cache }}/cargo-nextest --version ${{ env.CARGO_NEXTEST_VERSION }} cargo-nextest + - run: | + cargo miri nextest run -j4 --no-fail-fast \ + -p wasmtime \ + -p wasmtime-cli \ + -p wasmtime-runtime \ + -p wasmtime-environ + env: + MIRIFLAGS: -Zmiri-tree-borrows + + # common logic to cancel the entire run if this job fails + - run: gh run cancel ${{ github.run_id }} + if: failure() && github.event_name != 'pull_request' + env: + GH_TOKEN: ${{ github.token }} + # Perform release builds of `wasmtime` and `libwasmtime.so`. Builds a variety # of platforms and architectures and then uploads the release artifacts to # this workflow run's list of artifacts. @@ -694,6 +739,7 @@ jobs: - meta_deterministic_check - verify-publish - determine + - miri if: always() steps: - name: Dump needs context diff --git a/build.rs b/build.rs index 564133438413..ab728b739ab7 100644 --- a/build.rs +++ b/build.rs @@ -155,6 +155,8 @@ fn write_testsuite_tests( // Ignore when using QEMU for running tests (limited memory). if ignore(testsuite, &testname, strategy) { writeln!(out, "#[ignore]")?; + } else { + writeln!(out, "#[cfg_attr(miri, ignore)]")?; } writeln!( diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index 77c55fd73125..18badd6cb0ef 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -214,7 +214,7 @@ fn isa_constructor( // Check for compatibility between flags and ISA level // requested. In particular, SIMD support requires SSE4.2. - if shared_flags.enable_simd() { + if !cfg!(miri) && shared_flags.enable_simd() { if !isa_flags.has_sse3() || !isa_flags.has_ssse3() || !isa_flags.has_sse41() { return Err(CodegenError::Unsupported( "SIMD support requires SSE3, SSSE3, and SSE4.1 on x86_64.".into(), diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index 4b37cd08b935..97a9d0802ff6 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -53,23 +53,26 @@ pub struct Tunables { impl Default for Tunables { fn default() -> Self { - let (static_memory_bound, static_memory_offset_guard_size) = - if cfg!(target_pointer_width = "64") { - // 64-bit has tons of address space to static memories can have 4gb - // address space reservations liberally by default, allowing us to - // help eliminate bounds checks. - // - // Coupled with a 2 GiB address space guard it lets us translate - // wasm offsets into x86 offsets as aggressively as we can. - (0x1_0000, 0x8000_0000) - } else if cfg!(target_pointer_width = "32") { - // For 32-bit we scale way down to 10MB of reserved memory. This - // impacts performance severely but allows us to have more than a - // few instances running around. - ((10 * (1 << 20)) / crate::WASM_PAGE_SIZE as u64, 0x1_0000) - } else { - panic!("unsupported target_pointer_width"); - }; + let (static_memory_bound, static_memory_offset_guard_size) = if cfg!(miri) { + // No virtual memory tricks are available on miri so make these + // limits quite conservative. + ((1 << 20) / crate::WASM_PAGE_SIZE as u64, 0) + } else if cfg!(target_pointer_width = "64") { + // 64-bit has tons of address space to static memories can have 4gb + // address space reservations liberally by default, allowing us to + // help eliminate bounds checks. + // + // Coupled with a 2 GiB address space guard it lets us translate + // wasm offsets into x86 offsets as aggressively as we can. + (0x1_0000, 0x8000_0000) + } else if cfg!(target_pointer_width = "32") { + // For 32-bit we scale way down to 10MB of reserved memory. This + // impacts performance severely but allows us to have more than a + // few instances running around. + ((10 * (1 << 20)) / crate::WASM_PAGE_SIZE as u64, 0x1_0000) + } else { + panic!("unsupported target_pointer_width"); + }; Self { static_memory_bound, static_memory_offset_guard_size, @@ -78,14 +81,21 @@ impl Default for Tunables { // // Allocate a small guard to optimize common cases but without // wasting too much memory. - dynamic_memory_offset_guard_size: 0x1_0000, + dynamic_memory_offset_guard_size: if cfg!(miri) { 0 } else { 0x1_0000 }, // We've got lots of address space on 64-bit so use a larger - // grow-into-this area, but on 32-bit we aren't as lucky. - #[cfg(target_pointer_width = "64")] - dynamic_memory_growth_reserve: 2 << 30, // 2GB - #[cfg(target_pointer_width = "32")] - dynamic_memory_growth_reserve: 1 << 20, // 1MB + // grow-into-this area, but on 32-bit we aren't as lucky. Miri is + // not exactly fast so reduce memory consumption instead of trying + // to avoid memory movement. + dynamic_memory_growth_reserve: if cfg!(miri) { + 0 + } else if cfg!(target_pointer_width = "64") { + 2 << 30 // 2GB + } else if cfg!(target_pointer_width = "32") { + 1 << 20 // 1MB + } else { + panic!("unsupported target_pointer_width"); + }, generate_native_debuginfo: false, parse_wasm_debuginfo: true, diff --git a/crates/jit-icache-coherence/src/lib.rs b/crates/jit-icache-coherence/src/lib.rs index e47e53971489..8964372b729e 100644 --- a/crates/jit-icache-coherence/src/lib.rs +++ b/crates/jit-icache-coherence/src/lib.rs @@ -74,6 +74,9 @@ cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { mod win; use win as imp; + } else if #[cfg(miri)] { + mod miri; + use crate::miri as imp; } else { mod libc; use crate::libc as imp; diff --git a/crates/jit-icache-coherence/src/miri.rs b/crates/jit-icache-coherence/src/miri.rs new file mode 100644 index 000000000000..1ff641046a87 --- /dev/null +++ b/crates/jit-icache-coherence/src/miri.rs @@ -0,0 +1,10 @@ +use std::ffi::c_void; +use std::io::Result; + +pub(crate) fn pipeline_flush_mt() -> Result<()> { + Ok(()) +} + +pub(crate) fn clear_cache(_ptr: *const c_void, _len: usize) -> Result<()> { + Ok(()) +} diff --git a/crates/jit/src/unwind.rs b/crates/jit/src/unwind.rs index 72284872ce1d..9ce8660f36c9 100644 --- a/crates/jit/src/unwind.rs +++ b/crates/jit/src/unwind.rs @@ -2,6 +2,9 @@ cfg_if::cfg_if! { if #[cfg(all(windows, any(target_arch = "x86_64", target_arch = "aarch64")))] { mod winx64; pub use self::winx64::*; + } else if #[cfg(miri)] { + mod miri; + pub use self::miri::*; } else if #[cfg(unix)] { mod systemv; pub use self::systemv::*; diff --git a/crates/jit/src/unwind/miri.rs b/crates/jit/src/unwind/miri.rs new file mode 100644 index 000000000000..8e7113f7be40 --- /dev/null +++ b/crates/jit/src/unwind/miri.rs @@ -0,0 +1,15 @@ +use anyhow::Result; + +pub struct UnwindRegistration {} + +impl UnwindRegistration { + pub const SECTION_NAME: &str = ".eh_frame"; + + pub unsafe fn new( + _base_address: *const u8, + _unwind_info: *const u8, + _unwind_len: usize, + ) -> Result { + Ok(UnwindRegistration {}) + } +} diff --git a/crates/runtime/src/component.rs b/crates/runtime/src/component.rs index 9a7650f5b7cc..af0a1968a32e 100644 --- a/crates/runtime/src/component.rs +++ b/crates/runtime/src/component.rs @@ -40,11 +40,37 @@ pub struct ComponentInstance { /// Size and offset information for the trailing `VMComponentContext`. offsets: VMComponentOffsets, + /// A self-referential pointer to the `ComponentInstance` itself. + /// + /// The reason for this field's existence is a bit subtle because if you + /// have a pointer to `ComponentInstance` then why have this field which has + /// the same information? The subtlety here is due to the fact that this is + /// here to handle provenance and aliasing issues with optimizations in LLVM + /// and Rustc. Put another way, this is here to make MIRI happy. That's an + /// oversimplification, though, since this is actually required for + /// correctness to communicate the actual intent to LLVM's optimizations and + /// such. + /// + /// This field is chiefly used during `ComponentInstance::vmctx`. If you + /// think of pointers as a pair of address and provenance, this type stores + /// the provenance of the entire allocation. That's technically all we need + /// here, just the provenance, but that can only be done with storage of a + /// pointer hence the storage here. + /// + /// Finally note that there's a small wrapper around this to add `Send` and + /// `Sync` bounds, but serves no other purpose. + self_reference: RawComponentInstance, + /// A zero-sized field which represents the end of the struct for the actual /// `VMComponentContext` to be allocated behind. vmctx: VMComponentContext, } +struct RawComponentInstance(NonNull); + +unsafe impl Send for RawComponentInstance {} +unsafe impl Sync for RawComponentInstance {} + /// Type signature for host-defined trampolines that are called from /// WebAssembly. /// @@ -109,8 +135,7 @@ pub struct VMLowering { #[repr(C)] // Set an appropriate alignment for this structure where the most-aligned value // internally right now is a pointer. -#[cfg_attr(target_pointer_width = "32", repr(align(4)))] -#[cfg_attr(target_pointer_width = "64", repr(align(8)))] +#[repr(align(16))] pub struct VMComponentContext { /// For more information about this see the equivalent field in `VMContext` _marker: marker::PhantomPinned, @@ -138,7 +163,7 @@ impl ComponentInstance { /// the shape of the component being instantiated and `store` is a pointer /// back to the Wasmtime store for host functions to have access to. unsafe fn new_at( - ptr: *mut ComponentInstance, + ptr: NonNull, alloc_size: usize, offsets: VMComponentOffsets, store: *mut dyn Store, @@ -146,23 +171,37 @@ impl ComponentInstance { assert!(alloc_size >= Self::alloc_layout(&offsets).size()); ptr::write( - ptr, + ptr.as_ptr(), ComponentInstance { offsets, + self_reference: RawComponentInstance(ptr), vmctx: VMComponentContext { _marker: marker::PhantomPinned, }, }, ); - (*ptr).initialize_vmctx(store); + (*ptr.as_ptr()).initialize_vmctx(store); } fn vmctx(&self) -> *mut VMComponentContext { - &self.vmctx as *const VMComponentContext as *mut VMComponentContext + let self_ref = self.self_reference.0.as_ptr(); + unsafe { + self_ref + .cast::() + .add(mem::size_of::()) + .cast() + } } - unsafe fn vmctx_plus_offset(&self, offset: u32) -> *mut T { + unsafe fn vmctx_plus_offset(&self, offset: u32) -> *const T { + self.vmctx() + .cast::() + .add(usize::try_from(offset).unwrap()) + .cast() + } + + unsafe fn vmctx_plus_offset_mut(&mut self, offset: u32) -> *mut T { self.vmctx() .cast::() .add(usize::try_from(offset).unwrap()) @@ -172,7 +211,12 @@ impl ComponentInstance { /// Returns a pointer to the "may leave" flag for this instance specified /// for canonical lowering and lifting operations. pub fn instance_flags(&self, instance: RuntimeComponentInstanceIndex) -> InstanceFlags { - unsafe { InstanceFlags(self.vmctx_plus_offset(self.offsets.instance_flags(instance))) } + unsafe { + InstanceFlags( + self.vmctx_plus_offset::(self.offsets.instance_flags(instance)) + .cast_mut(), + ) + } } /// Returns the store that this component was created with. @@ -264,7 +308,7 @@ impl ComponentInstance { != INVALID_PTR ); debug_assert!((*ret).vmctx as usize != INVALID_PTR); - NonNull::new(ret).unwrap() + NonNull::new(ret.cast_mut()).unwrap() } /// Stores the runtime memory pointer at the index specified. @@ -278,7 +322,7 @@ impl ComponentInstance { pub fn set_runtime_memory(&mut self, idx: RuntimeMemoryIndex, ptr: *mut VMMemoryDefinition) { unsafe { debug_assert!(!ptr.is_null()); - let storage = self.vmctx_plus_offset(self.offsets.runtime_memory(idx)); + let storage = self.vmctx_plus_offset_mut(self.offsets.runtime_memory(idx)); debug_assert!(*storage as usize == INVALID_PTR); *storage = ptr; } @@ -287,7 +331,7 @@ impl ComponentInstance { /// Same as `set_runtime_memory` but for realloc function pointers. pub fn set_runtime_realloc(&mut self, idx: RuntimeReallocIndex, ptr: NonNull) { unsafe { - let storage = self.vmctx_plus_offset(self.offsets.runtime_realloc(idx)); + let storage = self.vmctx_plus_offset_mut(self.offsets.runtime_realloc(idx)); debug_assert!(*storage as usize == INVALID_PTR); *storage = ptr.as_ptr(); } @@ -300,7 +344,7 @@ impl ComponentInstance { ptr: NonNull, ) { unsafe { - let storage = self.vmctx_plus_offset(self.offsets.runtime_post_return(idx)); + let storage = self.vmctx_plus_offset_mut(self.offsets.runtime_post_return(idx)); debug_assert!(*storage as usize == INVALID_PTR); *storage = ptr.as_ptr(); } @@ -331,7 +375,7 @@ impl ComponentInstance { debug_assert!( *self.vmctx_plus_offset::(self.offsets.lowering_data(idx)) == INVALID_PTR ); - *self.vmctx_plus_offset(self.offsets.lowering(idx)) = lowering; + *self.vmctx_plus_offset_mut(self.offsets.lowering(idx)) = lowering; self.set_func_ref( self.offsets.lowering_func_ref(idx), wasm_call, @@ -392,7 +436,7 @@ impl ComponentInstance { ) { debug_assert!(*self.vmctx_plus_offset::(offset) == INVALID_PTR); let vmctx = VMOpaqueContext::from_vmcomponent(self.vmctx()); - *self.vmctx_plus_offset(offset) = VMFuncRef { + *self.vmctx_plus_offset_mut(offset) = VMFuncRef { wasm_call: Some(wasm_call), native_call, array_call, @@ -402,11 +446,11 @@ impl ComponentInstance { } unsafe fn initialize_vmctx(&mut self, store: *mut dyn Store) { - *self.vmctx_plus_offset(self.offsets.magic()) = VMCOMPONENT_MAGIC; - *self.vmctx_plus_offset(self.offsets.transcode_libcalls()) = + *self.vmctx_plus_offset_mut(self.offsets.magic()) = VMCOMPONENT_MAGIC; + *self.vmctx_plus_offset_mut(self.offsets.transcode_libcalls()) = &transcode::VMBuiltinTranscodeArray::INIT; - *self.vmctx_plus_offset(self.offsets.store()) = store; - *self.vmctx_plus_offset(self.offsets.limits()) = (*store).vmruntime_limits(); + *self.vmctx_plus_offset_mut(self.offsets.store()) = store; + *self.vmctx_plus_offset_mut(self.offsets.limits()) = (*store).vmruntime_limits(); for i in 0..self.offsets.num_runtime_component_instances { let i = RuntimeComponentInstanceIndex::from_u32(i); @@ -423,36 +467,36 @@ impl ComponentInstance { for i in 0..self.offsets.num_lowerings { let i = LoweredIndex::from_u32(i); let offset = self.offsets.lowering_callee(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; let offset = self.offsets.lowering_data(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; let offset = self.offsets.lowering_func_ref(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } for i in 0..self.offsets.num_always_trap { let i = RuntimeAlwaysTrapIndex::from_u32(i); let offset = self.offsets.always_trap_func_ref(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } for i in 0..self.offsets.num_transcoders { let i = RuntimeTranscoderIndex::from_u32(i); let offset = self.offsets.transcoder_func_ref(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } for i in 0..self.offsets.num_runtime_memories { let i = RuntimeMemoryIndex::from_u32(i); let offset = self.offsets.runtime_memory(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } for i in 0..self.offsets.num_runtime_reallocs { let i = RuntimeReallocIndex::from_u32(i); let offset = self.offsets.runtime_realloc(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } for i in 0..self.offsets.num_runtime_post_returns { let i = RuntimePostReturnIndex::from_u32(i); let offset = self.offsets.runtime_post_return(i); - *self.vmctx_plus_offset(offset) = INVALID_PTR; + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } } } @@ -503,7 +547,7 @@ impl OwnedComponentInstance { let ptr = alloc::alloc_zeroed(layout) as *mut ComponentInstance; let ptr = ptr::NonNull::new(ptr).unwrap(); - ComponentInstance::new_at(ptr.as_ptr(), layout.size(), offsets, store); + ComponentInstance::new_at(ptr, layout.size(), offsets, store); OwnedComponentInstance { ptr } } diff --git a/crates/runtime/src/cow.rs b/crates/runtime/src/cow.rs index 6bced64b69fa..20fea79343f1 100644 --- a/crates/runtime/src/cow.rs +++ b/crates/runtime/src/cow.rs @@ -1,7 +1,7 @@ //! Copy-on-write initialization support: creation of backing images for //! modules, and logic to support mapping these backing images into memory. -#![cfg_attr(not(unix), allow(unused_imports, unused_variables))] +#![cfg_attr(any(not(unix), miri), allow(unused_imports, unused_variables))] use crate::MmapVec; use anyhow::Result; @@ -62,14 +62,14 @@ pub struct MemoryImage { #[derive(Debug)] enum FdSource { - #[cfg(unix)] + #[cfg(all(unix, not(miri)))] Mmap(Arc), - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", not(miri)))] Memfd(memfd::Memfd), } impl FdSource { - #[cfg(unix)] + #[cfg(all(unix, not(miri)))] fn as_file(&self) -> &File { match self { FdSource::Mmap(ref file) => file, @@ -82,7 +82,7 @@ impl FdSource { impl PartialEq for FdSource { fn eq(&self, other: &FdSource) -> bool { cfg_if::cfg_if! { - if #[cfg(unix)] { + if #[cfg(all(unix, not(miri)))] { use rustix::fd::AsRawFd; self.as_file().as_raw_fd() == other.as_file().as_raw_fd() } else { @@ -123,7 +123,7 @@ impl MemoryImage { // files, but for now this is still a Linux-specific region of Wasmtime. // Some work will be needed to get this file compiling for macOS and // Windows. - #[cfg(not(windows))] + #[cfg(not(any(windows, miri)))] if let Some(mmap) = mmap { let start = mmap.as_ptr() as usize; let end = start + mmap.len(); @@ -150,7 +150,7 @@ impl MemoryImage { // may be used to place the data in a form that's amenable to an mmap. cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(all(target_os = "linux", not(miri)))] { // On Linux `memfd_create` is used to create an anonymous // in-memory file to represent the heap image. This anonymous // file is then used as the basis for further mmaps. @@ -204,7 +204,7 @@ impl MemoryImage { unsafe fn map_at(&self, base: usize) -> Result<()> { cfg_if::cfg_if! { - if #[cfg(unix)] { + if #[cfg(all(unix, not(miri)))] { let ptr = rustix::mm::mmap( (base + self.linear_memory_offset) as *mut c_void, self.len, @@ -239,7 +239,7 @@ impl MemoryImage { } } -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", not(miri)))] fn create_memfd() -> Result> { use std::io::ErrorKind; @@ -592,7 +592,7 @@ impl MemoryImageSlot { #[allow(dead_code)] // ignore warnings as this is only used in some cfgs unsafe fn reset_all_memory_contents(&mut self, keep_resident: usize) -> Result<()> { - if !cfg!(target_os = "linux") { + if !cfg!(target_os = "linux") || cfg!(miri) { // If we're not on Linux then there's no generic platform way to // reset memory back to its original state, so instead reset memory // back to entirely zeros with an anonymous backing. @@ -730,7 +730,11 @@ impl MemoryImageSlot { unsafe { cfg_if::cfg_if! { - if #[cfg(unix)] { + if #[cfg(miri)] { + if readwrite { + std::ptr::write_bytes(start as *mut u8, 0u8, range.len()); + } + } else if #[cfg(unix)] { let flags = if readwrite { rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE } else { @@ -775,7 +779,9 @@ impl MemoryImageSlot { unsafe { cfg_if::cfg_if! { - if #[cfg(unix)] { + if #[cfg(miri)] { + std::ptr::write_bytes(self.base as *mut u8, 0, self.static_size); + } else if #[cfg(unix)] { let ptr = rustix::mm::mmap_anonymous( self.base as *mut c_void, self.static_size, @@ -838,7 +844,7 @@ impl Drop for MemoryImageSlot { } } -#[cfg(all(test, target_os = "linux"))] +#[cfg(all(test, target_os = "linux", not(miri)))] mod test { use std::sync::Arc; diff --git a/crates/runtime/src/debug_builtins.rs b/crates/runtime/src/debug_builtins.rs index 411300008b7e..8354c1146687 100644 --- a/crates/runtime/src/debug_builtins.rs +++ b/crates/runtime/src/debug_builtins.rs @@ -45,6 +45,9 @@ pub unsafe extern "C" fn set_vmctx_memory(vmctx_ptr: *mut VMContext) { // exported as symbols. It is a workaround: the executable normally ignores // `pub extern "C"`, see rust-lang/rust#25057. pub fn ensure_exported() { + if cfg!(miri) { + return; + } unsafe { std::ptr::read_volatile(resolve_vmctx_memory_ptr as *const u8); std::ptr::read_volatile(set_vmctx_memory as *const u8); diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs index 917f16c62670..54e7588e3abd 100644 --- a/crates/runtime/src/instance/allocator/pooling.rs +++ b/crates/runtime/src/instance/allocator/pooling.rs @@ -35,7 +35,7 @@ cfg_if::cfg_if! { use imp::{commit_table_pages, decommit_table_pages}; -#[cfg(all(feature = "async", unix))] +#[cfg(all(feature = "async", unix, not(miri)))] use imp::{commit_stack_pages, reset_stack_pages_to_zero}; fn round_up_to_pow2(n: usize, to: usize) -> usize { @@ -368,7 +368,7 @@ impl TablePool { /// /// The top of the stack (starting stack pointer) is returned when a stack is allocated /// from the pool. -#[cfg(all(feature = "async", unix))] +#[cfg(all(feature = "async", unix, not(miri)))] #[derive(Debug)] struct StackPool { mapping: Mmap, @@ -380,7 +380,7 @@ struct StackPool { async_stack_keep_resident: usize, } -#[cfg(all(feature = "async", unix))] +#[cfg(all(feature = "async", unix, not(miri)))] impl StackPool { fn new(config: &PoolingInstanceAllocatorConfig) -> Result { use rustix::mm::{mprotect, MprotectFlags}; @@ -578,7 +578,7 @@ pub struct PoolingInstanceAllocator { linear_memory_keep_resident: usize, table_keep_resident: usize, - #[cfg(all(feature = "async", unix))] + #[cfg(all(feature = "async", unix, not(miri)))] stacks: StackPool, #[cfg(all(feature = "async", windows))] stack_size: usize, @@ -601,7 +601,7 @@ impl PoolingInstanceAllocator { tables: TablePool::new(&config.limits)?, linear_memory_keep_resident: config.linear_memory_keep_resident, table_keep_resident: config.table_keep_resident, - #[cfg(all(feature = "async", unix))] + #[cfg(all(feature = "async", unix, not(miri)))] stacks: StackPool::new(config)?, #[cfg(all(feature = "async", windows))] stack_size: config.stack_size, @@ -612,7 +612,8 @@ impl PoolingInstanceAllocator { let size_to_memset = size.min(self.table_keep_resident); unsafe { std::ptr::write_bytes(base, 0, size_to_memset); - decommit_table_pages(base.add(size_to_memset), size - size_to_memset)?; + decommit_table_pages(base.add(size_to_memset), size - size_to_memset) + .context("failed to decommit table page")?; } Ok(()) } @@ -895,30 +896,43 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { } } - #[cfg(all(feature = "async", unix))] + #[cfg(feature = "async")] fn allocate_fiber_stack(&self) -> Result { - self.stacks.allocate() - } - - #[cfg(all(feature = "async", unix))] - unsafe fn deallocate_fiber_stack(&self, stack: &wasmtime_fiber::FiberStack) { - self.stacks.deallocate(stack); - } + cfg_if::cfg_if! { + if #[cfg(miri)] { + unimplemented!() + } else if #[cfg(unix)] { + self.stacks.allocate() + } else if #[cfg(windows)] { + if self.stack_size == 0 { + bail!("fiber stack allocation not supported") + } - #[cfg(all(feature = "async", windows))] - fn allocate_fiber_stack(&self) -> Result { - if self.stack_size == 0 { - bail!("fiber stack allocation not supported") + // On windows, we don't use a stack pool as we use the native + // fiber implementation + let stack = wasmtime_fiber::FiberStack::new(self.stack_size)?; + Ok(stack) + } else { + compile_error!("not implemented"); + } } - - // On windows, we don't use a stack pool as we use the native fiber implementation - let stack = wasmtime_fiber::FiberStack::new(self.stack_size)?; - Ok(stack) } - #[cfg(all(feature = "async", windows))] - unsafe fn deallocate_fiber_stack(&self, _stack: &wasmtime_fiber::FiberStack) { - // A no-op as we don't own the fiber stack on Windows + #[cfg(feature = "async")] + unsafe fn deallocate_fiber_stack(&self, stack: &wasmtime_fiber::FiberStack) { + cfg_if::cfg_if! { + if #[cfg(miri)] { + let _ = stack; + unimplemented!() + } else if #[cfg(unix)] { + self.stacks.deallocate(stack); + } else if #[cfg(windows)] { + // A no-op as we don't own the fiber stack on Windows + let _ = stack; + } else { + compile_error!("not implemented"); + } + } } fn purge_module(&self, module: CompiledModuleId) { @@ -1160,7 +1174,7 @@ mod test { Ok(()) } - #[cfg(all(unix, target_pointer_width = "64", feature = "async"))] + #[cfg(all(unix, target_pointer_width = "64", feature = "async", not(miri)))] #[test] fn test_stack_pool() -> Result<()> { let config = PoolingInstanceAllocatorConfig { @@ -1283,7 +1297,7 @@ mod test { assert_eq!(pool.memories.memory_size, 2 * 65536); } - #[cfg(all(unix, target_pointer_width = "64", feature = "async"))] + #[cfg(all(unix, target_pointer_width = "64", feature = "async", not(miri)))] #[test] fn test_stack_zeroed() -> Result<()> { let config = PoolingInstanceAllocatorConfig { @@ -1319,7 +1333,7 @@ mod test { Ok(()) } - #[cfg(all(unix, target_pointer_width = "64", feature = "async"))] + #[cfg(all(unix, target_pointer_width = "64", feature = "async", not(miri)))] #[test] fn test_stack_unzeroed() -> Result<()> { let config = PoolingInstanceAllocatorConfig { diff --git a/crates/runtime/src/instance/allocator/pooling/index_allocator.rs b/crates/runtime/src/instance/allocator/pooling/index_allocator.rs index b68e294560ff..94a001f4e6b1 100644 --- a/crates/runtime/src/instance/allocator/pooling/index_allocator.rs +++ b/crates/runtime/src/instance/allocator/pooling/index_allocator.rs @@ -509,7 +509,8 @@ mod test { let mut last_id = vec![None; 1000]; let mut hits = 0; - for _ in 0..100_000 { + let amt = if cfg!(miri) { 100 } else { 100_000 }; + for _ in 0..amt { loop { if !allocated.is_empty() && rng.gen_bool(0.5) { let i = rng.gen_range(0..allocated.len()); @@ -535,7 +536,7 @@ mod test { // IDs). Check for at least double that to ensure some sort of // affinity is occurring. assert!( - hits > 20000, + hits > (amt / 5), "expected at least 20000 (20%) ID-reuses but got {}", hits ); diff --git a/crates/runtime/src/instance/allocator/pooling/unix.rs b/crates/runtime/src/instance/allocator/pooling/unix.rs index ab246c3b4434..00259f4b0afd 100644 --- a/crates/runtime/src/instance/allocator/pooling/unix.rs +++ b/crates/runtime/src/instance/allocator/pooling/unix.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::Result; fn decommit(addr: *mut u8, len: usize) -> Result<()> { if len == 0 { @@ -7,13 +7,14 @@ fn decommit(addr: *mut u8, len: usize) -> Result<()> { unsafe { cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(miri)] { + std::ptr::write_bytes(addr, 0, len); + } else if #[cfg(target_os = "linux")] { use rustix::mm::{madvise, Advice}; // On Linux, this is enough to cause the kernel to initialize // the pages to 0 on next access - madvise(addr as _, len, Advice::LinuxDontNeed) - .context("madvise failed to decommit: {}")?; + madvise(addr as _, len, Advice::LinuxDontNeed)?; } else { use rustix::mm::{mmap_anonymous, ProtFlags, MapFlags}; @@ -26,8 +27,7 @@ fn decommit(addr: *mut u8, len: usize) -> Result<()> { len, ProtFlags::READ | ProtFlags::WRITE, MapFlags::PRIVATE | MapFlags::FIXED, - ) - .context("mmap failed to remap pages: {}")?; + )?; } } } @@ -44,13 +44,13 @@ pub fn decommit_table_pages(addr: *mut u8, len: usize) -> Result<()> { decommit(addr, len) } -#[cfg(feature = "async")] +#[cfg(all(feature = "async", not(miri)))] pub fn commit_stack_pages(_addr: *mut u8, _len: usize) -> Result<()> { // A no-op as stack pages remain READ|WRITE Ok(()) } -#[cfg(feature = "async")] +#[cfg(all(feature = "async", not(miri)))] pub fn reset_stack_pages_to_zero(addr: *mut u8, len: usize) -> Result<()> { decommit(addr, len) } diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 28a8f699879c..47b854b2b0ae 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -40,6 +40,7 @@ mod memory; mod mmap; mod mmap_vec; mod parking_spot; +mod store_box; mod table; mod traphandlers; mod vmcontext; @@ -65,6 +66,7 @@ pub use crate::memory::{ }; pub use crate::mmap::Mmap; pub use crate::mmap_vec::MmapVec; +pub use crate::store_box::*; pub use crate::table::{Table, TableElement}; pub use crate::traphandlers::{ catch_traps, init_traps, raise_lib_trap, raise_user_trap, resume_panic, tls_eager_initialize, diff --git a/crates/runtime/src/mmap.rs b/crates/runtime/src/mmap.rs index fd322dd8c275..1c077890c35b 100644 --- a/crates/runtime/src/mmap.rs +++ b/crates/runtime/src/mmap.rs @@ -11,6 +11,9 @@ cfg_if::cfg_if! { if #[cfg(windows)] { mod windows; use windows as sys; + } else if #[cfg(miri)] { + mod miri; + use miri as sys; } else { mod unix; use unix as sys; diff --git a/crates/runtime/src/mmap/miri.rs b/crates/runtime/src/mmap/miri.rs new file mode 100644 index 000000000000..bb5e9e7511a7 --- /dev/null +++ b/crates/runtime/src/mmap/miri.rs @@ -0,0 +1,93 @@ +//! A "dummy" implementation of mmaps for miri where "we do the best we can" +//! +//! Namely this uses `alloc` to allocate memory for the "mmap" specifically to +//! create page-aligned allocations. This allocation doesn't handle oeprations +//! like becoming executable or becoming readonly or being created from files, +//! but it's enough to get various tests running relying on memories and such. + +use anyhow::{bail, Result}; +use std::alloc::{self, Layout}; +use std::fs::File; +use std::ops::Range; +use std::path::Path; + +#[derive(Debug)] +pub struct Mmap { + memory: *mut [u8], +} + +unsafe impl Send for Mmap {} +unsafe impl Sync for Mmap {} + +impl Mmap { + pub fn new_empty() -> Mmap { + Mmap { memory: &mut [] } + } + + pub fn new(size: usize) -> Result { + let mut ret = Mmap::reserve(size)?; + ret.make_accessible(0, size)?; + Ok(ret) + } + + pub fn reserve(size: usize) -> Result { + let layout = Layout::from_size_align(size, crate::page_size()).unwrap(); + let ptr = unsafe { alloc::alloc(layout) }; + if ptr.is_null() { + bail!("failed to allocate memory"); + } + + Ok(Mmap { + memory: std::ptr::slice_from_raw_parts_mut(ptr, size), + }) + } + + pub fn from_file(_path: &Path) -> Result<(Self, File)> { + bail!("not supported on miri"); + } + + pub fn make_accessible(&mut self, start: usize, len: usize) -> Result<()> { + // The memory is technically always accessible but this marks it as + // initialized for miri-level checking. + unsafe { + std::ptr::write_bytes(self.memory.cast::().add(start), 0u8, len); + } + Ok(()) + } + + pub fn as_ptr(&self) -> *const u8 { + self.memory as *const u8 + } + + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.memory.cast() + } + + pub fn len(&self) -> usize { + unsafe { (*self.memory).len() } + } + + pub unsafe fn make_executable( + &self, + _range: Range, + _enable_branch_protection: bool, + ) -> Result<()> { + Ok(()) + } + + pub unsafe fn make_readonly(&self, _range: Range) -> Result<()> { + Ok(()) + } +} + +impl Drop for Mmap { + fn drop(&mut self) { + if self.len() == 0 { + return; + } + unsafe { + let layout = Layout::from_size_align(self.len(), crate::page_size()).unwrap(); + alloc::dealloc(self.as_mut_ptr(), layout); + } + } +} diff --git a/crates/runtime/src/parking_spot.rs b/crates/runtime/src/parking_spot.rs index 33f9bce0ff80..8b974fde758f 100644 --- a/crates/runtime/src/parking_spot.rs +++ b/crates/runtime/src/parking_spot.rs @@ -279,6 +279,7 @@ mod tests { )* ) => { $( #[test] + #[cfg_attr(miri, ignore)] fn $name() { if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() { return; @@ -490,7 +491,7 @@ mod tests { let atomic_key = addr_of!(atomic) as u64; const N: u64 = 5; - const M: u64 = 1000; + const M: u64 = if cfg!(miri) { 10 } else { 1000 }; let thread = s.spawn(move || { while atomic.load(Ordering::SeqCst) != N * M { diff --git a/crates/runtime/src/store_box.rs b/crates/runtime/src/store_box.rs new file mode 100644 index 000000000000..00929e540a65 --- /dev/null +++ b/crates/runtime/src/store_box.rs @@ -0,0 +1,35 @@ +/// A `Box` lookalike for memory that's stored in a `Store` +/// +/// This is intended to be quite similar to a `Box` except without the +/// `Deref` implementations. The main motivation for this type's existence is to +/// appease the aliasing rules in miri to ensure that `StoreBox` can be moved +/// around without invalidating pointers to the contents within the box. The +/// standard `Box` type does not implement this for example and moving that +/// will invalidate derived pointers. +pub struct StoreBox(*mut T); + +unsafe impl Send for StoreBox {} +unsafe impl Sync for StoreBox {} + +impl StoreBox { + /// Allocates space on the heap to store `val` and returns a pointer to it + /// living on the heap. + pub fn new(val: T) -> StoreBox { + StoreBox(Box::into_raw(Box::new(val))) + } +} + +impl StoreBox { + /// Returns the underlying pointer to `T` which is owned by the store. + pub fn get(&self) -> *mut T { + self.0 + } +} + +impl Drop for StoreBox { + fn drop(&mut self) { + unsafe { + drop(Box::from_raw(self.0)); + } + } +} diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs index e9dbc958d342..ecc05453f8dd 100644 --- a/crates/runtime/src/traphandlers.rs +++ b/crates/runtime/src/traphandlers.rs @@ -14,16 +14,54 @@ use std::sync::Once; pub use self::backtrace::{Backtrace, Frame}; pub use self::tls::{tls_eager_initialize, TlsRestore}; -#[link(name = "wasmtime-helpers")] -extern "C" { - #[allow(improper_ctypes)] - fn wasmtime_setjmp( - jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8, *mut VMContext), - payload: *mut u8, - callee: *mut VMContext, - ) -> i32; - fn wasmtime_longjmp(jmp_buf: *const u8) -> !; +cfg_if::cfg_if! { + if #[cfg(miri)] { + // With MIRI set up just enough of a setjmp/longjmp with catching panics + // to get a few tests working that use this. + // + // Note that no actual JIT code runs in MIRI so this is purely here for + // host-to-host calls. + + struct WasmtimeLongjmp; + + unsafe extern "C" fn wasmtime_setjmp( + _jmp_buf: *mut *const u8, + callback: extern "C" fn(*mut u8, *mut VMContext), + payload: *mut u8, + callee: *mut VMContext, + ) -> i32 { + use std::panic::{self, AssertUnwindSafe}; + let result = panic::catch_unwind(AssertUnwindSafe(|| { + callback(payload, callee); + })); + match result { + Ok(()) => 1, + Err(e) => { + if e.is::() { + 0 + } else { + panic::resume_unwind(e) + } + } + } + } + + unsafe extern "C" fn wasmtime_longjmp(_jmp_buf: *const u8) -> ! { + std::panic::panic_any(WasmtimeLongjmp) + } + } else { + #[link(name = "wasmtime-helpers")] + extern "C" { + #[allow(improper_ctypes)] + fn wasmtime_setjmp( + jmp_buf: *mut *const u8, + callback: extern "C" fn(*mut u8, *mut VMContext), + payload: *mut u8, + callee: *mut VMContext, + ) -> i32; + fn wasmtime_longjmp(jmp_buf: *const u8) -> !; + } + } } cfg_if::cfg_if! { diff --git a/crates/runtime/src/traphandlers/unix.rs b/crates/runtime/src/traphandlers/unix.rs index 183773370633..022e32d9b28d 100644 --- a/crates/runtime/src/traphandlers/unix.rs +++ b/crates/runtime/src/traphandlers/unix.rs @@ -14,6 +14,9 @@ static mut PREV_SIGILL: MaybeUninit = MaybeUninit::uninit(); static mut PREV_SIGFPE: MaybeUninit = MaybeUninit::uninit(); pub unsafe fn platform_init() { + if cfg!(miri) { + return; + } let register = |slot: &mut MaybeUninit, signal: i32| { let mut handler: libc::sigaction = mem::zeroed(); // The flags here are relatively careful, and they are... @@ -312,6 +315,10 @@ pub fn lazy_per_thread_init() { }); unsafe fn allocate_sigaltstack() -> Option { + if cfg!(miri) { + return None; + } + // Check to see if the existing sigaltstack, if it exists, is big // enough. If so we don't need to allocate our own. let mut old_stack = mem::zeroed(); diff --git a/crates/runtime/src/vmcontext/vm_host_func_context.rs b/crates/runtime/src/vmcontext/vm_host_func_context.rs index ba6f9fbc84a9..e939dba4d45c 100644 --- a/crates/runtime/src/vmcontext/vm_host_func_context.rs +++ b/crates/runtime/src/vmcontext/vm_host_func_context.rs @@ -2,9 +2,8 @@ //! //! Keep in sync with `wasmtime_environ::VMHostFuncOffsets`. -use crate::VMFuncRef; - use super::VMOpaqueContext; +use crate::{StoreBox, VMFuncRef}; use std::any::Any; use wasmtime_environ::{VM_ARRAY_CALL_HOST_FUNC_MAGIC, VM_NATIVE_CALL_HOST_FUNC_MAGIC}; @@ -37,14 +36,17 @@ impl VMArrayCallHostFuncContext { pub unsafe fn new( func_ref: VMFuncRef, host_state: Box, - ) -> Box { + ) -> StoreBox { debug_assert!(func_ref.vmctx.is_null()); - let mut ctx = Box::new(VMArrayCallHostFuncContext { + let ctx = StoreBox::new(VMArrayCallHostFuncContext { magic: wasmtime_environ::VM_ARRAY_CALL_HOST_FUNC_MAGIC, func_ref, host_state, }); - ctx.func_ref.vmctx = VMOpaqueContext::from_vm_array_call_host_func_context(&mut *ctx); + let vmctx = VMOpaqueContext::from_vm_array_call_host_func_context(ctx.get()); + unsafe { + (*ctx.get()).func_ref.vmctx = vmctx; + } ctx } @@ -109,13 +111,16 @@ impl VMNativeCallHostFuncContext { pub unsafe fn new( func_ref: VMFuncRef, host_state: Box, - ) -> Box { - let mut ctx = Box::new(VMNativeCallHostFuncContext { + ) -> StoreBox { + let ctx = StoreBox::new(VMNativeCallHostFuncContext { magic: wasmtime_environ::VM_NATIVE_CALL_HOST_FUNC_MAGIC, func_ref, host_state, }); - ctx.func_ref.vmctx = VMOpaqueContext::from_vm_native_call_host_func_context(&mut *ctx); + let vmctx = VMOpaqueContext::from_vm_native_call_host_func_context(ctx.get()); + unsafe { + (*ctx.get()).func_ref.vmctx = vmctx; + } ctx } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 55acdb38b685..4beeb71ab206 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -185,7 +185,7 @@ impl Config { async_stack_size: 2 << 20, async_support: false, module_version: ModuleVersionStrategy::default(), - parallel_compilation: true, + parallel_compilation: !cfg!(miri), memory_init_cow: true, memory_guaranteed_dense_image_size: 16 << 20, force_memory_init_memfd: false, diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 31eddbe5d269..054ea2521ae7 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -650,6 +650,7 @@ mod tests { use tempfile::TempDir; #[test] + #[cfg_attr(miri, ignore)] fn cache_accounts_for_opt_level() -> Result<()> { let td = TempDir::new()?; let config_path = td.path().join("config.toml"); diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index f8b2f83e8e43..3979f0612dc9 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -532,6 +532,7 @@ Caused by: } #[test] + #[cfg_attr(miri, ignore)] fn test_tunables_int_mismatch() -> Result<()> { let engine = Engine::default(); let mut metadata = Metadata::new(&engine); diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs index 104c5f386027..906b80427514 100644 --- a/crates/wasmtime/src/func.rs +++ b/crates/wasmtime/src/func.rs @@ -11,7 +11,7 @@ use std::pin::Pin; use std::ptr::{self, NonNull}; use std::sync::Arc; use wasmtime_runtime::{ - ExportFunction, InstanceHandle, VMArrayCallHostFuncContext, VMContext, VMFuncRef, + ExportFunction, InstanceHandle, StoreBox, VMArrayCallHostFuncContext, VMContext, VMFuncRef, VMFunctionImport, VMNativeCallHostFuncContext, VMOpaqueContext, VMSharedSignatureIndex, }; @@ -1426,6 +1426,13 @@ fn enter_wasm(store: &mut StoreContextMut<'_, T>) -> Option { return None; } + // Ignore this stack pointer business on miri since we can't execute wasm + // anyway and the concept of a stack pointer on miri is a bit nebulous + // regardless. + if cfg!(miri) { + return None; + } + let stack_pointer = psm::stack_pointer() as usize; // Determine the stack pointer where, after which, any wasm code will @@ -2104,18 +2111,18 @@ for_each_function_signature!(impl_into_func); #[doc(hidden)] pub enum HostContext { - Native(Box), - Array(Box), + Native(StoreBox), + Array(StoreBox), } -impl From> for HostContext { - fn from(ctx: Box) -> Self { +impl From> for HostContext { + fn from(ctx: StoreBox) -> Self { HostContext::Native(ctx) } } -impl From> for HostContext { - fn from(ctx: Box) -> Self { +impl From> for HostContext { + fn from(ctx: StoreBox) -> Self { HostContext::Array(ctx) } } @@ -2267,8 +2274,8 @@ impl HostFunc { pub(crate) fn func_ref(&self) -> &VMFuncRef { match &self.ctx { - HostContext::Native(ctx) => ctx.func_ref(), - HostContext::Array(ctx) => ctx.func_ref(), + HostContext::Native(ctx) => unsafe { (*ctx.get()).func_ref() }, + HostContext::Array(ctx) => unsafe { (*ctx.get()).func_ref() }, } } diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/memory.rs index c3d17784fc56..1d309f995e5c 100644 --- a/crates/wasmtime/src/memory.rs +++ b/crates/wasmtime/src/memory.rs @@ -769,7 +769,7 @@ impl SharedMemory { pub fn data(&self) -> &[UnsafeCell] { unsafe { let definition = &*self.0.vmmemory_ptr(); - slice::from_raw_parts_mut(definition.base.cast(), definition.current_length()) + slice::from_raw_parts(definition.base.cast(), definition.current_length()) } } diff --git a/crates/wasmtime/src/module/registry.rs b/crates/wasmtime/src/module/registry.rs index 459c139bb530..de6fee9f179a 100644 --- a/crates/wasmtime/src/module/registry.rs +++ b/crates/wasmtime/src/module/registry.rs @@ -274,6 +274,7 @@ pub fn unregister_code(code: &Arc) { } #[test] +#[cfg_attr(miri, ignore)] fn test_frame_info() -> Result<(), anyhow::Error> { use crate::*; let mut store = Store::<()>::default(); diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs index b36e9650c73e..4e5878fe7601 100644 --- a/crates/wasmtime/src/store.rs +++ b/crates/wasmtime/src/store.rs @@ -96,7 +96,7 @@ use std::sync::Arc; use std::task::{Context, Poll}; use wasmtime_runtime::{ InstanceAllocationRequest, InstanceAllocator, InstanceHandle, ModuleInfo, - OnDemandInstanceAllocator, SignalHandler, StorePtr, VMContext, VMExternRef, + OnDemandInstanceAllocator, SignalHandler, StoreBox, StorePtr, VMContext, VMExternRef, VMExternRefActivationsTable, VMRuntimeLimits, WasmFault, }; @@ -281,7 +281,7 @@ pub struct StoreOpaque { externref_activations_table: VMExternRefActivationsTable, modules: ModuleRegistry, func_refs: FuncRefs, - host_globals: Vec>, + host_globals: Vec>, // Numbers of resources instantiated in this store, and their limits instance_count: usize, @@ -1190,7 +1190,7 @@ impl StoreOpaque { self.func_refs.push_instance_pre_func_refs(func_refs); } - pub(crate) fn host_globals(&mut self) -> &mut Vec> { + pub(crate) fn host_globals(&mut self) -> &mut Vec> { &mut self.host_globals } diff --git a/crates/wasmtime/src/store/func_refs.rs b/crates/wasmtime/src/store/func_refs.rs index 0e600866cf4e..814f680c6f7e 100644 --- a/crates/wasmtime/src/store/func_refs.rs +++ b/crates/wasmtime/src/store/func_refs.rs @@ -49,7 +49,7 @@ mod unpatched_func_ref { impl UnpatchedFuncRef { /// Safety: Callers must ensure that the given `func_ref` and resulting /// wrapped value are used in a `Send + Sync` compatible way. - pub unsafe fn new(func_ref: &VMFuncRef) -> UnpatchedFuncRef { + pub unsafe fn new(func_ref: &mut VMFuncRef) -> UnpatchedFuncRef { debug_assert!(func_ref.wasm_call.is_none()); UnpatchedFuncRef(NonNull::from(func_ref)) } @@ -80,8 +80,10 @@ impl FuncRefs { let _ = unsafe { VMNativeCallHostFuncContext::from_opaque(func_ref.vmctx) }; let func_ref = self.bump.alloc(func_ref); - self.with_holes.push(UnpatchedFuncRef::new(func_ref)); - NonNull::from(func_ref) + let unpatched = UnpatchedFuncRef::new(func_ref); + let ret = unpatched.func_ref(); + self.with_holes.push(unpatched); + ret } /// Patch any `VMFuncRef::wasm_call`s that need filling in. diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs index efb402b84147..e719186d89d9 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/trampoline/func.rs @@ -5,7 +5,9 @@ use anyhow::Result; use std::panic::{self, AssertUnwindSafe}; use std::ptr::NonNull; use wasmtime_jit::{CodeMemory, ProfilingAgent}; -use wasmtime_runtime::{VMArrayCallHostFuncContext, VMContext, VMFuncRef, VMOpaqueContext}; +use wasmtime_runtime::{ + StoreBox, VMArrayCallHostFuncContext, VMContext, VMFuncRef, VMOpaqueContext, +}; struct TrampolineState { func: F, @@ -113,7 +115,7 @@ pub fn create_array_call_function( ft: &FuncType, func: F, engine: &Engine, -) -> Result> +) -> Result> where F: Fn(*mut VMContext, &mut [ValRaw]) -> Result<()> + Send + Sync + 'static, { diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs index 32940504ed67..cadc0f3f1b8e 100644 --- a/crates/wasmtime/src/trampoline/global.rs +++ b/crates/wasmtime/src/trampoline/global.rs @@ -2,7 +2,7 @@ use crate::store::StoreOpaque; use crate::{GlobalType, Mutability, Val}; use std::ptr; use wasmtime_environ::GlobalInit; -use wasmtime_runtime::VMGlobalDefinition; +use wasmtime_runtime::{StoreBox, VMGlobalDefinition}; #[repr(C)] pub struct VMHostGlobalContext { @@ -33,40 +33,39 @@ pub fn generate_global_export( ty: GlobalType, val: Val, ) -> wasmtime_runtime::ExportGlobal { - let mut ctx = Box::new(VMHostGlobalContext { + let global = wasmtime_environ::Global { + wasm_ty: ty.content().to_wasm_type(), + mutability: match ty.mutability() { + Mutability::Const => false, + Mutability::Var => true, + }, + // TODO: This is just a dummy value; nothing should actually read + // this. We should probably remove this field from the struct. + initializer: GlobalInit::I32Const(0), + }; + let ctx = StoreBox::new(VMHostGlobalContext { ty, global: VMGlobalDefinition::new(), }); - unsafe { + let definition = unsafe { + let global = &mut (*ctx.get()).global; match val { - Val::I32(x) => *ctx.global.as_i32_mut() = x, - Val::I64(x) => *ctx.global.as_i64_mut() = x, - Val::F32(x) => *ctx.global.as_f32_bits_mut() = x, - Val::F64(x) => *ctx.global.as_f64_bits_mut() = x, - Val::V128(x) => *ctx.global.as_u128_mut() = x, + Val::I32(x) => *global.as_i32_mut() = x, + Val::I64(x) => *global.as_i64_mut() = x, + Val::F32(x) => *global.as_f32_bits_mut() = x, + Val::F64(x) => *global.as_f64_bits_mut() = x, + Val::V128(x) => *global.as_u128_mut() = x, Val::FuncRef(f) => { - *ctx.global.as_func_ref_mut() = f.map_or(ptr::null_mut(), |f| { + *global.as_func_ref_mut() = f.map_or(ptr::null_mut(), |f| { f.caller_checked_func_ref(store).as_ptr() }) } - Val::ExternRef(x) => *ctx.global.as_externref_mut() = x.map(|x| x.inner), + Val::ExternRef(x) => *global.as_externref_mut() = x.map(|x| x.inner), } - } - - let ret = wasmtime_runtime::ExportGlobal { - definition: &mut ctx.global as *mut _, - global: wasmtime_environ::Global { - wasm_ty: ctx.ty.content().to_wasm_type(), - mutability: match ctx.ty.mutability() { - Mutability::Const => false, - Mutability::Var => true, - }, - // TODO: This is just a dummy value; nothing should actually read - // this. We should probably remove this field from the struct. - initializer: GlobalInit::I32Const(0), - }, + global }; + store.host_globals().push(ctx); - ret + wasmtime_runtime::ExportGlobal { definition, global } } diff --git a/src/commands/compile.rs b/src/commands/compile.rs index e218fbcba559..a80b171f5b16 100644 --- a/src/commands/compile.rs +++ b/src/commands/compile.rs @@ -122,7 +122,7 @@ impl CompileCommand { } } -#[cfg(test)] +#[cfg(all(test, not(miri)))] mod test { use super::*; use std::io::Write; diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs index fdb26f247955..e7d9e6b5ba88 100644 --- a/tests/all/async_functions.rs +++ b/tests/all/async_functions.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::{anyhow, bail, Result}; use std::future::Future; use std::pin::Pin; diff --git a/tests/all/call_hook.rs b/tests/all/call_hook.rs index f5c61fdf4560..9d47ecb85d8b 100644 --- a/tests/all/call_hook.rs +++ b/tests/all/call_hook.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::{bail, Error, Result}; use std::future::Future; use std::pin::Pin; diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index 6a1baa47ed08..ca815bd77771 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::{bail, Context, Result}; use std::fs::File; use std::io::{Read, Write}; diff --git a/tests/all/component_model.rs b/tests/all/component_model.rs index fba2739a3cad..87b483d8b7bf 100644 --- a/tests/all/component_model.rs +++ b/tests/all/component_model.rs @@ -18,6 +18,7 @@ mod post_return; mod strings; #[test] +#[cfg_attr(miri, ignore)] fn components_importing_modules() -> Result<()> { let engine = engine(); diff --git a/tests/all/component_model/aot.rs b/tests/all/component_model/aot.rs index 2a86655a8e47..3b03aef23755 100644 --- a/tests/all/component_model/aot.rs +++ b/tests/all/component_model/aot.rs @@ -30,6 +30,7 @@ fn bare_bones() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn mildly_more_interesting() -> Result<()> { let engine = super::engine(); let component = Component::new( diff --git a/tests/all/component_model/async.rs b/tests/all/component_model/async.rs index 01c7341a6a4e..44877a1b0485 100644 --- a/tests/all/component_model/async.rs +++ b/tests/all/component_model/async.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::Result; use wasmtime::component::*; use wasmtime::{Store, StoreContextMut, Trap}; diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index 70681b6bfb22..d8c8f3023cd4 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::engine; use anyhow::Result; use wasmtime::{ diff --git a/tests/all/component_model/dynamic.rs b/tests/all/component_model/dynamic.rs index b71553493c60..ff93a049a607 100644 --- a/tests/all/component_model/dynamic.rs +++ b/tests/all/component_model/dynamic.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::{make_echo_component, make_echo_component_with_params, Param, Type}; use anyhow::Result; use component_test_util::FuncExt; diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs index 94c0cb38e29b..85bc25fc2001 100644 --- a/tests/all/component_model/func.rs +++ b/tests/all/component_model/func.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::{TypedFuncExt, REALLOC_AND_FREE}; use anyhow::Result; use std::rc::Rc; diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index dd479a8726b8..be5ba39bedf5 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::REALLOC_AND_FREE; use anyhow::Result; use std::ops::Deref; diff --git a/tests/all/component_model/macros.rs b/tests/all/component_model/macros.rs index f6c83e592db3..30b2f3ca2e22 100644 --- a/tests/all/component_model/macros.rs +++ b/tests/all/component_model/macros.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::{make_echo_component, TypedFuncExt}; use anyhow::Result; use component_macro_test::{add_variants, flags_test}; diff --git a/tests/all/component_model/nested.rs b/tests/all/component_model/nested.rs index 473c6b751b78..35349c2e73f6 100644 --- a/tests/all/component_model/nested.rs +++ b/tests/all/component_model/nested.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::REALLOC_AND_FREE; use anyhow::Result; use wasmtime::component::*; diff --git a/tests/all/component_model/post_return.rs b/tests/all/component_model/post_return.rs index d35be4337a05..8cd5d934a9b0 100644 --- a/tests/all/component_model/post_return.rs +++ b/tests/all/component_model/post_return.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::Result; use wasmtime::component::*; use wasmtime::{Store, StoreContextMut, Trap}; diff --git a/tests/all/component_model/strings.rs b/tests/all/component_model/strings.rs index 3cabe9161686..ad46ddb0a6ab 100644 --- a/tests/all/component_model/strings.rs +++ b/tests/all/component_model/strings.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use super::REALLOC_AND_FREE; use anyhow::Result; use wasmtime::component::{Component, Linker}; diff --git a/tests/all/custom_signal_handler.rs b/tests/all/custom_signal_handler.rs index 498014093548..fdfc22a7fef3 100644 --- a/tests/all/custom_signal_handler.rs +++ b/tests/all/custom_signal_handler.rs @@ -2,6 +2,7 @@ target_os = "linux", all(target_os = "macos", feature = "posix-signals-on-macos") ))] +#[cfg(not(miri))] mod tests { use anyhow::Result; use rustix::mm::{mprotect, MprotectFlags}; diff --git a/tests/all/epoch_interruption.rs b/tests/all/epoch_interruption.rs index a340ef658341..8e32881c2dc3 100644 --- a/tests/all/epoch_interruption.rs +++ b/tests/all/epoch_interruption.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use crate::async_functions::{CountPending, PollOnce}; use anyhow::{anyhow, Result}; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/tests/all/externals.rs b/tests/all/externals.rs index baf15c9d14ec..680b533198d1 100644 --- a/tests/all/externals.rs +++ b/tests/all/externals.rs @@ -57,6 +57,7 @@ fn bad_tables() { } #[test] +#[cfg_attr(miri, ignore)] fn cross_store() -> anyhow::Result<()> { let mut cfg = Config::new(); cfg.wasm_reference_types(true); diff --git a/tests/all/fuel.rs b/tests/all/fuel.rs index 730d56b44b78..0936458351cf 100644 --- a/tests/all/fuel.rs +++ b/tests/all/fuel.rs @@ -25,6 +25,7 @@ impl<'a> Parse<'a> for FuelWast<'a> { } #[test] +#[cfg_attr(miri, ignore)] fn run() -> Result<()> { let test = std::fs::read_to_string("tests/all/fuel.wast")?; let buf = ParseBuffer::new(&test)?; @@ -58,6 +59,7 @@ fn fuel_consumed(wasm: &[u8]) -> u64 { } #[test] +#[cfg_attr(miri, ignore)] fn iloop() { iloop_aborts( r#" @@ -141,6 +143,7 @@ fn manual_fuel() { } #[test] +#[cfg_attr(miri, ignore)] fn host_function_consumes_all() { const FUEL: u64 = 10_000; let mut config = Config::new(); @@ -185,6 +188,7 @@ fn manual_edge_cases() { } #[test] +#[cfg_attr(miri, ignore)] fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping() { let mut config = Config::new(); config.consume_fuel(true); diff --git a/tests/all/func.rs b/tests/all/func.rs index a1d37f174f82..7c8021478519 100644 --- a/tests/all/func.rs +++ b/tests/all/func.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use wasmtime::*; #[test] +#[cfg_attr(miri, ignore)] fn call_wasm_to_wasm() -> Result<()> { let wasm = wat::parse_str( r#" @@ -31,6 +32,7 @@ fn call_wasm_to_wasm() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_wasm_to_native() -> Result<()> { let wasm = wat::parse_str( r#" @@ -55,6 +57,7 @@ fn call_wasm_to_native() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_wasm_to_array() -> Result<()> { let wasm = wat::parse_str( r#" @@ -88,6 +91,7 @@ fn call_wasm_to_array() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_native_to_wasm() -> Result<()> { let wasm = wat::parse_str( r#" @@ -125,6 +129,7 @@ fn call_native_to_native() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_native_to_array() -> Result<()> { let mut store = Store::<()>::default(); @@ -148,6 +153,7 @@ fn call_native_to_array() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_array_to_wasm() -> Result<()> { let wasm = wat::parse_str( r#" @@ -195,6 +201,7 @@ fn call_array_to_native() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_array_to_array() -> Result<()> { let mut store = Store::<()>::default(); let func = Func::new( @@ -223,6 +230,7 @@ fn call_array_to_array() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_indirect_native_from_wasm_import_global() -> Result<()> { let wasm = wat::parse_str( r#" @@ -255,6 +263,7 @@ fn call_indirect_native_from_wasm_import_global() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_indirect_native_from_wasm_import_table() -> Result<()> { let wasm = wat::parse_str( r#" @@ -283,6 +292,7 @@ fn call_indirect_native_from_wasm_import_table() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_indirect_native_from_wasm_import_func_returns_funcref() -> Result<()> { let wasm = wat::parse_str( r#" @@ -311,6 +321,7 @@ fn call_indirect_native_from_wasm_import_func_returns_funcref() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_indirect_native_from_exported_table() -> Result<()> { let wasm = wat::parse_str( r#" @@ -337,6 +348,7 @@ fn call_indirect_native_from_exported_table() -> Result<()> { // wasm exports global, host puts native-call funcref in global, wasm calls funcref #[test] +#[cfg_attr(miri, ignore)] fn call_indirect_native_from_exported_global() -> Result<()> { let wasm = wat::parse_str( r#" @@ -482,6 +494,7 @@ fn signatures_match() { } #[test] +#[cfg_attr(miri, ignore)] fn import_works() -> Result<()> { static HITS: AtomicUsize = AtomicUsize::new(0); @@ -589,6 +602,7 @@ fn trap_smoke() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn trap_import() -> Result<()> { let wasm = wat::parse_str( r#" @@ -645,6 +659,7 @@ fn get_from_wrapper() { } #[test] +#[cfg_attr(miri, ignore)] fn get_from_signature() { let mut store = Store::<()>::default(); let ty = FuncType::new(None, None); @@ -662,6 +677,7 @@ fn get_from_signature() { } #[test] +#[cfg_attr(miri, ignore)] fn get_from_module() -> anyhow::Result<()> { let mut store = Store::<()>::default(); let module = Module::new( @@ -733,6 +749,7 @@ fn call_wrapped_func() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn caller_memory() -> anyhow::Result<()> { let mut store = Store::<()>::default(); let f = Func::wrap(&mut store, |mut c: Caller<'_, ()>| { @@ -798,6 +815,7 @@ fn caller_memory() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn func_write_nothing() -> anyhow::Result<()> { let mut store = Store::<()>::default(); let ty = FuncType::new(None, Some(ValType::I32)); @@ -810,6 +828,7 @@ fn func_write_nothing() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn return_cross_store_value() -> anyhow::Result<()> { let _ = env_logger::try_init(); @@ -875,6 +894,7 @@ fn pass_cross_store_arg() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn externref_signature_no_reference_types() -> anyhow::Result<()> { let mut config = Config::new(); config.wasm_reference_types(false); @@ -892,6 +912,7 @@ fn externref_signature_no_reference_types() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn trampolines_always_valid() -> anyhow::Result<()> { // Compile two modules up front let mut store = Store::<()>::default(); @@ -918,6 +939,7 @@ fn trampolines_always_valid() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn typed_multiple_results() -> anyhow::Result<()> { let mut store = Store::<()>::default(); let module = Module::new( @@ -954,6 +976,7 @@ fn typed_multiple_results() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn trap_doesnt_leak() -> anyhow::Result<()> { #[derive(Default)] struct Canary(Arc); @@ -994,6 +1017,7 @@ fn trap_doesnt_leak() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn wrap_multiple_results() -> anyhow::Result<()> { fn test(store: &mut Store<()>, t: T) -> anyhow::Result<()> where @@ -1158,6 +1182,7 @@ fn wrap_multiple_results() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn trampoline_for_declared_elem() -> anyhow::Result<()> { let engine = Engine::default(); @@ -1185,6 +1210,7 @@ fn trampoline_for_declared_elem() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn wasm_ty_roundtrip() -> Result<(), anyhow::Error> { let mut store = Store::<()>::default(); let debug = Func::wrap( @@ -1240,6 +1266,7 @@ fn wasm_ty_roundtrip() -> Result<(), anyhow::Error> { } #[test] +#[cfg_attr(miri, ignore)] fn typed_funcs_count_params_correctly_in_error_messages() -> anyhow::Result<()> { let mut store = Store::<()>::default(); let module = Module::new( diff --git a/tests/all/funcref.rs b/tests/all/funcref.rs index 74980bb21cda..e11b5ab02ff4 100644 --- a/tests/all/funcref.rs +++ b/tests/all/funcref.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use wasmtime::*; #[test] +#[cfg_attr(miri, ignore)] fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { let (mut store, module) = ref_types_module( false, @@ -77,6 +78,7 @@ fn pass_funcref_in_and_out_of_wasm() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn receive_null_funcref_from_wasm() -> anyhow::Result<()> { let (mut store, module) = ref_types_module( false, @@ -126,6 +128,7 @@ fn wrong_store() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn func_new_returns_wrong_store() -> anyhow::Result<()> { let dropped = Arc::new(AtomicBool::new(false)); { diff --git a/tests/all/gc.rs b/tests/all/gc.rs index e9b1e0abd0ea..096a3bec2952 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -13,11 +13,13 @@ impl Drop for SetFlagOnDrop { } #[test] +#[cfg_attr(miri, ignore)] fn smoke_test_gc() -> anyhow::Result<()> { smoke_test_gc_impl(false) } #[test] +#[cfg_attr(miri, ignore)] fn smoke_test_gc_epochs() -> anyhow::Result<()> { smoke_test_gc_impl(true) } @@ -77,6 +79,7 @@ fn smoke_test_gc_impl(use_epochs: bool) -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn wasm_dropping_refs() -> anyhow::Result<()> { let (mut store, module) = ref_types_module( false, @@ -120,6 +123,7 @@ fn wasm_dropping_refs() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn many_live_refs() -> anyhow::Result<()> { let mut wat = r#" (module @@ -200,6 +204,7 @@ fn many_live_refs() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn drop_externref_via_table_set() -> anyhow::Result<()> { let (mut store, module) = ref_types_module( false, @@ -247,6 +252,7 @@ fn drop_externref_via_table_set() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn global_drops_externref() -> anyhow::Result<()> { test_engine(&Engine::default())?; @@ -296,6 +302,7 @@ fn global_drops_externref() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn table_drops_externref() -> anyhow::Result<()> { test_engine(&Engine::default())?; @@ -346,6 +353,7 @@ fn table_drops_externref() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn gee_i_sure_hope_refcounting_is_atomic() -> anyhow::Result<()> { let mut config = Config::new(); config.wasm_reference_types(true); @@ -435,6 +443,7 @@ fn global_init_no_leak() -> anyhow::Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn no_gc_middle_of_args() -> anyhow::Result<()> { let (mut store, module) = ref_types_module( false, diff --git a/tests/all/host_funcs.rs b/tests/all/host_funcs.rs index 795305efb433..efa582eea1ee 100644 --- a/tests/all/host_funcs.rs +++ b/tests/all/host_funcs.rs @@ -210,6 +210,7 @@ fn signatures_match() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn import_works() -> Result<()> { static HITS: AtomicUsize = AtomicUsize::new(0); @@ -311,6 +312,7 @@ fn import_works() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_import_many_args() -> Result<()> { let wasm = wat::parse_str( r#" @@ -370,6 +372,7 @@ fn call_import_many_args() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_wasm_many_args() -> Result<()> { let wasm = wat::parse_str( r#" @@ -459,6 +462,7 @@ fn trap_smoke() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn trap_import() -> Result<()> { let wasm = wat::parse_str( r#" @@ -482,6 +486,7 @@ fn trap_import() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn new_from_signature() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); @@ -592,6 +597,7 @@ fn call_wrapped_func() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn func_return_nothing() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); @@ -608,6 +614,7 @@ fn func_return_nothing() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn call_via_funcref() -> Result<()> { static HITS: AtomicUsize = AtomicUsize::new(0); @@ -695,6 +702,7 @@ fn store_with_context() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn wasi_imports() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); diff --git a/tests/all/iloop.rs b/tests/all/iloop.rs index fedef6f6dbcf..6e7077e87427 100644 --- a/tests/all/iloop.rs +++ b/tests/all/iloop.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}; use wasmtime::*; diff --git a/tests/all/import_calling_export.rs b/tests/all/import_calling_export.rs index 5b17d6937e15..247fbcd1ebc0 100644 --- a/tests/all/import_calling_export.rs +++ b/tests/all/import_calling_export.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::Result; use wasmtime::*; diff --git a/tests/all/import_indexes.rs b/tests/all/import_indexes.rs index d6718f166597..6383d576e34c 100644 --- a/tests/all/import_indexes.rs +++ b/tests/all/import_indexes.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use wasmtime::*; #[test] diff --git a/tests/all/instance.rs b/tests/all/instance.rs index 95ed175df359..23d734a1cc39 100644 --- a/tests/all/instance.rs +++ b/tests/all/instance.rs @@ -13,6 +13,7 @@ fn wrong_import_numbers() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn initializes_linear_memory() -> Result<()> { // Test for https://github.com/bytecodealliance/wasmtime/issues/2784 let wat = r#" @@ -33,6 +34,7 @@ fn initializes_linear_memory() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn linear_memory_limits() -> Result<()> { // this test will allocate 4GB of virtual memory space, and may not work in // situations like CI QEMU emulation where it triggers SIGKILL. diff --git a/tests/all/invoke_func_via_table.rs b/tests/all/invoke_func_via_table.rs index a50d30ceeec2..8edb1838645b 100644 --- a/tests/all/invoke_func_via_table.rs +++ b/tests/all/invoke_func_via_table.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::{Context as _, Result}; use wasmtime::*; diff --git a/tests/all/limits.rs b/tests/all/limits.rs index 3dee9f3a0b68..810902f11332 100644 --- a/tests/all/limits.rs +++ b/tests/all/limits.rs @@ -4,6 +4,7 @@ use wasmtime::*; const WASM_PAGE_SIZE: usize = wasmtime_environ::WASM_PAGE_SIZE as usize; #[test] +#[cfg_attr(miri, ignore)] fn test_limits() -> Result<()> { let engine = Engine::default(); let module = Module::new( @@ -91,6 +92,7 @@ fn test_limits() -> Result<()> { } #[tokio::test] +#[cfg_attr(miri, ignore)] async fn test_limits_async() -> Result<()> { let mut config = Config::new(); config.async_support(true); @@ -425,6 +427,7 @@ impl ResourceLimiter for MemoryContext { } #[test] +#[cfg_attr(miri, ignore)] fn test_custom_memory_limiter() -> Result<()> { let engine = Engine::default(); let mut linker = Linker::new(&engine); @@ -540,6 +543,7 @@ impl ResourceLimiterAsync for MemoryContext { } #[tokio::test] +#[cfg_attr(miri, ignore)] async fn test_custom_memory_limiter_async() -> Result<()> { let mut config = Config::new(); config.async_support(true); @@ -840,6 +844,7 @@ impl ResourceLimiterAsync for FailureDetector { } #[tokio::test] +#[cfg_attr(miri, ignore)] async fn custom_limiter_async_detect_grow_failure() -> Result<()> { if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() { return Ok(()); @@ -979,6 +984,7 @@ fn panic_in_memory_limiter() { #[test] #[should_panic(expected = "resource limiter memory growing")] +#[cfg_attr(miri, ignore)] fn panic_in_memory_limiter_wasm_stack() { // Like the test above, except the memory.grow happens in wasm code // instead of a host function call. @@ -1025,6 +1031,7 @@ fn panic_in_table_limiter() { #[tokio::test] #[should_panic(expected = "async resource limiter memory growing")] +#[cfg_attr(miri, ignore)] async fn panic_in_async_memory_limiter() { let mut config = Config::new(); config.async_support(true); @@ -1044,6 +1051,7 @@ async fn panic_in_async_memory_limiter() { #[tokio::test] #[should_panic(expected = "async resource limiter memory growing")] +#[cfg_attr(miri, ignore)] async fn panic_in_async_memory_limiter_wasm_stack() { // Like the test above, except the memory.grow happens in // wasm code instead of a host function call. @@ -1075,6 +1083,7 @@ async fn panic_in_async_memory_limiter_wasm_stack() { #[tokio::test] #[should_panic(expected = "async resource limiter table growing")] +#[cfg_attr(miri, ignore)] async fn panic_in_async_table_limiter() { let mut config = Config::new(); config.async_support(true); @@ -1096,6 +1105,7 @@ async fn panic_in_async_table_limiter() { } #[test] +#[cfg_attr(miri, ignore)] fn growth_trap() -> Result<()> { let engine = Engine::default(); let module = Module::new( diff --git a/tests/all/linker.rs b/tests/all/linker.rs index 7fe580b4c32c..737b2b498117 100644 --- a/tests/all/linker.rs +++ b/tests/all/linker.rs @@ -90,6 +90,7 @@ fn link_twice_bad() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn function_interposition() -> Result<()> { let mut store = Store::<()>::default(); let mut linker = Linker::new(store.engine()); @@ -124,6 +125,7 @@ fn function_interposition() -> Result<()> { // Same as `function_interposition`, but the linker's name for the function // differs from the module's name. #[test] +#[cfg_attr(miri, ignore)] fn function_interposition_renamed() -> Result<()> { let mut store = Store::<()>::default(); let mut linker = Linker::new(store.engine()); @@ -154,6 +156,7 @@ fn function_interposition_renamed() -> Result<()> { // Similar to `function_interposition`, but use `Linker::instance` instead of // `Linker::define`. #[test] +#[cfg_attr(miri, ignore)] fn module_interposition() -> Result<()> { let mut store = Store::<()>::default(); let mut linker = Linker::new(store.engine()); @@ -185,6 +188,7 @@ fn module_interposition() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn allow_unknown_exports() -> Result<()> { let mut store = Store::<()>::default(); let mut linker = Linker::new(store.engine()); @@ -203,6 +207,7 @@ fn allow_unknown_exports() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn no_leak() -> Result<()> { struct DropMe(Rc>); @@ -231,6 +236,7 @@ fn no_leak() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn no_leak_with_imports() -> Result<()> { struct DropMe(Arc); @@ -354,6 +360,7 @@ fn instance_pre() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_trapping_unknown_import() -> Result<()> { const WAT: &str = r#" (module @@ -392,6 +399,7 @@ fn test_trapping_unknown_import() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_default_value_unknown_import() -> Result<()> { const WAT: &str = r#" (module diff --git a/tests/all/main.rs b/tests/all/main.rs index 754492b0cd3d..e9941cc73710 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(miri, allow(dead_code, unused_imports))] + mod async_functions; mod call_hook; mod cli_tests; diff --git a/tests/all/memory.rs b/tests/all/memory.rs index a2eaa0e54acb..ecd1a0ab5881 100644 --- a/tests/all/memory.rs +++ b/tests/all/memory.rs @@ -92,6 +92,7 @@ fn test_traps(store: &mut Store<()>, funcs: &[TestFunc], addr: u32, mem: &Memory } #[test] +#[cfg_attr(miri, ignore)] fn offsets_static_dynamic_oh_my() -> Result<()> { const GB: u64 = 1 << 30; @@ -137,6 +138,7 @@ fn offsets_static_dynamic_oh_my() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn guards_present() -> Result<()> { const GUARD_SIZE: u64 = 65536; @@ -185,6 +187,7 @@ fn guards_present() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn guards_present_pooling() -> Result<()> { const GUARD_SIZE: u64 = 65536; @@ -322,6 +325,7 @@ fn massive_64_bit_still_limited() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn tiny_static_heap() -> Result<()> { // The size of the memory in the module below is the exact same size as // the static memory size limit in the configuration. This is intended to @@ -533,6 +537,7 @@ fn shared_memory_basics() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn shared_memory_wait_notify() -> Result<()> { const THREADS: usize = 8; const COUNT: usize = 100_000; diff --git a/tests/all/memory_creator.rs b/tests/all/memory_creator.rs index 26950ca6169b..3984b2e00c42 100644 --- a/tests/all/memory_creator.rs +++ b/tests/all/memory_creator.rs @@ -1,4 +1,4 @@ -#[cfg(not(target_os = "windows"))] +#[cfg(all(not(target_os = "windows"), not(miri)))] mod not_for_windows { use wasmtime::*; use wasmtime_environ::{WASM32_MAX_PAGES, WASM_PAGE_SIZE}; diff --git a/tests/all/module.rs b/tests/all/module.rs index b1eea2f6516d..9b3b83ab1bb6 100644 --- a/tests/all/module.rs +++ b/tests/all/module.rs @@ -51,6 +51,7 @@ fn caches_across_engines() { } #[test] +#[cfg_attr(miri, ignore)] fn aot_compiles() -> Result<()> { let engine = Engine::default(); let bytes = engine.precompile_module( @@ -69,6 +70,7 @@ fn aot_compiles() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn serialize_deterministic() { let engine = Engine::default(); @@ -177,7 +179,7 @@ fn serialize_not_overly_massive() -> Result<()> { // This test then also tests that loading modules through various means, e.g. // through precompiled artifacts, all works. #[test] -#[cfg_attr(not(target_arch = "x86_64"), ignore)] +#[cfg_attr(any(not(target_arch = "x86_64"), miri), ignore)] fn missing_sse_and_floats_still_works() -> Result<()> { let mut config = Config::new(); config.wasm_simd(false); diff --git a/tests/all/module_serialize.rs b/tests/all/module_serialize.rs index 614803019ef6..023018528f60 100644 --- a/tests/all/module_serialize.rs +++ b/tests/all/module_serialize.rs @@ -43,6 +43,7 @@ fn test_version_mismatch() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_module_serialize_simple() -> Result<()> { let buffer = serialize( &Engine::default(), @@ -59,6 +60,7 @@ fn test_module_serialize_simple() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_module_serialize_fail() -> Result<()> { let buffer = serialize( &Engine::default(), @@ -76,6 +78,7 @@ fn test_module_serialize_fail() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_deserialize_from_file() -> Result<()> { serialize_and_call("(module (func (export \"run\") (result i32) i32.const 42))")?; serialize_and_call( @@ -105,6 +108,7 @@ fn test_deserialize_from_file() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn deserialize_from_serialized() -> Result<()> { let engine = Engine::default(); let buffer1 = serialize( diff --git a/tests/all/name.rs b/tests/all/name.rs index ec6095b23557..5e64e1935632 100644 --- a/tests/all/name.rs +++ b/tests/all/name.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use wasmtime::*; #[test] diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs index 8eaafbf63a64..e015ee02680f 100644 --- a/tests/all/pooling_allocator.rs +++ b/tests/all/pooling_allocator.rs @@ -25,6 +25,7 @@ fn successful_instantiation() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn memory_limit() -> Result<()> { let mut pool = PoolingAllocationConfig::default(); pool.instance_count(1) @@ -112,7 +113,13 @@ fn memory_init() -> Result<()> { let module = Module::new( &engine, - r#"(module (memory (export "m") 2) (data (i32.const 65530) "this data spans multiple pages") (data (i32.const 10) "hello world"))"#, + r#" + (module + (memory (export "m") 2) + (data (i32.const 65530) "this data spans multiple pages") + (data (i32.const 10) "hello world") + ) + "#, )?; let mut store = Store::new(&engine, ()); @@ -129,6 +136,7 @@ fn memory_init() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn memory_guard_page_trap() -> Result<()> { let mut pool = PoolingAllocationConfig::default(); pool.instance_count(1) @@ -141,7 +149,12 @@ fn memory_guard_page_trap() -> Result<()> { let module = Module::new( &engine, - r#"(module (memory (export "m") 0) (func (export "f") (param i32) local.get 0 i32.load drop))"#, + r#" + (module + (memory (export "m") 0) + (func (export "f") (param i32) local.get 0 i32.load drop) + ) + "#, )?; // Instantiate the module and check for out of bounds trap @@ -231,6 +244,7 @@ fn memory_zeroed() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn table_limit() -> Result<()> { const TABLE_ELEMENTS: u32 = 10; let mut pool = PoolingAllocationConfig::default(); @@ -315,6 +329,7 @@ fn table_limit() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn table_init() -> Result<()> { let mut pool = PoolingAllocationConfig::default(); pool.instance_count(1) @@ -327,7 +342,18 @@ fn table_init() -> Result<()> { let module = Module::new( &engine, - r#"(module (table (export "t") 6 funcref) (elem (i32.const 1) 1 2 3 4) (elem (i32.const 0) 0) (func) (func (param i32)) (func (param i32 i32)) (func (param i32 i32 i32)) (func (param i32 i32 i32 i32)))"#, + r#" + (module + (table (export "t") 6 funcref) + (elem (i32.const 1) 1 2 3 4) + (elem (i32.const 0) 0) + (func) + (func (param i32)) + (func (param i32 i32)) + (func (param i32 i32 i32)) + (func (param i32 i32 i32 i32)) + ) + "#, )?; let mut store = Store::new(&engine, ()); @@ -473,15 +499,17 @@ fn preserve_data_segments() -> Result<()> { // Spray some stuff on the heap. If wasm data lived on the heap this should // paper over things and help us catch use-after-free here if it would // otherwise happen. - let mut strings = Vec::new(); - for _ in 0..1000 { - let mut string = String::new(); + if !cfg!(miri) { + let mut strings = Vec::new(); for _ in 0..1000 { - string.push('g'); + let mut string = String::new(); + for _ in 0..1000 { + string.push('g'); + } + strings.push(string); } - strings.push(string); + drop(strings); } - drop(strings); let mem = i.get_memory(&mut store, "mem").unwrap(); @@ -580,6 +608,7 @@ fn drop_externref_global_during_module_init() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn switch_image_and_non_image() -> Result<()> { let mut pool = PoolingAllocationConfig::default(); pool.instance_count(1); @@ -638,6 +667,7 @@ fn switch_image_and_non_image() -> Result<()> { #[test] #[cfg(target_pointer_width = "64")] +#[cfg_attr(miri, ignore)] fn instance_too_large() -> Result<()> { let mut pool = PoolingAllocationConfig::default(); pool.instance_size(16).instance_count(1); @@ -679,6 +709,7 @@ configured maximum of 16 bytes; breakdown of allocation requirement: } #[test] +#[cfg_attr(miri, ignore)] fn dynamic_memory_pooling_allocator() -> Result<()> { for guard_size in [0, 1 << 16] { let max_size = 128 << 20; @@ -787,6 +818,7 @@ fn dynamic_memory_pooling_allocator() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn zero_memory_pages_disallows_oob() -> Result<()> { let mut pool = PoolingAllocationConfig::default(); pool.instance_count(1).instance_memory_pages(0); diff --git a/tests/all/relocs.rs b/tests/all/relocs.rs index a27ff6a001ef..db2feee5dbc3 100644 --- a/tests/all/relocs.rs +++ b/tests/all/relocs.rs @@ -8,6 +8,8 @@ //! 32-bits, and right now object files aren't supported larger than 4gb anyway //! so we would need a lot of other support necessary to exercise that. +#![cfg(not(miri))] + use anyhow::Result; use wasmtime::*; diff --git a/tests/all/stack_overflow.rs b/tests/all/stack_overflow.rs index f2e2417d46c7..ec534b7af594 100644 --- a/tests/all/stack_overflow.rs +++ b/tests/all/stack_overflow.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::Result; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use wasmtime::*; diff --git a/tests/all/table.rs b/tests/all/table.rs index 8bc62f4f1a14..2b22c2ee7631 100644 --- a/tests/all/table.rs +++ b/tests/all/table.rs @@ -53,6 +53,7 @@ fn copy_wrong() { } #[test] +#[cfg_attr(miri, ignore)] fn null_elem_segment_works_with_imported_table() -> Result<()> { let mut store = Store::<()>::default(); let ty = TableType::new(ValType::FuncRef, 1, None); diff --git a/tests/all/threads.rs b/tests/all/threads.rs index 30ca86bdd3ae..b08daf279163 100644 --- a/tests/all/threads.rs +++ b/tests/all/threads.rs @@ -52,6 +52,7 @@ fn test_export_shared_memory() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_sharing_of_shared_memory() -> Result<()> { let wat = r#"(module (import "env" "memory" (memory 1 5 shared)) @@ -95,6 +96,7 @@ fn test_sharing_of_shared_memory() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_probe_shared_memory_size() -> Result<()> { let wat = r#"(module (memory (export "memory") 1 2 shared) @@ -157,6 +159,7 @@ fn test_multi_memory() -> Result<()> { } #[test] +#[cfg_attr(miri, ignore)] fn test_grow_memory_in_multiple_threads() -> Result<()> { const NUM_THREADS: usize = 4; const NUM_GROW_OPS: usize = 1000; @@ -226,6 +229,7 @@ fn is_sorted(data: &[u32]) -> bool { } #[test] +#[cfg_attr(miri, ignore)] fn test_memory_size_accessibility() -> Result<()> { const NUM_GROW_OPS: usize = 1000; let wat = r#"(module diff --git a/tests/all/traps.rs b/tests/all/traps.rs index 84d8fe587b56..79946263a6ca 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::{bail, Error, Result}; use std::panic::{self, AssertUnwindSafe}; use std::process::Command; diff --git a/tests/all/wait_notify.rs b/tests/all/wait_notify.rs index 930b49744a49..fb95e5d6baa9 100644 --- a/tests/all/wait_notify.rs +++ b/tests/all/wait_notify.rs @@ -1,3 +1,5 @@ +#![cfg(not(miri))] + use anyhow::Result; use std::time::Instant; use wasmtime::*; diff --git a/tests/all/wasi_testsuite.rs b/tests/all/wasi_testsuite.rs index 81d2bf434a43..680715eb7b43 100644 --- a/tests/all/wasi_testsuite.rs +++ b/tests/all/wasi_testsuite.rs @@ -3,6 +3,8 @@ //! //! [wasi-testsuite]: https://github.com/WebAssembly/wasi-testsuite +#![cfg(not(miri))] + use crate::cli_tests::run_wasmtime_for_output; use anyhow::Result; use serde::Deserialize; diff --git a/tests/host_segfault.rs b/tests/host_segfault.rs index 404fb4947028..8c6112df3597 100644 --- a/tests/host_segfault.rs +++ b/tests/host_segfault.rs @@ -80,6 +80,9 @@ fn dummy_waker() -> Waker { } fn main() { + if cfg!(miri) { + return; + } // Skip this tests if it looks like we're in a cross-compiled situation and // we're emulating this test for a different platform. In that scenario // emulators (like QEMU) tend to not report signals the same way and such. diff --git a/tests/rlimited-memory.rs b/tests/rlimited-memory.rs index 810ea07d5d2c..bd57fdd0c95c 100644 --- a/tests/rlimited-memory.rs +++ b/tests/rlimited-memory.rs @@ -37,6 +37,7 @@ impl ResourceLimiter for MemoryGrowFailureDetector { } #[test] +#[cfg_attr(miri, ignore)] fn custom_limiter_detect_os_oom_failure() -> Result<()> { if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() { return Ok(()); From 1c13b1975e7007dc676e376ad18110f0a1ebe50d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 3 May 2023 09:52:38 -0700 Subject: [PATCH 2/2] Update alignment comment --- crates/runtime/src/component.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/runtime/src/component.rs b/crates/runtime/src/component.rs index af0a1968a32e..aa167ca6b3e9 100644 --- a/crates/runtime/src/component.rs +++ b/crates/runtime/src/component.rs @@ -134,7 +134,7 @@ pub struct VMLowering { /// `ComponentInstance`. #[repr(C)] // Set an appropriate alignment for this structure where the most-aligned value -// internally right now is a pointer. +// internally right now `VMGlobalDefinition` which has an alignment of 16 bytes. #[repr(align(16))] pub struct VMComponentContext { /// For more information about this see the equivalent field in `VMContext`