Skip to content

Commit 1fcd093

Browse files
authored
Prevent using shared memories with Memory (#12022)
* Prevent using shared memories with `Memory` This commit fixes a few issues where it was possible to represent a wasm shared linear memory with the `wasmtime::Memory` type. This is not sound because `wasmtime::Memory` provides safe Rust access to the bytes where that is not possible with wasm shared memories. Shared memories in Rust must be represented by `SharedMemory`, not `wasmtime::Memory`. Specifically this commit prevents two vectors of this happening: 1. `Memory::new` now requires that the memory type specified is non-shared. Instead `SharedMemory::new` must be used instead. 2. Core dumps now skip over shared memories when iterating over all memories in the store. Supporting shared memories is left to a future feature request for now. * CI fixes
1 parent b450924 commit 1fcd093

File tree

13 files changed

+181
-93
lines changed

13 files changed

+181
-93
lines changed

crates/wasmtime/src/runtime/component/instance.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -928,11 +928,11 @@ impl<'a> Instantiator<'a> {
928928
}
929929

930930
fn extract_memory(&mut self, store: &mut StoreOpaque, memory: &ExtractMemory) {
931-
let mem = match lookup_vmexport(store, self.id, &memory.export) {
932-
crate::runtime::vm::Export::Memory { memory, .. } => memory,
931+
let import = match lookup_vmexport(store, self.id, &memory.export) {
932+
crate::runtime::vm::Export::Memory(memory) => memory.vmimport(store),
933+
crate::runtime::vm::Export::SharedMemory(_, import) => import,
933934
_ => unreachable!(),
934935
};
935-
let import = mem.vmimport(store);
936936
self.instance_mut(store)
937937
.set_runtime_memory(memory.index, import.from.as_non_null());
938938
}
@@ -1040,7 +1040,7 @@ impl<'a> Instantiator<'a> {
10401040
return;
10411041
}
10421042

1043-
let val = unsafe { crate::Extern::from_wasmtime_export(export, store) };
1043+
let val = crate::Extern::from_wasmtime_export(export, store);
10441044
let ty = DefinitionType::from(store, &val);
10451045
crate::types::matching::MatchCx::new(module.engine())
10461046
.definition(&expected, &ty)

crates/wasmtime/src/runtime/coredump.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ impl WasmCoreDump {
4141
pub(crate) fn new(store: &mut StoreOpaque, backtrace: WasmBacktrace) -> WasmCoreDump {
4242
let modules: Vec<_> = store.modules().all_modules().cloned().collect();
4343
let instances: Vec<Instance> = store.all_instances().collect();
44-
let store_memories: Vec<Memory> = store.all_memories().collect();
44+
let store_memories: Vec<Memory> =
45+
store.all_memories().filter_map(|m| m.unshared()).collect();
4546

4647
let mut store_globals: Vec<Global> = vec![];
4748
store.for_each_global(|_store, global| store_globals.push(global));
@@ -266,9 +267,13 @@ impl WasmCoreDump {
266267

267268
let memories = instance
268269
.all_memories(store.0)
269-
.collect::<Vec<_>>()
270-
.into_iter()
271-
.map(|(_i, memory)| memory_to_idx[&memory.hash_key(&store.0)])
270+
.filter_map(|(_, m)| m.unshared())
271+
.map(|memory| {
272+
memory_to_idx
273+
.get(&memory.hash_key(&store.0))
274+
.copied()
275+
.unwrap_or(u32::MAX)
276+
})
272277
.collect::<Vec<_>>();
273278

274279
let globals = instance

crates/wasmtime/src/runtime/externals.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,15 @@ impl Extern {
125125
}
126126
}
127127

128-
pub(crate) unsafe fn from_wasmtime_export(
128+
pub(crate) fn from_wasmtime_export(
129129
wasmtime_export: crate::runtime::vm::Export,
130130
store: &StoreOpaque,
131131
) -> Extern {
132132
match wasmtime_export {
133133
crate::runtime::vm::Export::Function(f) => Extern::Func(f),
134-
crate::runtime::vm::Export::Memory { memory, shared } => {
135-
if shared {
136-
Extern::SharedMemory(SharedMemory::from_memory(memory, store))
137-
} else {
138-
Extern::Memory(memory)
139-
}
134+
crate::runtime::vm::Export::Memory(m) => Extern::Memory(m),
135+
crate::runtime::vm::Export::SharedMemory(m, _) => {
136+
Extern::SharedMemory(crate::SharedMemory::from_raw(m, store.engine().clone()))
140137
}
141138
crate::runtime::vm::Export::Global(g) => Extern::Global(g),
142139
crate::runtime::vm::Export::Table(t) => Extern::Table(t),

crates/wasmtime/src/runtime/instance.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ impl Instance {
480480
// SAFETY: the store `id` owns this instance and all exports contained
481481
// within.
482482
let export = unsafe { self.id.get_mut(store).get_export_by_index_mut(id, entity) };
483-
unsafe { Extern::from_wasmtime_export(export, store) }
483+
Extern::from_wasmtime_export(export, store)
484484
}
485485

486486
/// Looks up an exported [`Func`] value by name.
@@ -623,7 +623,7 @@ impl Instance {
623623
pub(crate) fn all_memories<'a>(
624624
&'a self,
625625
store: &'a StoreOpaque,
626-
) -> impl ExactSizeIterator<Item = (MemoryIndex, Memory)> + 'a {
626+
) -> impl ExactSizeIterator<Item = (MemoryIndex, vm::ExportMemory)> + 'a {
627627
let store_id = store.id();
628628
store[self.id].all_memories(store_id)
629629
}
@@ -716,8 +716,11 @@ impl OwnedImports {
716716
crate::runtime::vm::Export::Table(t) => {
717717
self.tables.push(t.vmimport(store));
718718
}
719-
crate::runtime::vm::Export::Memory { memory, .. } => {
720-
self.memories.push(memory.vmimport(store));
719+
crate::runtime::vm::Export::Memory(m) => {
720+
self.memories.push(m.vmimport(store));
721+
}
722+
crate::runtime::vm::Export::SharedMemory(_, vmimport) => {
723+
self.memories.push(*vmimport);
721724
}
722725
crate::runtime::vm::Export::Tag(t) => {
723726
self.tags.push(t.vmimport(store));

crates/wasmtime/src/runtime/memory.rs

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::Trap;
22
use crate::prelude::*;
3-
use crate::runtime::vm::{self, VMStore};
3+
use crate::runtime::vm::{self, ExportMemory, VMStore};
44
use crate::store::{StoreInstanceId, StoreOpaque, StoreResourceLimiter};
55
use crate::trampoline::generate_memory_export;
66
use crate::{AsContext, AsContextMut, Engine, MemoryType, StoreContext, StoreContextMut};
@@ -285,7 +285,13 @@ impl Memory {
285285
limiter: Option<&mut StoreResourceLimiter<'_>>,
286286
ty: MemoryType,
287287
) -> Result<Memory> {
288-
generate_memory_export(store, limiter, &ty, None).await
288+
if ty.is_shared() {
289+
bail!("shared memories must be created through `SharedMemory`")
290+
}
291+
Ok(generate_memory_export(store, limiter, &ty, None)
292+
.await?
293+
.unshared()
294+
.unwrap())
289295
}
290296

291297
/// Returns the underlying type of this memory.
@@ -638,7 +644,14 @@ impl Memory {
638644
}
639645
}
640646

641-
pub(crate) fn from_raw(instance: StoreInstanceId, index: DefinedMemoryIndex) -> Memory {
647+
/// Creates a new memory from its raw component parts.
648+
///
649+
/// # Safety
650+
///
651+
/// The caller must ensure that the memory pointed to by `instance` and
652+
/// `index` is not a shared memory. For that `SharedMemory` must be used
653+
/// instead.
654+
pub(crate) unsafe fn from_raw(instance: StoreInstanceId, index: DefinedMemoryIndex) -> Memory {
642655
Memory { instance, index }
643656
}
644657

@@ -649,12 +662,7 @@ impl Memory {
649662
}
650663

651664
pub(crate) fn vmimport(&self, store: &StoreOpaque) -> crate::runtime::vm::VMMemoryImport {
652-
let instance = &store[self.instance];
653-
crate::runtime::vm::VMMemoryImport {
654-
from: instance.memory_ptr(self.index).into(),
655-
vmctx: instance.vmctx().into(),
656-
index: self.index,
657-
}
665+
store[self.instance].get_defined_memory_vmimport(self.index)
658666
}
659667

660668
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
@@ -804,7 +812,6 @@ pub unsafe trait MemoryCreator: Send + Sync {
804812
pub struct SharedMemory {
805813
vm: crate::runtime::vm::SharedMemory,
806814
engine: Engine,
807-
page_size_log2: u8,
808815
}
809816

810817
impl SharedMemory {
@@ -820,13 +827,11 @@ impl SharedMemory {
820827

821828
let tunables = engine.tunables();
822829
let ty = ty.wasmtime_memory();
823-
let page_size_log2 = ty.page_size_log2;
824830
let memory = crate::runtime::vm::SharedMemory::new(ty, tunables)?;
825831

826832
Ok(Self {
827833
vm: memory,
828834
engine: engine.clone(),
829-
page_size_log2,
830835
})
831836
}
832837

@@ -838,7 +843,7 @@ impl SharedMemory {
838843
/// Returns the size, in WebAssembly pages, of this wasm memory.
839844
pub fn size(&self) -> u64 {
840845
let byte_size = u64::try_from(self.data_size()).unwrap();
841-
let page_size = u64::from(self.page_size());
846+
let page_size = self.page_size();
842847
byte_size / page_size
843848
}
844849

@@ -849,9 +854,8 @@ impl SharedMemory {
849854
/// `1`. Future extensions might allow any power of two as a page size.
850855
///
851856
/// [the custom-page-sizes proposal]: https://github.com/WebAssembly/custom-page-sizes
852-
pub fn page_size(&self) -> u32 {
853-
debug_assert!(self.page_size_log2 == 0 || self.page_size_log2 == 16);
854-
1 << self.page_size_log2
857+
pub fn page_size(&self) -> u64 {
858+
self.ty().page_size()
855859
}
856860

857861
/// Returns the byte length of this memory.
@@ -912,7 +916,7 @@ impl SharedMemory {
912916
Some((old_size, _new_size)) => {
913917
// For shared memory, the `VMMemoryDefinition` is updated inside
914918
// the locked region.
915-
Ok(u64::try_from(old_size).unwrap() / u64::from(self.page_size()))
919+
Ok(u64::try_from(old_size).unwrap() / self.page_size())
916920
}
917921
None => bail!("failed to grow memory by `{delta}`"),
918922
}
@@ -1010,41 +1014,22 @@ impl SharedMemory {
10101014
// Note `vm::assert_ready` shouldn't panic here because this isn't
10111015
// actually allocating any new memory (also no limiter), so resource
10121016
// limiting shouldn't kick in.
1013-
vm::assert_ready(generate_memory_export(
1017+
let memory = vm::assert_ready(generate_memory_export(
10141018
store,
10151019
None,
10161020
&self.ty(),
10171021
Some(&self.vm),
10181022
))
1019-
.unwrap()
1020-
.vmimport(store)
1023+
.unwrap();
1024+
match memory {
1025+
ExportMemory::Unshared(_) => unreachable!(),
1026+
ExportMemory::Shared(_shared, vmimport) => vmimport,
1027+
}
10211028
}
10221029

1023-
/// Create a [`SharedMemory`] from an [`ExportMemory`] definition. This
1024-
/// function is available to handle the case in which a Wasm module exports
1025-
/// shared memory and the user wants host-side access to it.
1026-
pub(crate) fn from_memory(mem: Memory, store: &StoreOpaque) -> Self {
1027-
#![cfg_attr(
1028-
not(feature = "threads"),
1029-
expect(
1030-
unused_variables,
1031-
unreachable_code,
1032-
reason = "definitions cfg'd to dummy",
1033-
)
1034-
)]
1035-
1036-
let instance = mem.instance.get(store);
1037-
let memory = instance.get_defined_memory(mem.index);
1038-
let module = instance.env_module();
1039-
let page_size_log2 = module.memories[module.memory_index(mem.index)].page_size_log2;
1040-
match memory.as_shared_memory() {
1041-
Some(mem) => Self {
1042-
vm: mem.clone(),
1043-
engine: store.engine().clone(),
1044-
page_size_log2,
1045-
},
1046-
None => panic!("unable to convert from a shared memory"),
1047-
}
1030+
/// Creates a [`SharedMemory`] from its constituent parts.
1031+
pub(crate) fn from_raw(vm: crate::runtime::vm::SharedMemory, engine: Engine) -> Self {
1032+
SharedMemory { vm, engine }
10481033
}
10491034
}
10501035

crates/wasmtime/src/runtime/store.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,16 @@ use crate::runtime::vm::GcRootsList;
9797
use crate::runtime::vm::VMContRef;
9898
use crate::runtime::vm::mpk::ProtectionKey;
9999
use crate::runtime::vm::{
100-
self, GcStore, Imports, InstanceAllocationRequest, InstanceAllocator, InstanceHandle,
101-
Interpreter, InterpreterRef, ModuleRuntimeInfo, OnDemandInstanceAllocator, SendSyncPtr,
102-
SignalHandler, StoreBox, Unwind, VMContext, VMFuncRef, VMGcRef, VMStore, VMStoreContext,
100+
self, ExportMemory, GcStore, Imports, InstanceAllocationRequest, InstanceAllocator,
101+
InstanceHandle, Interpreter, InterpreterRef, ModuleRuntimeInfo, OnDemandInstanceAllocator,
102+
SendSyncPtr, SignalHandler, StoreBox, Unwind, VMContext, VMFuncRef, VMGcRef, VMStore,
103+
VMStoreContext,
103104
};
104105
use crate::trampoline::VMHostGlobalContext;
105106
use crate::{Engine, Module, Val, ValRaw, module::ModuleRegistry};
106107
#[cfg(feature = "gc")]
107108
use crate::{ExnRef, Rooted};
108-
use crate::{Global, Instance, Memory, Table, Uninhabited};
109+
use crate::{Global, Instance, Table, Uninhabited};
109110
use alloc::sync::Arc;
110111
use core::fmt;
111112
use core::marker;
@@ -1728,7 +1729,7 @@ impl StoreOpaque {
17281729
}
17291730

17301731
/// Get all memories (host- or Wasm-defined) within this store.
1731-
pub fn all_memories<'a>(&'a self) -> impl Iterator<Item = Memory> + 'a {
1732+
pub fn all_memories<'a>(&'a self) -> impl Iterator<Item = ExportMemory> + 'a {
17321733
// NB: Host-created memories have dummy instances. Therefore, we can get
17331734
// all memories in the store by iterating over all instances (including
17341735
// dummy instances) and getting each of their defined memories.

crates/wasmtime/src/runtime/trampoline.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use self::memory::create_memory;
1414
use self::table::create_table;
1515
use self::tag::create_tag;
1616
use crate::prelude::*;
17-
use crate::runtime::vm::SharedMemory;
17+
use crate::runtime::vm::{ExportMemory, SharedMemory};
1818
use crate::store::{StoreOpaque, StoreResourceLimiter};
1919
use crate::{MemoryType, TableType, TagType};
2020
use wasmtime_environ::{MemoryIndex, TableIndex, TagIndex};
@@ -24,7 +24,7 @@ pub async fn generate_memory_export(
2424
limiter: Option<&mut StoreResourceLimiter<'_>>,
2525
m: &MemoryType,
2626
preallocation: Option<&SharedMemory>,
27-
) -> Result<crate::Memory> {
27+
) -> Result<ExportMemory> {
2828
let id = store.id();
2929
let instance = create_memory(store, limiter, m, preallocation).await?;
3030
Ok(store
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::runtime::vm::{SharedMemory, VMMemoryImport};
2+
13
/// The value of an export passed from one instance to another.
24
pub enum Export {
35
/// A function export value.
@@ -6,12 +8,29 @@ pub enum Export {
68
/// A table export value.
79
Table(crate::Table),
810

9-
/// A memory export value.
10-
Memory { memory: crate::Memory, shared: bool },
11+
/// An unshared memory export value.
12+
Memory(crate::Memory),
13+
14+
/// A shared memory export value.
15+
SharedMemory(SharedMemory, VMMemoryImport),
1116

1217
/// A global export value.
1318
Global(crate::Global),
1419

1520
/// A tag export value.
1621
Tag(crate::Tag),
1722
}
23+
24+
pub enum ExportMemory {
25+
Unshared(crate::Memory),
26+
Shared(SharedMemory, VMMemoryImport),
27+
}
28+
29+
impl ExportMemory {
30+
pub fn unshared(self) -> Option<crate::Memory> {
31+
match self {
32+
ExportMemory::Unshared(m) => Some(m),
33+
ExportMemory::Shared(..) => None,
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)