CVE-2023-30624: Reliance on Undefined, Unspecified, or Implementation-Defined Behavior (CWE-758)
value: Wasmtime is a standalone runtime for WebAssembly. Prior to versions 6.0.2, 7.0.1, and 8.0.1, Wasmtime's implementation of managing per-instance state, such as tables and memories, contains LLVM-level undefined behavior. This undefined behavior was found to cause runtime-level issues when compiled with LLVM 16 which causes some writes, which are critical for correctness, to be optimized away. Vulnerable versions of Wasmtime compiled with Rust 1.70, which is currently in beta, or later are known to have incorrectly compiled functions. Versions of Wasmtime compiled with the current Rust stable release, 1.69, and prior are not known at this time to have any issues, but can theoretically exhibit potential issues.

The underlying problem is that Wasmtime's runtime state for an instance involves a Rust-defined structure called `Instance` which has a trailing `VMContext` structure after it. This `VMContext` structure has a runtime-defined layout that is unique per-module. This representation cannot be expressed with safe code in Rust so `unsafe` code is required to maintain this state. The code doing this, however, has methods which take `&self` as an argument but modify data in the `VMContext` part of the allocation. This means that pointers derived from `&self` are mutated. This is typically not allowed, except in the presence of `UnsafeCell`, in Rust. When compiled to LLVM these functions have `noalias readonly` parameters which means it's UB to write through the pointers.

Wasmtime's internal representation and management of `VMContext` has been updated to use `&mut self` methods where appropriate. Additionally verification tools for `unsafe` code in Rust, such as `cargo miri`, are planned to be executed on the `main` branch soon to fix any Rust-level issues that may be exploited in future compiler versions.

Precomplied binaries available for Wasmtime from GitHub releases have been compiled with at most LLVM 15 so are not known to be vulnerable. As mentioned above, however, it's still recommended to update.

Wasmtime version 6.0.2, 7.0.1, and 8.0.1 have been issued which contain the patch necessary to work correctly on LLVM 16 and have no known UB on LLVM 15 and earlier. If Wasmtime is compiled with Rust 1.69 and prior, which use LLVM 15, then there are no known issues. There is a theoretical possibility for undefined behavior to exploited, however, so it's recommended that users upgrade to a patched version of Wasmtime. Users using beta Rust (1.70 at this time) or nightly Rust (1.71 at this time) must update to a patched version to work correctly.
CWE description: The product uses an API function, data structure, or other entity in a way that relies on properties that are not always guaranteed to hold for that entity.
CVSS v3 base score: 8.8
Published: 2023-04-27T17:15Z
Repository: https://github.com/bytecodealliance/wasmtime
Language: Rust
Files changed: RELEASES.md, crates/environ/src/module.rs, crates/runtime/src/instance.rs, crates/runtime/src/instance/allocator.rs, crates/runtime/src/libcalls.rs, crates/runtime/src/traphandlers.rs
Commit date: 2023-04-27 11:50:26 -0500
Commit message: Merge pull request from GHSA-ch89-5g45-qwc7

* Fix miscompile from functions mutating `VMContext`

This commit fixes a miscompilation in Wasmtime on LLVM 16 where methods
on `Instance` which mutated the state of the internal `VMContext` were
optimized to not actually mutate the state. The root cause of this issue
is a change in LLVM which takes advantage of `noalias readonly` pointers
which is how `&self` methods are translated. This means that `Instance`
methods which take `&self` but actually mutate the `VMContext` end up
being undefined behavior from LLVM's point of view, meaning that the
writes are candidate for removal.

The fix applied here is intended to be a temporary one while a more
formal fix, ideally backed by `cargo miri` verification, is implemented
on `main`. The fix here is to change the return value of
`vmctx_plus_offset` to return `*const T` instead of `*mut T`. This
caused lots of portions of the runtime code to stop compiling because
mutations were indeed happening. To cover these a new
`vmctx_plus_offset_mut` method was added which notably takes `&mut self`
instead of `&self`. This forced all callers which may mutate to reflect
the `&mut self` requirement, propagating that outwards.

This fixes the miscompilation with LLVM 16 in the immediate future and
should be at least a meager line of defense against issues like this in
the future. This is not a long-term fix, though, since `cargo miri`
still does not like what's being done in `Instance` and with
`VMContext`. That fix is likely to be more invasive, though, so it's
being deferred to later.

* Update release notes

* Fix dates and fill out more notes
Diff: diff --git a/RELEASES.md b/RELEASES.md
index 14449c90ba..4da7e8a3c4 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -8,10 +8,31 @@ Unreleased.
 
 ### Changed
 
 --------------------------------------------------------------------------------
 
+## 8.0.1
+
+Released 2023-04-27.
+
+### Changed
+
+* Breaking: Files opened using Wasmtime's implementation of WASI on Windows now
+  cannot be deleted until the file handle is closed. This was already true for
+  open directories. The change was necessary for the bug fix in
+  [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163).
+
+### Fixed
+
+* Fixed wasi-common's implementation of the `O_DIRECTORY` flag to match POSIX.
+  [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163)
+
+* Undefined Behavior in Rust runtime functions
+  [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7)
+
+--------------------------------------------------------------------------------
+
 ## 8.0.0
 
 Released 2023-04-20
 
 ### Added
@@ -108,10 +129,21 @@ Released 2023-04-20
   [#6087](https://github.com/bytecodealliance/wasmtime/pull/6087)
 
 
 --------------------------------------------------------------------------------
 
+## 7.0.1
+
+Released 2023-04-27.
+
+### Fixed
+
+* Undefined Behavior in Rust runtime functions
+  [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7)
+
+--------------------------------------------------------------------------------
+
 ## 7.0.0
 
 Released 2023-03-20
 
 ### Added
@@ -172,10 +204,21 @@ Released 2023-03-20
 *  Miscompilation of `i8x16.select` with the same inputs on x86\_64
   [GHSA-xm67-587q-r2vw](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-xm67-587q-r2vw)
 
 --------------------------------------------------------------------------------
 
+## 6.0.2
+
+Released 2023-04-27.
+
+### Fixed
+
+* Undefined Behavior in Rust runtime functions
+  [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7)
+
+--------------------------------------------------------------------------------
+
 ## 6.0.1
 
 Released 2023-03-08.
 
 ### Fixed
diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs
index 3f3f2317c6..e6e61cc5ed 100644
--- a/crates/environ/src/module.rs
+++ b/crates/environ/src/module.rs
@@ -239,12 +239,13 @@ impl ModuleTranslation<'_> {
                 segments: Vec::new(),
             });
         }
         let mut idx = 0;
         let ok = self.module.memory_initialization.init_memory(
+            &mut (),
             InitMemory::CompileTime(&self.module),
-            &mut |memory, init| {
+            |(), memory, init| {
                 // Currently `Static` only applies to locally-defined memories,
                 // so if a data segment references an imported memory then
                 // transitioning to a `Static` memory initializer is not
                 // possible.
                 if self.module.defined_memory_index(memory).is_none() {
@@ -523,14 +524,15 @@ impl MemoryInitialization {
     /// global value is needed but `None` is returned, then false will be
     /// returned. At compile-time this typically means that the "error" in
     /// question needs to be deferred to runtime, and at runtime this means
     /// that an invalid initializer has been found and a trap should be
     /// generated.
-    pub fn init_memory(
+    pub fn init_memory<T>(
         &self,
-        state: InitMemory<'_>,
-        write: &mut dyn FnMut(MemoryIndex, &StaticMemoryInitializer) -> bool,
+        state: &mut T,
+        init: InitMemory<'_, T>,
+        mut write: impl FnMut(&mut T, MemoryIndex, &StaticMemoryInitializer) -> bool,
     ) -> bool {
         let initializers = match self {
             // Fall through below to the segmented memory one-by-one
             // initialization.
             MemoryInitialization::Segmented(list) => list,
@@ -541,11 +543,11 @@ impl MemoryInitialization {
             // Note that existence of `Static` already guarantees that all
             // indices are in-bounds.
             MemoryInitialization::Static { map } => {
                 for (index, init) in map {
                     if let Some(init) = init {
-                        let result = write(index, init);
+                        let result = write(state, index, init);
                         if !result {
                             return result;
                         }
                     }
                 }
@@ -565,14 +567,14 @@ impl MemoryInitialization {
             // in-bounds for the initial size of the memory at `memory_index`.
             // Note that this can bail if we don't have access to globals yet
             // (e.g. this is a task happening before instantiation at
             // compile-time).
             let base = match base {
-                Some(index) => match &state {
+                Some(index) => match &init {
                     InitMemory::Runtime {
                         get_global_as_u64, ..
-                    } => get_global_as_u64(index),
+                    } => get_global_as_u64(state, index),
                     InitMemory::CompileTime(_) => return false,
                 },
                 None => 0,
             };
             let start = match base.checked_add(offset) {
@@ -583,16 +585,16 @@ impl MemoryInitialization {
             let end = match start.checked_add(len) {
                 Some(end) => end,
                 None => return false,
             };
 
-            let cur_size_in_pages = match &state {
+            let cur_size_in_pages = match &init {
                 InitMemory::CompileTime(module) => module.memory_plans[memory_index].memory.minimum,
                 InitMemory::Runtime {
                     memory_size_in_pages,
                     ..
-                } => memory_size_in_pages(memory_index),
+                } => memory_size_in_pages(state, memory_index),
             };
 
             // Note that this `minimum` can overflow if `minimum` is
             // `1 << 48`, the maximum number of minimum pages for 64-bit
             // memories. If this overflow happens, though, then there's no need
@@ -614,11 +616,11 @@ impl MemoryInitialization {
             // written. Any erroneous result is propagated upwards.
             let init = StaticMemoryInitializer {
                 offset: start,
                 data: data.clone(),
             };
-            let result = write(memory_index, &init);
+            let result = write(state, memory_index, &init);
             if !result {
                 return result;
             }
         }
 
@@ -626,11 +628,11 @@ impl MemoryInitialization {
     }
 }
 
 /// Argument to [`MemoryInitialization::init_memory`] indicating the current
 /// status of the instance.
-pub enum InitMemory<'a> {
+pub enum InitMemory<'a, T> {
     /// This evaluation of memory initializers is happening at compile time.
     /// This means that the current state of memories is whatever their initial
     /// state is, and additionally globals are not available if data segments
     /// have global offsets.
     CompileTime(&'a Module),
@@ -638,14 +640,14 @@ pub enum InitMemory<'a> {
     /// Evaluation of memory initializers is happening at runtime when the
     /// instance is available, and callbacks are provided to learn about the
     /// instance's state.
     Runtime {
         /// Returns the size, in wasm pages, of the the memory specified.
-        memory_size_in_pages: &'a dyn Fn(MemoryIndex) -> u64,
+        memory_size_in_pages: &'a dyn Fn(&mut T, MemoryIndex) -> u64,
         /// Returns the value of the global, as a `u64`. Note that this may
         /// involve zero-extending a 32-bit global to a 64-bit number.
-        get_global_as_u64: &'a dyn Fn(GlobalIndex) -> u64,
+        get_global_as_u64: &'a dyn Fn(&mut T, GlobalIndex) -> u64,
     },
 }
 
 /// Implementation styles for WebAssembly tables.
 #[derive(Debug, Clone, Hash, Serialize, Deserialize)]
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index c3c6d6378a..faad6b7d2d 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -145,12 +145,18 @@ impl Instance {
         InstanceHandle { instance: ptr }
     }
 
     /// Helper function to access various locations offset from our `*mut
     /// VMContext` object.
-    unsafe fn vmctx_plus_offset<T>(&self, offset: u32) -> *mut T {
-        (self.vmctx_ptr().cast::<u8>())
+    unsafe fn vmctx_plus_offset<T>(&self, offset: u32) -> *const T {
+        (std::ptr::addr_of!(self.vmctx).cast::<u8>())
+            .add(usize::try_from(offset).unwrap())
+            .cast()
+    }
+
+    unsafe fn vmctx_plus_offset_mut<T>(&mut self, offset: u32) -> *mut T {
+        (std::ptr::addr_of_mut!(self.vmctx).cast::<u8>())
             .add(usize::try_from(offset).unwrap())
             .cast()
     }
 
     pub(crate) fn module(&self) -> &Arc<Module> {
@@ -181,24 +187,24 @@ impl Instance {
         unsafe { &*self.vmctx_plus_offset(self.offsets().vmctx_vmglobal_import(index)) }
     }
 
     /// Return the indexed `VMTableDefinition`.
     #[allow(dead_code)]
-    fn table(&self, index: DefinedTableIndex) -> VMTableDefinition {
+    fn table(&mut self, index: DefinedTableIndex) -> VMTableDefinition {
         unsafe { *self.table_ptr(index) }
     }
 
     /// Updates the value for a defined table to `VMTableDefinition`.
-    fn set_table(&self, index: DefinedTableIndex, table: VMTableDefinition) {
+    fn set_table(&mut self, index: DefinedTableIndex, table: VMTableDefinition) {
         unsafe {
             *self.table_ptr(index) = table;
         }
     }
 
     /// Return the indexed `VMTableDefinition`.
-    fn table_ptr(&self, index: DefinedTableIndex) -> *mut VMTableDefinition {
-        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_vmtable_definition(index)) }
+    fn table_ptr(&mut self, index: DefinedTableIndex) -> *mut VMTableDefinition {
+        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_vmtable_definition(index)) }
     }
 
     /// Get a locally defined or imported memory.
     pub(crate) fn get_memory(&self, index: MemoryIndex) -> VMMemoryDefinition {
         if let Some(defined_index) = self.module().defined_memory_index(index) {
@@ -236,47 +242,47 @@ impl Instance {
     fn memory_ptr(&self, index: DefinedMemoryIndex) -> *mut VMMemoryDefinition {
         unsafe { *self.vmctx_plus_offset(self.offsets().vmctx_vmmemory_pointer(index)) }
     }
 
     /// Return the indexed `VMGlobalDefinition`.
-    fn global(&self, index: DefinedGlobalIndex) -> &VMGlobalDefinition {
+    fn global(&mut self, index: DefinedGlobalIndex) -> &VMGlobalDefinition {
         unsafe { &*self.global_ptr(index) }
     }
 
     /// Return the indexed `VMGlobalDefinition`.
-    fn global_ptr(&self, index: DefinedGlobalIndex) -> *mut VMGlobalDefinition {
-        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_vmglobal_definition(index)) }
+    fn global_ptr(&mut self, index: DefinedGlobalIndex) -> *mut VMGlobalDefinition {
+        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_vmglobal_definition(index)) }
     }
 
     /// Get a raw pointer to the global at the given index regardless whether it
     /// is defined locally or imported from another module.
     ///
     /// Panics if the index is out of bound or is the reserved value.
     pub(crate) fn defined_or_imported_global_ptr(
-        &self,
+        &mut self,
         index: GlobalIndex,
     ) -> *mut VMGlobalDefinition {
         if let Some(index) = self.module().defined_global_index(index) {
             self.global_ptr(index)
         } else {
             self.imported_global(index).from
         }
     }
 
     /// Return a pointer to the interrupts structure
-    pub fn runtime_limits(&self) -> *mut *const VMRuntimeLimits {
-        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_runtime_limits()) }
+    pub fn runtime_limits(&mut self) -> *mut *const VMRuntimeLimits {
+        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_runtime_limits()) }
     }
 
     /// Return a pointer to the global epoch counter used by this instance.
-    pub fn epoch_ptr(&self) -> *mut *const AtomicU64 {
-        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_epoch_ptr()) }
+    pub fn epoch_ptr(&mut self) -> *mut *const AtomicU64 {
+        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_epoch_ptr()) }
     }
 
     /// Return a pointer to the `VMExternRefActivationsTable`.
-    pub fn externref_activations_table(&self) -> *mut *mut VMExternRefActivationsTable {
-        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_externref_activations_table()) }
+    pub fn externref_activations_table(&mut self) -> *mut *mut VMExternRefActivationsTable {
+        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_externref_activations_table()) }
     }
 
     /// Gets a pointer to this instance's `Store` which was originally
     /// configured on creation.
     ///
@@ -295,30 +301,30 @@ impl Instance {
         ptr
     }
 
     pub unsafe fn set_store(&mut self, store: Option<*mut dyn Store>) {
         if let Some(store) = store {
-            *self.vmctx_plus_offset(self.offsets().vmctx_store()) = store;
+            *self.vmctx_plus_offset_mut(self.offsets().vmctx_store()) = store;
             *self.runtime_limits() = (*store).vmruntime_limits();
             *self.epoch_ptr() = (*store).epoch_ptr();
             *self.externref_activations_table() = (*store).externref_activations_table().0;
         } else {
             assert_eq!(
                 mem::size_of::<*mut dyn Store>(),
                 mem::size_of::<[*mut (); 2]>()
             );
-            *self.vmctx_plus_offset::<[*mut (); 2]>(self.offsets().vmctx_store()) =
+            *self.vmctx_plus_offset_mut::<[*mut (); 2]>(self.offsets().vmctx_store()) =
                 [ptr::null_mut(), ptr::null_mut()];
 
             *self.runtime_limits() = ptr::null_mut();
             *self.epoch_ptr() = ptr::null_mut();
             *self.externref_activations_table() = ptr::null_mut();
         }
     }
 
     pub(crate) unsafe fn set_callee(&mut self, callee: Option<NonNull<VMFunctionBody>>) {
-        *self.vmctx_plus_offset(self.offsets().vmctx_callee()) =
+        *self.vmctx_plus_offset_mut(self.offsets().vmctx_callee()) =
             callee.map_or(ptr::null_mut(), |c| c.as_ptr());
     }
 
     /// Return a reference to the vmctx used by compiled wasm code.
     #[inline]
@@ -400,11 +406,11 @@ impl Instance {
     pub(crate) fn vmctx_offset() -> isize {
         offset_of!(Self, vmctx) as isize
     }
 
     /// Return the table index for the given `VMTableDefinition`.
-    unsafe fn table_index(&self, table: &VMTableDefinition) -> DefinedTableIndex {
+    unsafe fn table_index(&mut self, table: &VMTableDefinition) -> DefinedTableIndex {
         let index = DefinedTableIndex::new(
             usize::try_from(
                 (table as *const VMTableDefinition)
                     .offset_from(self.table_ptr(DefinedTableIndex::new(0))),
             )
@@ -513,11 +519,11 @@ impl Instance {
         sig: SignatureIndex,
         into: *mut VMCallerCheckedFuncRef,
     ) {
         let type_index = unsafe {
             let base: *const VMSharedSignatureIndex =
-                *self.vmctx_plus_offset(self.offsets().vmctx_signature_ids_array());
+                *self.vmctx_plus_offset_mut(self.offsets().vmctx_signature_ids_array());
             *base.add(sig.index())
         };
 
         let (func_ptr, vmctx) = if let Some(def_index) = self.module().defined_func_index(index) {
             (
@@ -582,11 +588,11 @@ impl Instance {
             // if we don't have to track "is-initialized" state at
             // all!
             let func = &self.module().functions[index];
             let sig = func.signature;
             let anyfunc: *mut VMCallerCheckedFuncRef = self
-                .vmctx_plus_offset::<VMCallerCheckedFuncRef>(
+                .vmctx_plus_offset_mut::<VMCallerCheckedFuncRef>(
                     self.offsets().vmctx_anyfunc(func.anyfunc),
                 );
             self.construct_anyfunc(index, sig, anyfunc);
 
             Some(anyfunc)
@@ -921,67 +927,68 @@ impl Instance {
         store: StorePtr,
         imports: Imports,
     ) {
         assert!(std::ptr::eq(module, self.module().as_ref()));
 
-        *self.vmctx_plus_offset(offsets.vmctx_magic()) = VMCONTEXT_MAGIC;
+        *self.vmctx_plus_offset_mut(offsets.vmctx_magic()) = VMCONTEXT_MAGIC;
         self.set_callee(None);
         self.set_store(store.as_raw());
 
         // Initialize shared signatures
         let signatures = self.runtime_info.signature_ids();
-        *self.vmctx_plus_offset(offsets.vmctx_signature_ids_array()) = signatures.as_ptr();
+        *self.vmctx_plus_offset_mut(offsets.vmctx_signature_ids_array()) = signatures.as_ptr();
 
         // Initialize the built-in functions
-        *self.vmctx_plus_offset(offsets.vmctx_builtin_functions()) = &VMBuiltinFunctionsArray::INIT;
+        *self.vmctx_plus_offset_mut(offsets.vmctx_builtin_functions()) =
+            &VMBuiltinFunctionsArray::INIT;
 
         // Initialize the imports
         debug_assert_eq!(imports.functions.len(), module.num_imported_funcs);
         ptr::copy_nonoverlapping(
             imports.functions.as_ptr(),
-            self.vmctx_plus_offset(offsets.vmctx_imported_functions_begin()),
+            self.vmctx_plus_offset_mut(offsets.vmctx_imported_functions_begin()),
             imports.functions.len(),
         );
         debug_assert_eq!(imports.tables.len(), module.num_imported_tables);
         ptr::copy_nonoverlapping(
             imports.tables.as_ptr(),
-            self.vmctx_plus_offset(offsets.vmctx_imported_tables_begin()),
+            self.vmctx_plus_offset_mut(offsets.vmctx_imported_tables_begin()),
             imports.tables.len(),
         );
         debug_assert_eq!(imports.memories.len(), module.num_imported_memories);
         ptr::copy_nonoverlapping(
             imports.memories.as_ptr(),
-            self.vmctx_plus_offset(offsets.vmctx_imported_memories_begin()),
+            self.vmctx_plus_offset_mut(offsets.vmctx_imported_memories_begin()),
             imports.memories.len(),
         );
         debug_assert_eq!(imports.globals.len(), module.num_imported_globals);
         ptr::copy_nonoverlapping(
             imports.globals.as_ptr(),
-            self.vmctx_plus_offset(offsets.vmctx_imported_globals_begin()),
+            self.vmctx_plus_offset_mut(offsets.vmctx_imported_globals_begin()),
             imports.globals.len(),
         );
 
         // N.B.: there is no need to initialize the anyfuncs array because
         // we eagerly construct each element in it whenever asked for a
         // reference to that element. In other words, there is no state
         // needed to track the lazy-init, so we don't need to initialize
         // any state now.
 
         // Initialize the defined tables
-        let mut ptr = self.vmctx_plus_offset(offsets.vmctx_tables_begin());
+        let mut ptr = self.vmctx_plus_offset_mut(offsets.vmctx_tables_begin());
         for i in 0..module.table_plans.len() - module.num_imported_tables {
             ptr::write(ptr, self.tables[DefinedTableIndex::new(i)].vmtable());
             ptr = ptr.add(1);
         }
 
         // Initialize the defined memories. This fills in both the
         // `defined_memories` table and the `owned_memories` table at the same
         // time. Entries in `defined_memories` hold a pointer to a definition
         // (all memories) whereas the `owned_memories` hold the actual
         // definitions of memories owned (not shared) in the module.
-        let mut ptr = self.vmctx_plus_offset(offsets.vmctx_memories_begin());
-        let mut owned_ptr = self.vmctx_plus_offset(offsets.vmctx_owned_memories_begin());
+        let mut ptr = self.vmctx_plus_offset_mut(offsets.vmctx_memories_begin());
+        let mut owned_ptr = self.vmctx_plus_offset_mut(offsets.vmctx_owned_memories_begin());
         for i in 0..module.memory_plans.len() - module.num_imported_memories {
             let defined_memory_index = DefinedMemoryIndex::new(i);
             let memory_index = module.memory_index(defined_memory_index);
             if module.memory_plans[memory_index].memory.shared {
                 let def_ptr = self.memories[defined_memory_index]
@@ -1066,12 +1073,13 @@ impl Instance {
 }
 
 impl Drop for Instance {
     fn drop(&mut self) {
         // Drop any defined globals
-        for (idx, global) in self.module().globals.iter() {
-            let idx = match self.module().defined_global_index(idx) {
+        let module = self.module().clone();
+        for (idx, global) in module.globals.iter() {
+            let idx = match module.defined_global_index(idx) {
                 Some(idx) => idx,
                 None => continue,
             };
             match global.wasm_ty {
                 // For now only externref globals need to get destroyed
@@ -1180,12 +1188,12 @@ impl InstanceHandle {
     pub fn get_defined_memory(&mut self, index: DefinedMemoryIndex) -> *mut Memory {
         self.instance_mut().get_defined_memory(index)
     }
 
     /// Return the table index for the given `VMTableDefinition` in this instance.
-    pub unsafe fn table_index(&self, table: &VMTableDefinition) -> DefinedTableIndex {
-        self.instance().table_index(table)
+    pub unsafe fn table_index(&mut self, table: &VMTableDefinition) -> DefinedTableIndex {
+        self.instance_mut().table_index(table)
     }
 
     /// Get a table defined locally within this module.
     pub fn get_defined_table(&mut self, index: DefinedTableIndex) -> *mut Table {
         self.instance_mut().get_defined_table(index)
diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs
index 3edc25c210..3c5ab816ec 100644
--- a/crates/runtime/src/instance/allocator.rs
+++ b/crates/runtime/src/instance/allocator.rs
@@ -198,20 +198,14 @@ pub unsafe trait InstanceAllocator {
     /// Primarily present for the pooling allocator to remove mappings of
     /// this module from slots in linear memory.
     fn purge_module(&self, module: CompiledModuleId);
 }
 
-fn get_table_init_start(init: &TableInitializer, instance: &Instance) -> Result<u32> {
+fn get_table_init_start(init: &TableInitializer, instance: &mut Instance) -> Result<u32> {
     match init.base {
         Some(base) => {
-            let val = unsafe {
-                if let Some(def_index) = instance.module().defined_global_index(base) {
-                    *instance.global(def_index).as_u32()
-                } else {
-                    *(*instance.imported_global(base).from).as_u32()
-                }
-            };
+            let val = unsafe { *(*instance.defined_or_imported_global_ptr(base)).as_u32() };
 
             init.offset
                 .checked_add(val)
                 .ok_or_else(|| anyhow!("element segment global base overflows"))
         }
@@ -254,40 +248,37 @@ fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<()> {
     // segments (FuncTable mode) to initialize.
     match &module.table_initialization {
         TableInitialization::FuncTable { segments, .. }
         | TableInitialization::Segments { segments } => {
             for segment in segments {
+                let start = get_table_init_start(segment, instance)?;
                 instance.table_init_segment(
                     segment.table_index,
                     &segment.elements,
-                    get_table_init_start(segment, instance)?,
+                    start,
                     0,
                     segment.elements.len() as u32,
                 )?;
             }
         }
     }
 
     Ok(())
 }
 
-fn get_memory_init_start(init: &MemoryInitializer, instance: &Instance) -> Result<u64> {
+fn get_memory_init_start(init: &MemoryInitializer, instance: &mut Instance) -> Result<u64> {
     match init.base {
         Some(base) => {
             let mem64 = instance.module().memory_plans[init.memory_index]
                 .memory
                 .memory64;
             let val = unsafe {
-                let global = if let Some(def_index) = instance.module().defined_global_index(base) {
-                    instance.global(def_index)
-                } else {
-                    &*instance.imported_global(base).from
-                };
+                let global = instance.defined_or_imported_global_ptr(base);
                 if mem64 {
-                    *global.as_u64()
+                    *(*global).as_u64()
                 } else {
-                    u64::from(*global.as_u32())
+                    u64::from(*(*global).as_u32())
                 }
             };
 
             init.offset
                 .checked_add(val)
@@ -295,11 +286,14 @@ fn get_memory_init_start(init: &MemoryInitializer, instance: &Instance) -> Resul
         }
         None => Ok(init.offset),
     }
 }
 
-fn check_memory_init_bounds(instance: &Instance, initializers: &[MemoryInitializer]) -> Result<()> {
+fn check_memory_init_bounds(
+    instance: &mut Instance,
+    initializers: &[MemoryInitializer],
+) -> Result<()> {
     for init in initializers {
         let memory = instance.get_memory(init.memory_index);
         let start = get_memory_init_start(init, instance)?;
         let end = usize::try_from(start)
             .ok()
@@ -317,25 +311,22 @@ fn check_memory_init_bounds(instance: &Instance, initializers: &[MemoryInitializ
 
     Ok(())
 }
 
 fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<()> {
-    let memory_size_in_pages =
-        &|memory| (instance.get_memory(memory).current_length() as u64) / u64::from(WASM_PAGE_SIZE);
+    let memory_size_in_pages = &|instance: &mut Instance, memory| {
+        (instance.get_memory(memory).current_length() as u64) / u64::from(WASM_PAGE_SIZE)
+    };
 
     // Loads the `global` value and returns it as a `u64`, but sign-extends
     // 32-bit globals which can be used as the base for 32-bit memories.
-    let get_global_as_u64 = &|global| unsafe {
-        let def = if let Some(def_index) = instance.module().defined_global_index(global) {
-            instance.global(def_index)
-        } else {
-            &*instance.imported_global(global).from
-        };
+    let get_global_as_u64 = &mut |instance: &mut Instance, global| unsafe {
+        let def = instance.defined_or_imported_global_ptr(global);
         if module.globals[global].wasm_ty == WasmType::I64 {
-            *def.as_u64()
+            *(*def).as_u64()
         } else {
-            u64::from(*def.as_u32())
+            u64::from(*(*def).as_u32())
         }
     };
 
     // Delegates to the `init_memory` method which is sort of a duplicate of
     // `instance.memory_init_segment` but is used at compile-time in other
@@ -344,15 +335,16 @@ fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<()> {
     //
     // This call to `init_memory` notably implements all the bells and whistles
     // so errors only happen if an out-of-bounds segment is found, in which case
     // a trap is returned.
     let ok = module.memory_initialization.init_memory(
+        instance,
         InitMemory::Runtime {
             memory_size_in_pages,
             get_global_as_u64,
         },
-        &mut |memory_index, init| {
+        |instance, memory_index, init| {
             // If this initializer applies to a defined memory but that memory
             // doesn't need initialization, due to something like copy-on-write
             // pre-initializing it via mmap magic, then this initializer can be
             // skipped entirely.
             if let Some(memory_index) = module.defined_memory_index(memory_index) {
@@ -381,11 +373,11 @@ fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<()> {
 }
 
 fn check_init_bounds(instance: &mut Instance, module: &Module) -> Result<()> {
     check_table_init_bounds(instance, module)?;
 
-    match &instance.module().memory_initialization {
+    match &module.memory_initialization {
         MemoryInitialization::Segmented(initializers) => {
             check_memory_init_bounds(instance, initializers)?;
         }
         // Statically validated already to have everything in-bounds.
         MemoryInitialization::Static { .. } => {}
diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs
index ee04b146b4..b6ce872253 100644
--- a/crates/runtime/src/libcalls.rs
+++ b/crates/runtime/src/libcalls.rs
@@ -410,11 +410,11 @@ unsafe fn activations_table_insert_with_gc(vmctx: *mut VMContext, externref: *mu
 }
 
 // Perform a Wasm `global.get` for `externref` globals.
 unsafe fn externref_global_get(vmctx: *mut VMContext, index: u32) -> *mut u8 {
     let index = GlobalIndex::from_u32(index);
-    let instance = (*vmctx).instance();
+    let instance = (*vmctx).instance_mut();
     let global = instance.defined_or_imported_global_ptr(index);
     match (*global).as_externref().clone() {
         None => ptr::null_mut(),
         Some(externref) => {
             let raw = externref.as_raw();
@@ -433,11 +433,11 @@ unsafe fn externref_global_set(vmctx: *mut VMContext, index: u32, externref: *mu
     } else {
         Some(VMExternRef::clone_from_raw(externref))
     };
 
     let index = GlobalIndex::from_u32(index);
-    let instance = (*vmctx).instance();
+    let instance = (*vmctx).instance_mut();
     let global = instance.defined_or_imported_global_ptr(index);
 
     // Swap the new `externref` value into the global before we drop the old
     // value. This protects against an `externref` with a `Drop` implementation
     // that calls back into Wasm and touches this global again (we want to avoid
diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs
index 0eb2acd2fe..e9dbc958d3 100644
--- a/crates/runtime/src/traphandlers.rs
+++ b/crates/runtime/src/traphandlers.rs
@@ -217,11 +217,11 @@ pub unsafe fn catch_traps<'a, F>(
     mut closure: F,
 ) -> Result<(), Box<Trap>>
 where
     F: FnMut(*mut VMContext),
 {
-    let limits = (*caller).instance().runtime_limits();
+    let limits = (*caller).instance_mut().runtime_limits();
 
     let result = CallThreadState::new(signal_handler, capture_backtrace, *limits).with(|cx| {
         wasmtime_setjmp(
             cx.jmp_buf.as_ptr(),
             call_closure::<F>,

Vulnerable code:             &mut |memory, init| {
    pub fn init_memory(
        state: InitMemory<'_>,
        write: &mut dyn FnMut(MemoryIndex, &StaticMemoryInitializer) -> bool,
                        let result = write(index, init);
                Some(index) => match &state {
                    } => get_global_as_u64(index),
            let cur_size_in_pages = match &state {
                } => memory_size_in_pages(memory_index),
            let result = write(memory_index, &init);
pub enum InitMemory<'a> {
        memory_size_in_pages: &'a dyn Fn(MemoryIndex) -> u64,
        get_global_as_u64: &'a dyn Fn(GlobalIndex) -> u64,
    unsafe fn vmctx_plus_offset<T>(&self, offset: u32) -> *mut T {
        (self.vmctx_ptr().cast::<u8>())
    fn table(&self, index: DefinedTableIndex) -> VMTableDefinition {
    fn set_table(&self, index: DefinedTableIndex, table: VMTableDefinition) {
    fn table_ptr(&self, index: DefinedTableIndex) -> *mut VMTableDefinition {
        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_vmtable_definition(index)) }
    fn global(&self, index: DefinedGlobalIndex) -> &VMGlobalDefinition {
    fn global_ptr(&self, index: DefinedGlobalIndex) -> *mut VMGlobalDefinition {
        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_vmglobal_definition(index)) }
        &self,
    pub fn runtime_limits(&self) -> *mut *const VMRuntimeLimits {
        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_runtime_limits()) }
    pub fn epoch_ptr(&self) -> *mut *const AtomicU64 {
        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_epoch_ptr()) }
    pub fn externref_activations_table(&self) -> *mut *mut VMExternRefActivationsTable {
        unsafe { self.vmctx_plus_offset(self.offsets().vmctx_externref_activations_table()) }
            *self.vmctx_plus_offset(self.offsets().vmctx_store()) = store;
            *self.vmctx_plus_offset::<[*mut (); 2]>(self.offsets().vmctx_store()) =
        *self.vmctx_plus_offset(self.offsets().vmctx_callee()) =
    unsafe fn table_index(&self, table: &VMTableDefinition) -> DefinedTableIndex {
                *self.vmctx_plus_offset(self.offsets().vmctx_signature_ids_array());
                .vmctx_plus_offset::<VMCallerCheckedFuncRef>(
        *self.vmctx_plus_offset(offsets.vmctx_magic()) = VMCONTEXT_MAGIC;
        *self.vmctx_plus_offset(offsets.vmctx_signature_ids_array()) = signatures.as_ptr();
        *self.vmctx_plus_offset(offsets.vmctx_builtin_functions()) = &VMBuiltinFunctionsArray::INIT;
            self.vmctx_plus_offset(offsets.vmctx_imported_functions_begin()),
            self.vmctx_plus_offset(offsets.vmctx_imported_tables_begin()),
            self.vmctx_plus_offset(offsets.vmctx_imported_memories_begin()),
            self.vmctx_plus_offset(offsets.vmctx_imported_globals_begin()),
        let mut ptr = self.vmctx_plus_offset(offsets.vmctx_tables_begin());
        let mut ptr = self.vmctx_plus_offset(offsets.vmctx_memories_begin());
        let mut owned_ptr = self.vmctx_plus_offset(offsets.vmctx_owned_memories_begin());
        for (idx, global) in self.module().globals.iter() {
            let idx = match self.module().defined_global_index(idx) {
    pub unsafe fn table_index(&self, table: &VMTableDefinition) -> DefinedTableIndex {
        self.instance().table_index(table)
fn get_table_init_start(init: &TableInitializer, instance: &Instance) -> Result<u32> {
            let val = unsafe {
                if let Some(def_index) = instance.module().defined_global_index(base) {
                    *instance.global(def_index).as_u32()
                } else {
                    *(*instance.imported_global(base).from).as_u32()
                }
            };
                    get_table_init_start(segment, instance)?,
fn get_memory_init_start(init: &MemoryInitializer, instance: &Instance) -> Result<u64> {
                let global = if let Some(def_index) = instance.module().defined_global_index(base) {
                    instance.global(def_index)
                } else {
                    &*instance.imported_global(base).from
                };
                    *global.as_u64()
                    u64::from(*global.as_u32())
fn check_memory_init_bounds(instance: &Instance, initializers: &[MemoryInitializer]) -> Result<()> {
    let memory_size_in_pages =
        &|memory| (instance.get_memory(memory).current_length() as u64) / u64::from(WASM_PAGE_SIZE);
    let get_global_as_u64 = &|global| unsafe {
        let def = if let Some(def_index) = instance.module().defined_global_index(global) {
            instance.global(def_index)
        } else {
            &*instance.imported_global(global).from
        };
            *def.as_u64()
            u64::from(*def.as_u32())
        &mut |memory_index, init| {
    match &instance.module().memory_initialization {
    let instance = (*vmctx).instance();
    let instance = (*vmctx).instance();
    let limits = (*caller).instance().runtime_limits();
Fixed code: ## 8.0.1

Released 2023-04-27.

### Changed

* Breaking: Files opened using Wasmtime's implementation of WASI on Windows now
  cannot be deleted until the file handle is closed. This was already true for
  open directories. The change was necessary for the bug fix in
  [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163).

### Fixed

* Fixed wasi-common's implementation of the `O_DIRECTORY` flag to match POSIX.
  [#6163](https://github.com/bytecodealliance/wasmtime/pull/6163)

* Undefined Behavior in Rust runtime functions
  [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7)

--------------------------------------------------------------------------------

## 7.0.1

Released 2023-04-27.

### Fixed

* Undefined Behavior in Rust runtime functions
  [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7)

--------------------------------------------------------------------------------

## 6.0.2

Released 2023-04-27.

### Fixed

* Undefined Behavior in Rust runtime functions
  [GHSA-ch89-5g45-qwc7](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-ch89-5g45-qwc7)

--------------------------------------------------------------------------------

            &mut (),
            |(), memory, init| {
    pub fn init_memory<T>(
        state: &mut T,
        init: InitMemory<'_, T>,
        mut write: impl FnMut(&mut T, MemoryIndex, &StaticMemoryInitializer) -> bool,
                        let result = write(state, index, init);
                Some(index) => match &init {
                    } => get_global_as_u64(state, index),
            let cur_size_in_pages = match &init {
                } => memory_size_in_pages(state, memory_index),
            let result = write(state, memory_index, &init);
pub enum InitMemory<'a, T> {
        memory_size_in_pages: &'a dyn Fn(&mut T, MemoryIndex) -> u64,
        get_global_as_u64: &'a dyn Fn(&mut T, GlobalIndex) -> u64,
    unsafe fn vmctx_plus_offset<T>(&self, offset: u32) -> *const T {
        (std::ptr::addr_of!(self.vmctx).cast::<u8>())
            .add(usize::try_from(offset).unwrap())
            .cast()
    }

    unsafe fn vmctx_plus_offset_mut<T>(&mut self, offset: u32) -> *mut T {
        (std::ptr::addr_of_mut!(self.vmctx).cast::<u8>())
    fn table(&mut self, index: DefinedTableIndex) -> VMTableDefinition {
    fn set_table(&mut self, index: DefinedTableIndex, table: VMTableDefinition) {
    fn table_ptr(&mut self, index: DefinedTableIndex) -> *mut VMTableDefinition {
        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_vmtable_definition(index)) }
    fn global(&mut self, index: DefinedGlobalIndex) -> &VMGlobalDefinition {
    fn global_ptr(&mut self, index: DefinedGlobalIndex) -> *mut VMGlobalDefinition {
        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_vmglobal_definition(index)) }
        &mut self,
    pub fn runtime_limits(&mut self) -> *mut *const VMRuntimeLimits {
        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_runtime_limits()) }
    pub fn epoch_ptr(&mut self) -> *mut *const AtomicU64 {
        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_epoch_ptr()) }
    pub fn externref_activations_table(&mut self) -> *mut *mut VMExternRefActivationsTable {
        unsafe { self.vmctx_plus_offset_mut(self.offsets().vmctx_externref_activations_table()) }
            *self.vmctx_plus_offset_mut(self.offsets().vmctx_store()) = store;
            *self.vmctx_plus_offset_mut::<[*mut (); 2]>(self.offsets().vmctx_store()) =
        *self.vmctx_plus_offset_mut(self.offsets().vmctx_callee()) =
    unsafe fn table_index(&mut self, table: &VMTableDefinition) -> DefinedTableIndex {
                *self.vmctx_plus_offset_mut(self.offsets().vmctx_signature_ids_array());
                .vmctx_plus_offset_mut::<VMCallerCheckedFuncRef>(
        *self.vmctx_plus_offset_mut(offsets.vmctx_magic()) = VMCONTEXT_MAGIC;
        *self.vmctx_plus_offset_mut(offsets.vmctx_signature_ids_array()) = signatures.as_ptr();
        *self.vmctx_plus_offset_mut(offsets.vmctx_builtin_functions()) =
            &VMBuiltinFunctionsArray::INIT;
            self.vmctx_plus_offset_mut(offsets.vmctx_imported_functions_begin()),
            self.vmctx_plus_offset_mut(offsets.vmctx_imported_tables_begin()),
            self.vmctx_plus_offset_mut(offsets.vmctx_imported_memories_begin()),
            self.vmctx_plus_offset_mut(offsets.vmctx_imported_globals_begin()),
        let mut ptr = self.vmctx_plus_offset_mut(offsets.vmctx_tables_begin());
        let mut ptr = self.vmctx_plus_offset_mut(offsets.vmctx_memories_begin());
        let mut owned_ptr = self.vmctx_plus_offset_mut(offsets.vmctx_owned_memories_begin());
        let module = self.module().clone();
        for (idx, global) in module.globals.iter() {
            let idx = match module.defined_global_index(idx) {
    pub unsafe fn table_index(&mut self, table: &VMTableDefinition) -> DefinedTableIndex {
        self.instance_mut().table_index(table)
fn get_table_init_start(init: &TableInitializer, instance: &mut Instance) -> Result<u32> {
            let val = unsafe { *(*instance.defined_or_imported_global_ptr(base)).as_u32() };
                let start = get_table_init_start(segment, instance)?;
                    start,
fn get_memory_init_start(init: &MemoryInitializer, instance: &mut Instance) -> Result<u64> {
                let global = instance.defined_or_imported_global_ptr(base);
                    *(*global).as_u64()
                    u64::from(*(*global).as_u32())
fn check_memory_init_bounds(
    instance: &mut Instance,
    initializers: &[MemoryInitializer],
) -> Result<()> {
    let memory_size_in_pages = &|instance: &mut Instance, memory| {
        (instance.get_memory(memory).current_length() as u64) / u64::from(WASM_PAGE_SIZE)
    };
    let get_global_as_u64 = &mut |instance: &mut Instance, global| unsafe {
        let def = instance.defined_or_imported_global_ptr(global);
            *(*def).as_u64()
            u64::from(*(*def).as_u32())
        instance,
        |instance, memory_index, init| {
    match &module.memory_initialization {
    let instance = (*vmctx).instance_mut();
    let instance = (*vmctx).instance_mut();
    let limits = (*caller).instance_mut().runtime_limits();