From c7c1507b8b20c841252043995062f83ef39482df Mon Sep 17 00:00:00 2001 From: John Sharratt's Shared Account Date: Wed, 17 Aug 2022 17:45:48 +1000 Subject: [PATCH 01/10] Implemented LinearMemory trait --- Cargo.lock | 1 + lib/api/src/js/export.rs | 6 + lib/api/src/js/externals/memory.rs | 35 +- lib/api/src/js/function_env.rs | 7 +- lib/api/src/js/imports.rs | 30 ++ lib/api/src/js/mod.rs | 6 + lib/api/src/js/native.rs | 1 - lib/api/src/js/store.rs | 5 + lib/api/src/sys/externals/memory.rs | 16 +- lib/api/src/sys/externals/memory_view.rs | 2 +- lib/api/src/sys/mod.rs | 2 +- .../src/translator/code_translator.rs | 7 +- lib/compiler/src/engine/resolver.rs | 2 +- lib/compiler/src/translator/environ.rs | 2 +- lib/types/Cargo.toml | 3 + lib/types/src/error.rs | 48 +- lib/types/src/lib.rs | 6 +- lib/types/src/memory.rs | 88 +++- lib/vm/src/instance/allocator.rs | 4 +- lib/vm/src/instance/mod.rs | 16 +- lib/vm/src/lib.rs | 7 +- lib/vm/src/memory.rs | 423 +++++++++++------- lib/vm/src/store.rs | 20 + lib/vm/src/vmcontext.rs | 157 +++---- 24 files changed, 579 insertions(+), 315 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9328944bd83..04df81af3cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3194,6 +3194,7 @@ dependencies = [ "enum-iterator", "enumset", "indexmap", + "memoffset", "more-asserts", "rkyv", "serde", diff --git a/lib/api/src/js/export.rs b/lib/api/src/js/export.rs index f396e617c53..0241899019f 100644 --- a/lib/api/src/js/export.rs +++ b/lib/api/src/js/export.rs @@ -7,6 +7,7 @@ use std::fmt; use wasm_bindgen::{JsCast, JsValue}; use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType}; +/// Represents linear memory that is managed by the javascript runtime #[derive(Clone, Debug, PartialEq)] pub struct VMMemory { pub(crate) memory: Memory, @@ -20,6 +21,11 @@ impl VMMemory { pub(crate) fn new(memory: Memory, ty: MemoryType) -> Self { Self { memory, ty } } + + /// Attempts to clone this memory (if its clonable) + pub(crate) fn try_clone(&self) -> Option { + Some(self.clone()) + } } #[derive(Clone, Debug, PartialEq)] diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 6c5229ce92f..2b66d8ed79b 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -6,7 +6,6 @@ use crate::js::{MemoryAccessError, MemoryType}; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::slice; -use thiserror::Error; #[cfg(feature = "tracing")] use tracing::warn; @@ -16,22 +15,7 @@ use wasmer_types::Pages; use super::MemoryView; -/// Error type describing things that can go wrong when operating on Wasm Memories. -#[derive(Error, Debug, Clone, PartialEq, Hash)] -pub enum MemoryError { - /// The operation would cause the size of the memory to exceed the maximum or would cause - /// an overflow leading to unindexable memory. - #[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)] - CouldNotGrow { - /// The current size in pages. - current: Pages, - /// The attempted amount to grow by in pages. - attempted_delta: Pages, - }, - /// A user defined error value, used for error cases not listed above. - #[error("A user-defined error occurred: {0}")] - Generic(String), -} +pub use wasmer_types::MemoryError; #[wasm_bindgen] extern "C" { @@ -116,6 +100,17 @@ impl Memory { Ok(Self::from_vm_export(store, vm_memory)) } + /// Creates a new host `Memory` from provided JavaScript memory. + pub fn new_raw(store: &mut impl AsStoreMut, js_memory: js_sys::WebAssembly::Memory, ty: MemoryType) -> Result { + let vm_memory = VMMemory::new(js_memory, ty); + Ok(Self::from_vm_export(store, vm_memory)) + } + + /// Create a memory object from an existing memory and attaches it to the store + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self::from_vm_export(new_store, memory) + } + /// Returns the [`MemoryType`] of the `Memory`. /// /// # Example @@ -210,6 +205,12 @@ impl Memory { } } + /// Attempts to clone this memory (if its clonable) + pub fn try_clone(&self, store: &impl AsStoreRef) -> Option { + let mem = self.handle.get(store.as_store_ref().objects()); + mem.try_clone() + } + /// Checks whether this `Global` can be used with the given context. pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() diff --git a/lib/api/src/js/function_env.rs b/lib/api/src/js/function_env.rs index 1705f1a4713..524fc7a2dbe 100644 --- a/lib/api/src/js/function_env.rs +++ b/lib/api/src/js/function_env.rs @@ -36,7 +36,7 @@ impl FunctionEnv { } /// Get the data as reference - pub fn as_ref<'a>(&self, store: &'a impl AsStoreMut) -> &'a T + pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T where T: Any + Send + 'static + Sized, { @@ -112,6 +112,11 @@ impl FunctionEnvMut<'_, T> { self.func_env.as_mut(&mut self.store_mut) } + /// Borrows a new immmutable reference + pub fn as_ref(&self) -> FunctionEnv { + self.func_env.clone() + } + /// Borrows a new mutable reference pub fn as_mut<'a>(&'a mut self) -> FunctionEnvMut<'a, T> { FunctionEnvMut { diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs index 95a9930995c..d3b2c42efa5 100644 --- a/lib/api/src/js/imports.rs +++ b/lib/api/src/js/imports.rs @@ -174,6 +174,36 @@ impl Imports { } imports } + + /// Iterates through all the imports in this structure + pub fn iter<'a>(&'a self) -> ImportsIterator<'a> { + ImportsIterator::new(self) + } +} + +pub struct ImportsIterator<'a> { + iter: std::collections::hash_map::Iter<'a, (String, String), Extern> +} + +impl<'a> ImportsIterator<'a> +{ + fn new(imports: &'a Imports) -> Self { + let iter = imports.map.iter(); + Self { iter } + } +} + +impl<'a> Iterator +for ImportsIterator<'a> { + type Item = (&'a str, &'a str, &'a Extern); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|(k, v)| { + (k.0.as_str(), k.1.as_str(), v) + }) + } } impl IntoIterator for &Imports { diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 14594839a15..a88509086e0 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -73,6 +73,12 @@ pub use crate::js::types::{ pub use crate::js::value::Value; pub use crate::js::value::Value as Val; +pub mod vm { + //! The `vm` module re-exports wasmer-vm types. + + pub use crate::js::export::VMMemory; +} + pub use wasmer_types::is_wasm; pub use wasmer_types::{ Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES, diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index 0e9066edac8..69b350652a1 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -11,7 +11,6 @@ use std::marker::PhantomData; use crate::js::externals::Function; use crate::js::store::{AsStoreMut, AsStoreRef, StoreHandle}; -use crate::js::FunctionEnv; use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList}; // use std::panic::{catch_unwind, AssertUnwindSafe}; use crate::js::export::VMFunction; diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index b6c10e84627..66d6d58ec8c 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -263,6 +263,11 @@ mod objects { self.id } + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + /// Returns a pair of mutable references from two handles. /// /// Panics if both handles point to the same object. diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index c0db55fa8af..64ab9a136e2 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -10,7 +10,7 @@ use std::mem::MaybeUninit; use std::slice; #[cfg(feature = "tracing")] use tracing::warn; -use wasmer_types::Pages; +use wasmer_types::{Pages, LinearMemory}; use wasmer_vm::{InternalStoreHandle, MemoryError, StoreHandle, VMExtern, VMMemory}; use super::MemoryView; @@ -60,6 +60,13 @@ impl Memory { }) } + /// Create a memory object from an existing memory and attaches it to the store + pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { + Self { + handle: StoreHandle::new(new_store.objects_mut(), memory) + } + } + /// Returns the [`MemoryType`] of the `Memory`. /// /// # Example @@ -142,6 +149,13 @@ impl Memory { self.handle.store_id() == store.as_store_ref().objects().id() } + /// Attempts to clone this memory (if its clonable) + pub fn try_clone(&self, store: &impl AsStoreRef) -> Option { + let mem = self.handle.get(store.as_store_ref().objects()); + mem.try_clone() + .map(|mem| mem.into()) + } + pub(crate) fn to_vm_extern(&self) -> VMExtern { VMExtern::Memory(self.handle.internal_handle()) } diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index 891700c0854..a638acb6b97 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -4,7 +4,7 @@ use std::convert::TryInto; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::slice; -use wasmer_types::Pages; +use wasmer_types::{Pages, LinearMemory}; use super::memory::MemoryBuffer; use super::Memory; diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 1c272a2a60e..b6f9088c30d 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -58,7 +58,7 @@ pub mod vm { pub use wasmer_vm::{ MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, VMTable, - VMTableDefinition, + VMTableDefinition, }; } diff --git a/lib/compiler-cranelift/src/translator/code_translator.rs b/lib/compiler-cranelift/src/translator/code_translator.rs index c750d6c3231..60da48347ae 100644 --- a/lib/compiler-cranelift/src/translator/code_translator.rs +++ b/lib/compiler-cranelift/src/translator/code_translator.rs @@ -1071,8 +1071,7 @@ pub fn translate_operator( expected, timeout, )?; - state.push1(res); - } + state.push1(res); } Operator::MemoryAtomicNotify { memarg } => { let heap_index = MemoryIndex::from_u32(memarg.memory); let heap = state.get_heap(builder.func, memarg.memory, environ)?; @@ -1080,8 +1079,8 @@ pub fn translate_operator( let addr = state.pop1(); // 32 (fixed) let addr = fold_atomic_mem_addr(addr, memarg, I32, builder); let res = - environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; - state.push1(res); + environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; + state.push1(res); } Operator::I32AtomicLoad { memarg } => { translate_atomic_load(I32, I32, memarg, builder, state, environ)? diff --git a/lib/compiler/src/engine/resolver.rs b/lib/compiler/src/engine/resolver.rs index 207380bb819..94d40a50229 100644 --- a/lib/compiler/src/engine/resolver.rs +++ b/lib/compiler/src/engine/resolver.rs @@ -4,7 +4,7 @@ use crate::LinkError; use more_asserts::assert_ge; use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ - ExternType, FunctionIndex, ImportError, ImportIndex, MemoryIndex, ModuleInfo, TableIndex, + ExternType, FunctionIndex, ImportError, ImportIndex, MemoryIndex, ModuleInfo, TableIndex, LinearMemory, }; use wasmer_vm::{ diff --git a/lib/compiler/src/translator/environ.rs b/lib/compiler/src/translator/environ.rs index e172d92b063..cd00a7bc01f 100644 --- a/lib/compiler/src/translator/environ.rs +++ b/lib/compiler/src/translator/environ.rs @@ -1,8 +1,8 @@ // This file contains code from external sources. // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md use super::state::ModuleTranslationState; -use crate::lib::std::borrow::ToOwned; use crate::lib::std::string::ToString; +use crate::lib::std::borrow::ToOwned; use crate::lib::std::{boxed::Box, string::String, vec::Vec}; use crate::translate_module; use crate::wasmparser::{Operator, Range, Type}; diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index e10aebf87df..3ee22d50426 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -21,6 +21,9 @@ enum-iterator = "0.7.0" target-lexicon = { version = "0.12.2", default-features = false } enumset = "1.0" +[dev-dependencies] +memoffset = "0.6" + [features] default = ["std"] std = [] diff --git a/lib/types/src/error.rs b/lib/types/src/error.rs index 53555d6239c..0e0a1eb12fd 100644 --- a/lib/types/src/error.rs +++ b/lib/types/src/error.rs @@ -1,5 +1,5 @@ //! The WebAssembly possible errors -use crate::ExternType; +use crate::{ExternType, Pages}; use std::io; use thiserror::Error; @@ -37,6 +37,48 @@ pub enum DeserializeError { Compiler(#[from] CompileError), } +/// Error type describing things that can go wrong when operating on Wasm Memories. +#[derive(Error, Debug, Clone, PartialEq, Hash)] +pub enum MemoryError { + /// Low level error with mmap. + #[error("Error when allocating memory: {0}")] + Region(String), + /// The operation would cause the size of the memory to exceed the maximum or would cause + /// an overflow leading to unindexable memory. + #[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)] + CouldNotGrow { + /// The current size in pages. + current: Pages, + /// The attempted amount to grow by in pages. + attempted_delta: Pages, + }, + /// The operation would cause the size of the memory size exceed the maximum. + #[error("The memory is invalid because {}", reason)] + InvalidMemory { + /// The reason why the provided memory is invalid. + reason: String, + }, + /// Caller asked for more minimum memory than we can give them. + #[error("The minimum requested ({} pages) memory is greater than the maximum allowed memory ({} pages)", min_requested.0, max_allowed.0)] + MinimumMemoryTooLarge { + /// The number of pages requested as the minimum amount of memory. + min_requested: Pages, + /// The maximum amount of memory we can allocate. + max_allowed: Pages, + }, + /// Caller asked for a maximum memory greater than we can give them. + #[error("The maximum requested memory ({} pages) is greater than the maximum allowed memory ({} pages)", max_requested.0, max_allowed.0)] + MaximumMemoryTooLarge { + /// The number of pages requested as the maximum amount of memory. + max_requested: Pages, + /// The number of pages requested as the maximum amount of memory. + max_allowed: Pages, + }, + /// A user defined error value, used for error cases not listed above. + #[error("A user-defined error occurred: {0}")] + Generic(String), +} + /// An ImportError. /// /// Note: this error is not standard to WebAssembly, but it's @@ -52,6 +94,10 @@ pub enum ImportError { /// This error occurs when an import was expected but not provided. #[error("unknown import. Expected {0:?}")] UnknownImport(ExternType), + + /// Memory Error + #[error("memory error. {0}")] + MemoryError(String), } /// An error while preinstantiating a module. diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 3fcfbe9504d..3f71079c94c 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -77,7 +77,7 @@ pub use crate::compilation::target::{ pub use crate::serialize::{MetadataHeader, SerializableCompilation, SerializableModule}; pub use error::{ CompileError, DeserializeError, ImportError, MiddlewareError, ParseCpuFeatureError, - PreInstantiationError, SerializeError, WasmError, WasmResult, + PreInstantiationError, SerializeError, WasmError, WasmResult, MemoryError, }; /// The entity module, with common helpers for Rust structures @@ -103,7 +103,9 @@ pub use types::{ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; -pub use crate::memory::MemoryStyle; +pub use crate::memory::{ + MemoryStyle, LinearMemory, VMMemoryDefinition +}; pub use crate::table::TableStyle; pub use crate::trapcode::TrapCode; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index fafcb955a8a..6ebac15e7e8 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -2,12 +2,16 @@ use crate::{Pages, ValueType}; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; +use core::ptr::NonNull; use std::convert::{TryFrom, TryInto}; use std::iter::Sum; use std::ops::{Add, AddAssign}; +use super::MemoryType; +use super::MemoryError; + /// Implementation styles for WebAssembly linear memory. -#[derive(Debug, Clone, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] pub enum MemoryStyle { @@ -84,6 +88,9 @@ pub unsafe trait MemorySize: Copy { /// Zero value used for `WasmPtr::is_null`. const ZERO: Self::Offset; + /// One value used for counting. + const ONE: Self::Offset; + /// Convert an `Offset` to a `Native`. fn offset_to_native(offset: Self::Offset) -> Self::Native; @@ -98,6 +105,7 @@ unsafe impl MemorySize for Memory32 { type Offset = u32; type Native = i32; const ZERO: Self::Offset = 0; + const ONE: Self::Offset = 1; fn offset_to_native(offset: Self::Offset) -> Self::Native { offset as Self::Native } @@ -113,6 +121,7 @@ unsafe impl MemorySize for Memory64 { type Offset = u64; type Native = i64; const ZERO: Self::Offset = 0; + const ONE: Self::Offset = 1; fn offset_to_native(offset: Self::Offset) -> Self::Native { offset as Self::Native } @@ -120,3 +129,80 @@ unsafe impl MemorySize for Memory64 { native as Self::Offset } } + +/// Represents memory that is used by the WebAsssembly module +pub trait LinearMemory +where Self: std::fmt::Debug + Send +{ + /// Returns the type for this memory. + fn ty(&self) -> MemoryType; + + /// Returns the size of hte memory in pages + fn size(&self) -> Pages; + + /// Returns the memory style for this memory. + fn style(&self) -> MemoryStyle; + + /// Grow memory by the specified amount of wasm pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of wasm pages. + fn grow(&mut self, delta: Pages) -> Result; + + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + fn vmmemory(&self) -> NonNull; + + /// Attempts to clone this memory (if its clonable) + fn try_clone(&self) -> Option>; +} + +/// The fields compiled code needs to access to utilize a WebAssembly linear +/// memory defined within the instance, namely the start address and the +/// size in bytes. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMMemoryDefinition { + /// The start address which is always valid, even if the memory grows. + pub base: *mut u8, + + /// The current logical size of this linear memory in bytes. + pub current_length: usize, +} + +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. +unsafe impl Send for VMMemoryDefinition {} +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. And it's `Copy` so there's +/// really no difference between passing it by reference or by value as far as +/// correctness in a multi-threaded context is concerned. +unsafe impl Sync for VMMemoryDefinition {} + +#[cfg(test)] +mod test_vmmemory_definition { + use super::VMMemoryDefinition; + use crate::VMOffsets; + use memoffset::offset_of; + use std::mem::size_of; + use crate::ModuleInfo; + + #[test] + fn check_vmmemory_definition_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmmemory_definition()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, base), + usize::from(offsets.vmmemory_definition_base()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, current_length), + usize::from(offsets.vmmemory_definition_current_length()) + ); + } +} diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index 29804c7460e..e00625f5e8c 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -1,11 +1,11 @@ use super::{Instance, InstanceHandle}; -use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition}; +use crate::vmcontext::VMTableDefinition; use std::alloc::{self, Layout}; use std::convert::TryFrom; use std::mem; use std::ptr::{self, NonNull}; use wasmer_types::entity::EntityRef; -use wasmer_types::VMOffsets; +use wasmer_types::{VMOffsets, VMMemoryDefinition}; use wasmer_types::{LocalMemoryIndex, LocalTableIndex, ModuleInfo}; /// This is an intermediate type that manages the raw allocation and diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index bf0be8a295b..ab11b2b5501 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -10,14 +10,13 @@ mod allocator; use crate::export::VMExtern; use crate::imports::Imports; -use crate::memory::MemoryError; use crate::store::{InternalStoreHandle, StoreObjects}; use crate::table::TableElement; use crate::trap::{catch_traps, Trap, TrapCode}; use crate::vmcontext::{ VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionContext, - VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, - VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, + VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, + VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, memory_copy, memory_fill, }; use crate::{FunctionBodyPtr, MaybeInstanceOwned, TrapHandlerFn, VMFunctionBody}; use crate::{VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable}; @@ -37,7 +36,8 @@ use wasmer_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, use wasmer_types::{ DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, - ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, VMOffsets, + ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, VMOffsets, LinearMemory, + MemoryError, VMMemoryDefinition }; /// A WebAssembly instance. @@ -632,7 +632,7 @@ impl Instance { let memory = self.memory(memory_index); // The following memory copy is not synchronized and is not atomic: - unsafe { memory.memory_copy(dst, src, len) } + unsafe { memory_copy(&memory, dst, src, len) } } /// Perform a `memory.copy` on an imported memory. @@ -646,7 +646,7 @@ impl Instance { let import = self.imported_memory(memory_index); let memory = unsafe { import.definition.as_ref() }; // The following memory copy is not synchronized and is not atomic: - unsafe { memory.memory_copy(dst, src, len) } + unsafe { memory_copy(memory, dst, src, len) } } /// Perform the `memory.fill` operation on a locally defined memory. @@ -663,7 +663,7 @@ impl Instance { ) -> Result<(), Trap> { let memory = self.memory(memory_index); // The following memory fill is not synchronized and is not atomic: - unsafe { memory.memory_fill(dst, val, len) } + unsafe { memory_fill(&memory, dst, val, len) } } /// Perform the `memory.fill` operation on an imported memory. @@ -681,7 +681,7 @@ impl Instance { let import = self.imported_memory(memory_index); let memory = unsafe { import.definition.as_ref() }; // The following memory fill is not synchronized and is not atomic: - unsafe { memory.memory_fill(dst, val, len) } + unsafe { memory_fill(memory, dst, val, len) } } /// Performs the `memory.init` operation. diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 1b30f1e61e4..88b209a241d 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -45,7 +45,8 @@ pub use crate::function_env::VMFunctionEnvironment; pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{InstanceAllocator, InstanceHandle}; -pub use crate::memory::{MemoryError, VMMemory}; +pub use crate::memory::VMMemory; +pub use wasmer_types::MemoryError; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; @@ -56,11 +57,11 @@ pub use crate::table::{TableElement, VMTable}; pub use crate::trap::*; pub use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionContext, - VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, + VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; pub use wasmer_types::LibCall; -pub use wasmer_types::MemoryStyle; +pub use wasmer_types::{MemoryStyle, VMMemoryDefinition}; use wasmer_types::RawValue; pub use wasmer_types::TableStyle; pub use wasmer_types::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 787d5f390b5..265c5c259b6 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -5,88 +5,160 @@ //! //! `Memory` is to WebAssembly linear memories what `Table` is to WebAssembly tables. -use crate::vmcontext::VMMemoryDefinition; use crate::{mmap::Mmap, store::MaybeInstanceOwned}; use more_asserts::assert_ge; use std::cell::UnsafeCell; use std::convert::TryInto; use std::ptr::NonNull; -use thiserror::Error; -use wasmer_types::{Bytes, MemoryStyle, MemoryType, Pages}; - -/// Error type describing things that can go wrong when operating on Wasm Memories. -#[derive(Error, Debug, Clone, Eq, PartialEq, Hash)] -pub enum MemoryError { - /// Low level error with mmap. - #[error("Error when allocating memory: {0}")] - Region(String), - /// The operation would cause the size of the memory to exceed the maximum or would cause - /// an overflow leading to unindexable memory. - #[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)] - CouldNotGrow { - /// The current size in pages. - current: Pages, - /// The attempted amount to grow by in pages. - attempted_delta: Pages, - }, - /// The operation would cause the size of the memory size exceed the maximum. - #[error("The memory is invalid because {}", reason)] - InvalidMemory { - /// The reason why the provided memory is invalid. - reason: String, - }, - /// Caller asked for more minimum memory than we can give them. - #[error("The minimum requested ({} pages) memory is greater than the maximum allowed memory ({} pages)", min_requested.0, max_allowed.0)] - MinimumMemoryTooLarge { - /// The number of pages requested as the minimum amount of memory. - min_requested: Pages, - /// The maximum amount of memory we can allocate. - max_allowed: Pages, - }, - /// Caller asked for a maximum memory greater than we can give them. - #[error("The maximum requested memory ({} pages) is greater than the maximum allowed memory ({} pages)", max_requested.0, max_allowed.0)] - MaximumMemoryTooLarge { - /// The number of pages requested as the maximum amount of memory. - max_requested: Pages, - /// The number of pages requested as the maximum amount of memory. - max_allowed: Pages, - }, - /// A user defined error value, used for error cases not listed above. - #[error("A user-defined error occurred: {0}")] - Generic(String), +use wasmer_types::{Bytes, MemoryStyle, MemoryType, Pages, MemoryError, LinearMemory, VMMemoryDefinition}; + +// The memory mapped area +#[derive(Debug)] +struct WasmMmap { + // Our OS allocation of mmap'd memory. + alloc: Mmap, + // The current logical size in wasm pages of this linear memory. + size: Pages, + /// The owned memory definition used by the generated code + vm_memory_definition: MaybeInstanceOwned, } -/// A linear memory instance. -pub struct VMMemory { - // The underlying allocation. - mmap: WasmMmap, +impl WasmMmap +{ + fn get_vm_memory_definition(&self) -> NonNull { + self.vm_memory_definition.as_ptr() + } + + fn size(&self) -> Pages { + unsafe { + let md_ptr = self.get_vm_memory_definition(); + let md = md_ptr.as_ref(); + Bytes::from(md.current_length).try_into().unwrap() + } + } + + fn grow(&mut self, delta: Pages, conf: VMMemoryConfig) -> Result { + // Optimization of memory.grow 0 calls. + if delta.0 == 0 { + return Ok(self.size); + } + + let new_pages = self + .size + .checked_add(delta) + .ok_or(MemoryError::CouldNotGrow { + current: self.size, + attempted_delta: delta, + })?; + let prev_pages = self.size; + + if let Some(maximum) = conf.maximum { + if new_pages > maximum { + return Err(MemoryError::CouldNotGrow { + current: self.size, + attempted_delta: delta, + }); + } + } + + // Wasm linear memories are never allowed to grow beyond what is + // indexable. If the memory has no maximum, enforce the greatest + // limit here. + if new_pages >= Pages::max_value() { + // Linear memory size would exceed the index range. + return Err(MemoryError::CouldNotGrow { + current: self.size, + attempted_delta: delta, + }); + } + + let delta_bytes = delta.bytes().0; + let prev_bytes = prev_pages.bytes().0; + let new_bytes = new_pages.bytes().0; + + if new_bytes > self.alloc.len() - conf.offset_guard_size { + // If the new size is within the declared maximum, but needs more memory than we + // have on hand, it's a dynamic heap and it can move. + let guard_bytes = conf.offset_guard_size; + let request_bytes = + new_bytes + .checked_add(guard_bytes) + .ok_or_else(|| MemoryError::CouldNotGrow { + current: new_pages, + attempted_delta: Bytes(guard_bytes).try_into().unwrap(), + })?; + let mut new_mmap = + Mmap::accessible_reserved(new_bytes, request_bytes).map_err(MemoryError::Region)?; + + let copy_len = self.alloc.len() - conf.offset_guard_size; + new_mmap.as_mut_slice()[..copy_len] + .copy_from_slice(&self.alloc.as_slice()[..copy_len]); + + self.alloc = new_mmap; + } else if delta_bytes > 0 { + // Make the newly allocated pages accessible. + self + .alloc + .make_accessible(prev_bytes, delta_bytes) + .map_err(MemoryError::Region)?; + } + + self.size = new_pages; + + // update memory definition + unsafe { + let mut md_ptr = self.vm_memory_definition.as_ptr(); + let md = md_ptr.as_mut(); + md.current_length = new_pages.bytes().0; + md.base = self.alloc.as_mut_ptr() as _; + } + + Ok(prev_pages) + } +} + +/// A linear memory instance. +#[derive(Debug, Clone)] +struct VMMemoryConfig { // The optional maximum size in wasm pages of this linear memory. maximum: Option, - /// The WebAssembly linear memory description. memory: MemoryType, - /// Our chosen implementation style. style: MemoryStyle, - // Size in bytes of extra guard pages after the end to optimize loads and stores with // constant offsets. offset_guard_size: usize, +} - /// The owned memory definition used by the generated code - vm_memory_definition: MaybeInstanceOwned, +impl VMMemoryConfig +{ + fn ty(&self, minimum: Pages) -> MemoryType { + let mut out = self.memory; + out.minimum = minimum; + + out + } + + fn style(&self) -> MemoryStyle { + self.style + } } +/// A linear memory instance. #[derive(Debug)] -struct WasmMmap { - // Our OS allocation of mmap'd memory. - alloc: Mmap, - // The current logical size in wasm pages of this linear memory. - size: Pages, +pub struct VMOwnedMemory { + // The underlying allocation. + mmap: WasmMmap, + // Configuration of this memory + config: VMMemoryConfig, } -impl VMMemory { +unsafe impl Send for VMOwnedMemory { } +unsafe impl Sync for VMOwnedMemory { } + +impl VMOwnedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// /// This creates a `Memory` with owned metadata: this can be used to create a memory @@ -154,18 +226,11 @@ impl VMMemory { let mapped_pages = memory.minimum; let mapped_bytes = mapped_pages.bytes(); - let mut mmap = WasmMmap { - alloc: Mmap::accessible_reserved(mapped_bytes.0, request_bytes) - .map_err(MemoryError::Region)?, - size: memory.minimum, - }; - - let base_ptr = mmap.alloc.as_mut_ptr(); + let mut alloc = Mmap::accessible_reserved(mapped_bytes.0, request_bytes) + .map_err(MemoryError::Region)?; + let base_ptr = alloc.as_mut_ptr(); let mem_length = memory.minimum.bytes().0; - Ok(Self { - mmap, - maximum: memory.maximum, - offset_guard_size: offset_guard_bytes, + let mmap = WasmMmap { vm_memory_definition: if let Some(mem_loc) = vm_memory_location { { let mut ptr = mem_loc; @@ -180,127 +245,155 @@ impl VMMemory { current_length: mem_length, }))) }, - memory: *memory, - style: style.clone(), + alloc, + size: memory.minimum, + }; + + Ok(Self { + mmap: mmap, + config: VMMemoryConfig { + maximum: memory.maximum, + offset_guard_size: offset_guard_bytes, + memory: *memory, + style: style.clone(), + } }) } +} - /// Get the `VMMemoryDefinition`. - fn get_vm_memory_definition(&self) -> NonNull { - self.vm_memory_definition.as_ptr() - } - +impl LinearMemory +for VMOwnedMemory +{ /// Returns the type for this memory. - pub fn ty(&self) -> MemoryType { - let minimum = self.size(); - let mut out = self.memory; - out.minimum = minimum; - - out + fn ty(&self) -> MemoryType { + let minimum = self.mmap.size(); + self.config.ty(minimum) } - /// Returns the memory style for this memory. - pub fn style(&self) -> &MemoryStyle { - &self.style + /// Returns the size of hte memory in pages + fn size(&self) -> Pages { + self.mmap.size() } - /// Returns the number of allocated wasm pages. - pub fn size(&self) -> Pages { - // TODO: investigate this function for race conditions - unsafe { - let md_ptr = self.get_vm_memory_definition(); - let md = md_ptr.as_ref(); - Bytes::from(md.current_length).try_into().unwrap() - } + /// Returns the memory style for this memory. + fn style(&self) -> MemoryStyle { + self.config.style() } /// Grow memory by the specified amount of wasm pages. /// /// Returns `None` if memory can't be grown by the specified amount /// of wasm pages. - pub fn grow(&mut self, delta: Pages) -> Result { - // Optimization of memory.grow 0 calls. - if delta.0 == 0 { - return Ok(self.mmap.size); - } + fn grow(&mut self, delta: Pages) -> Result { + self.mmap.grow(delta, self.config.clone()) + } - let new_pages = self - .mmap - .size - .checked_add(delta) - .ok_or(MemoryError::CouldNotGrow { - current: self.mmap.size, - attempted_delta: delta, - })?; - let prev_pages = self.mmap.size; + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + fn vmmemory(&self) -> NonNull { + self.mmap.vm_memory_definition.as_ptr() + } - if let Some(maximum) = self.maximum { - if new_pages > maximum { - return Err(MemoryError::CouldNotGrow { - current: self.mmap.size, - attempted_delta: delta, - }); - } - } + /// Owned memory can not be cloned (this will always return None) + fn try_clone(&self) -> Option> { + None + } +} - // Wasm linear memories are never allowed to grow beyond what is - // indexable. If the memory has no maximum, enforce the greatest - // limit here. - if new_pages >= Pages::max_value() { - // Linear memory size would exceed the index range. - return Err(MemoryError::CouldNotGrow { - current: self.mmap.size, - attempted_delta: delta, - }); - } +impl Into +for VMOwnedMemory +{ + fn into(self) -> VMMemory { + VMMemory(Box::new(self)) + } +} - let delta_bytes = delta.bytes().0; - let prev_bytes = prev_pages.bytes().0; - let new_bytes = new_pages.bytes().0; +/// Represents linear memory that can be either owned or shared +#[derive(Debug)] +pub struct VMMemory(Box); - if new_bytes > self.mmap.alloc.len() - self.offset_guard_size { - // If the new size is within the declared maximum, but needs more memory than we - // have on hand, it's a dynamic heap and it can move. - let guard_bytes = self.offset_guard_size; - let request_bytes = - new_bytes - .checked_add(guard_bytes) - .ok_or_else(|| MemoryError::CouldNotGrow { - current: new_pages, - attempted_delta: Bytes(guard_bytes).try_into().unwrap(), - })?; +impl Into +for Box +{ + fn into(self) -> VMMemory { + VMMemory(self) + } +} - let mut new_mmap = - Mmap::accessible_reserved(new_bytes, request_bytes).map_err(MemoryError::Region)?; +impl LinearMemory +for VMMemory +{ + /// Returns the type for this memory. + fn ty(&self) -> MemoryType { + self.0.ty() + } - let copy_len = self.mmap.alloc.len() - self.offset_guard_size; - new_mmap.as_mut_slice()[..copy_len] - .copy_from_slice(&self.mmap.alloc.as_slice()[..copy_len]); + /// Returns the size of hte memory in pages + fn size(&self) -> Pages { + self.0.size() + } - self.mmap.alloc = new_mmap; - } else if delta_bytes > 0 { - // Make the newly allocated pages accessible. - self.mmap - .alloc - .make_accessible(prev_bytes, delta_bytes) - .map_err(MemoryError::Region)?; - } + /// Grow memory by the specified amount of wasm pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of wasm pages. + fn grow(&mut self, delta: Pages) -> Result { + self.0.grow(delta) + } + + /// Returns the memory style for this memory. + fn style(&self) -> MemoryStyle { + self.0.style() + } - self.mmap.size = new_pages; + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + fn vmmemory(&self) -> NonNull { + self.0.vmmemory() + } - // update memory definition - unsafe { - let mut md_ptr = self.get_vm_memory_definition(); - let md = md_ptr.as_mut(); - md.current_length = new_pages.bytes().0; - md.base = self.mmap.alloc.as_mut_ptr() as _; - } + /// Attempts to clone this memory (if its clonable) + fn try_clone(&self) -> Option> { + self.0.try_clone() + } +} - Ok(prev_pages) +impl VMMemory +{ + /// Creates a new linear memory instance of the correct type with specified + /// minimum and maximum number of wasm pages. + /// + /// This creates a `Memory` with owned metadata: this can be used to create a memory + /// that will be imported into Wasm modules. + pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { + Ok( + Self(Box::new(VMOwnedMemory::new(memory, style)?)) + ) } - /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. - pub fn vmmemory(&self) -> NonNull { - self.get_vm_memory_definition() + /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. + /// + /// This creates a `Memory` with metadata owned by a VM, pointed to by + /// `vm_memory_location`: this can be used to create a local memory. + /// + /// # Safety + /// - `vm_memory_location` must point to a valid location in VM memory. + pub unsafe fn from_definition( + memory: &MemoryType, + style: &MemoryStyle, + vm_memory_location: NonNull, + ) -> Result { + Ok( + Self(Box::new(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?)) + ) + } + + /// Creates VMMemory from a custom implementation - the following into implementations + /// are natively supported + /// - VMOwnedMemory -> VMMemory + /// - VMSharedMemory -> VMMemory + /// - Box -> VMMemory + pub fn from_custom(memory: IntoVMMemory) -> VMMemory + where IntoVMMemory: Into + { + memory.into() } } diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 4faee579a4d..25afdb7c1c5 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -266,3 +266,23 @@ impl MaybeInstanceOwned { } } } + +impl std::fmt::Debug +for MaybeInstanceOwned +where T: std::fmt::Debug +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + MaybeInstanceOwned::Host(p) => { + write!(f, "host(")?; + p.as_ref().fmt(f)?; + write!(f, ")") + }, + MaybeInstanceOwned::Instance(p) => { + write!(f, "instance(")?; + unsafe { p.as_ref().fmt(f)? }; + write!(f, ")") + } + } + } +} \ No newline at end of file diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index bb1cb6cc9fc..6940f43a265 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -15,7 +15,7 @@ use crate::{VMBuiltinFunctionIndex, VMFunction}; use std::convert::TryFrom; use std::ptr::{self, NonNull}; use std::u32; -use wasmer_types::RawValue; +use wasmer_types::{RawValue, VMMemoryDefinition}; /// Union representing the first parameter passed when calling a function. /// @@ -303,120 +303,67 @@ mod test_vmglobal_import { } } -/// The fields compiled code needs to access to utilize a WebAssembly linear -/// memory defined within the instance, namely the start address and the -/// size in bytes. -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct VMMemoryDefinition { - /// The start address which is always valid, even if the memory grows. - pub base: *mut u8, - - /// The current logical size of this linear memory in bytes. - pub current_length: usize, -} - -/// # Safety -/// This data is safe to share between threads because it's plain data that -/// is the user's responsibility to synchronize. -unsafe impl Send for VMMemoryDefinition {} +/// Do an unsynchronized, non-atomic `memory.copy` for the memory. +/// +/// # Errors +/// +/// Returns a `Trap` error when the source or destination ranges are out of +/// bounds. +/// /// # Safety -/// This data is safe to share between threads because it's plain data that -/// is the user's responsibility to synchronize. And it's `Copy` so there's -/// really no difference between passing it by reference or by value as far as -/// correctness in a multi-threaded context is concerned. -unsafe impl Sync for VMMemoryDefinition {} - -impl VMMemoryDefinition { - /// Do an unsynchronized, non-atomic `memory.copy` for the memory. - /// - /// # Errors - /// - /// Returns a `Trap` error when the source or destination ranges are out of - /// bounds. - /// - /// # Safety - /// The memory is not copied atomically and is not synchronized: it's the - /// caller's responsibility to synchronize. - pub(crate) unsafe fn memory_copy(&self, dst: u32, src: u32, len: u32) -> Result<(), Trap> { - // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy - if src +/// The memory is not copied atomically and is not synchronized: it's the +/// caller's responsibility to synchronize. +pub(crate) unsafe fn memory_copy(mem: &VMMemoryDefinition, dst: u32, src: u32, len: u32) -> Result<(), Trap> { + // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy + if src + .checked_add(len) + .map_or(true, |n| usize::try_from(n).unwrap() > mem.current_length) + || dst .checked_add(len) - .map_or(true, |n| usize::try_from(n).unwrap() > self.current_length) - || dst - .checked_add(len) - .map_or(true, |m| usize::try_from(m).unwrap() > self.current_length) - { - return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); - } - - let dst = usize::try_from(dst).unwrap(); - let src = usize::try_from(src).unwrap(); - - // Bounds and casts are checked above, by this point we know that - // everything is safe. - let dst = self.base.add(dst); - let src = self.base.add(src); - ptr::copy(src, dst, len as usize); - - Ok(()) + .map_or(true, |m| usize::try_from(m).unwrap() > mem.current_length) + { + return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); } - /// Perform the `memory.fill` operation for the memory in an unsynchronized, - /// non-atomic way. - /// - /// # Errors - /// - /// Returns a `Trap` error if the memory range is out of bounds. - /// - /// # Safety - /// The memory is not filled atomically and is not synchronized: it's the - /// caller's responsibility to synchronize. - pub(crate) unsafe fn memory_fill(&self, dst: u32, val: u32, len: u32) -> Result<(), Trap> { - if dst - .checked_add(len) - .map_or(true, |m| usize::try_from(m).unwrap() > self.current_length) - { - return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); - } + let dst = usize::try_from(dst).unwrap(); + let src = usize::try_from(src).unwrap(); - let dst = isize::try_from(dst).unwrap(); - let val = val as u8; + // Bounds and casts are checked above, by this point we know that + // everything is safe. + let dst = mem.base.add(dst); + let src = mem.base.add(src); + ptr::copy(src, dst, len as usize); - // Bounds and casts are checked above, by this point we know that - // everything is safe. - let dst = self.base.offset(dst); - ptr::write_bytes(dst, val, len as usize); + Ok(()) +} - Ok(()) +/// Perform the `memory.fill` operation for the memory in an unsynchronized, +/// non-atomic way. +/// +/// # Errors +/// +/// Returns a `Trap` error if the memory range is out of bounds. +/// +/// # Safety +/// The memory is not filled atomically and is not synchronized: it's the +/// caller's responsibility to synchronize. +pub(crate) unsafe fn memory_fill(mem: &VMMemoryDefinition, dst: u32, val: u32, len: u32) -> Result<(), Trap> { + if dst + .checked_add(len) + .map_or(true, |m| usize::try_from(m).unwrap() > mem.current_length) + { + return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds)); } -} -#[cfg(test)] -mod test_vmmemory_definition { - use super::VMMemoryDefinition; - use crate::VMOffsets; - use memoffset::offset_of; - use std::mem::size_of; - use wasmer_types::ModuleInfo; + let dst = isize::try_from(dst).unwrap(); + let val = val as u8; - #[test] - fn check_vmmemory_definition_offsets() { - let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); - assert_eq!( - size_of::(), - usize::from(offsets.size_of_vmmemory_definition()) - ); - assert_eq!( - offset_of!(VMMemoryDefinition, base), - usize::from(offsets.vmmemory_definition_base()) - ); - assert_eq!( - offset_of!(VMMemoryDefinition, current_length), - usize::from(offsets.vmmemory_definition_current_length()) - ); - } + // Bounds and casts are checked above, by this point we know that + // everything is safe. + let dst = mem.base.offset(dst); + ptr::write_bytes(dst, val, len as usize); + + Ok(()) } /// The fields compiled code needs to access to utilize a WebAssembly table From f599df0240ae60cd3045fbdf594f1af80c928308 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 18 Aug 2022 16:58:04 +0200 Subject: [PATCH 02/10] Fix linter --- lib/api/src/js/externals/memory.rs | 6 +- lib/api/src/js/imports.rs | 12 ++-- lib/api/src/js/mod.rs | 2 +- lib/api/src/sys/externals/memory.rs | 7 +-- lib/api/src/sys/externals/memory_view.rs | 2 +- lib/api/src/sys/mod.rs | 2 +- .../src/translator/code_translator.rs | 7 ++- lib/compiler/src/engine/resolver.rs | 3 +- lib/compiler/src/translator/environ.rs | 2 +- lib/types/src/lib.rs | 8 +-- lib/types/src/memory.rs | 9 +-- lib/vm/src/instance/allocator.rs | 2 +- lib/vm/src/instance/mod.rs | 12 ++-- lib/vm/src/lib.rs | 8 +-- lib/vm/src/memory.rs | 58 ++++++++----------- lib/vm/src/store.rs | 10 ++-- lib/vm/src/vmcontext.rs | 14 ++++- 17 files changed, 82 insertions(+), 82 deletions(-) diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 2b66d8ed79b..599b261e2cc 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -101,7 +101,11 @@ impl Memory { } /// Creates a new host `Memory` from provided JavaScript memory. - pub fn new_raw(store: &mut impl AsStoreMut, js_memory: js_sys::WebAssembly::Memory, ty: MemoryType) -> Result { + pub fn new_raw( + store: &mut impl AsStoreMut, + js_memory: js_sys::WebAssembly::Memory, + ty: MemoryType, + ) -> Result { let vm_memory = VMMemory::new(js_memory, ty); Ok(Self::from_vm_export(store, vm_memory)) } diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs index d3b2c42efa5..63a199abb9c 100644 --- a/lib/api/src/js/imports.rs +++ b/lib/api/src/js/imports.rs @@ -182,27 +182,23 @@ impl Imports { } pub struct ImportsIterator<'a> { - iter: std::collections::hash_map::Iter<'a, (String, String), Extern> + iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, } -impl<'a> ImportsIterator<'a> -{ +impl<'a> ImportsIterator<'a> { fn new(imports: &'a Imports) -> Self { let iter = imports.map.iter(); Self { iter } } } -impl<'a> Iterator -for ImportsIterator<'a> { +impl<'a> Iterator for ImportsIterator<'a> { type Item = (&'a str, &'a str, &'a Extern); fn next(&mut self) -> Option { self.iter .next() - .map(|(k, v)| { - (k.0.as_str(), k.1.as_str(), v) - }) + .map(|(k, v)| (k.0.as_str(), k.1.as_str(), v)) } } diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index a88509086e0..1e660a53013 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -75,7 +75,7 @@ pub use crate::js::value::Value as Val; pub mod vm { //! The `vm` module re-exports wasmer-vm types. - + pub use crate::js::export::VMMemory; } diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 64ab9a136e2..4598631553d 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -10,7 +10,7 @@ use std::mem::MaybeUninit; use std::slice; #[cfg(feature = "tracing")] use tracing::warn; -use wasmer_types::{Pages, LinearMemory}; +use wasmer_types::{LinearMemory, Pages}; use wasmer_vm::{InternalStoreHandle, MemoryError, StoreHandle, VMExtern, VMMemory}; use super::MemoryView; @@ -63,7 +63,7 @@ impl Memory { /// Create a memory object from an existing memory and attaches it to the store pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { Self { - handle: StoreHandle::new(new_store.objects_mut(), memory) + handle: StoreHandle::new(new_store.objects_mut(), memory), } } @@ -152,8 +152,7 @@ impl Memory { /// Attempts to clone this memory (if its clonable) pub fn try_clone(&self, store: &impl AsStoreRef) -> Option { let mem = self.handle.get(store.as_store_ref().objects()); - mem.try_clone() - .map(|mem| mem.into()) + mem.try_clone().map(|mem| mem.into()) } pub(crate) fn to_vm_extern(&self) -> VMExtern { diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index a638acb6b97..5822c4113da 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -4,7 +4,7 @@ use std::convert::TryInto; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::slice; -use wasmer_types::{Pages, LinearMemory}; +use wasmer_types::{LinearMemory, Pages}; use super::memory::MemoryBuffer; use super::Memory; diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index b6f9088c30d..1c272a2a60e 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -58,7 +58,7 @@ pub mod vm { pub use wasmer_vm::{ MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, VMTable, - VMTableDefinition, + VMTableDefinition, }; } diff --git a/lib/compiler-cranelift/src/translator/code_translator.rs b/lib/compiler-cranelift/src/translator/code_translator.rs index 60da48347ae..c750d6c3231 100644 --- a/lib/compiler-cranelift/src/translator/code_translator.rs +++ b/lib/compiler-cranelift/src/translator/code_translator.rs @@ -1071,7 +1071,8 @@ pub fn translate_operator( expected, timeout, )?; - state.push1(res); } + state.push1(res); + } Operator::MemoryAtomicNotify { memarg } => { let heap_index = MemoryIndex::from_u32(memarg.memory); let heap = state.get_heap(builder.func, memarg.memory, environ)?; @@ -1079,8 +1080,8 @@ pub fn translate_operator( let addr = state.pop1(); // 32 (fixed) let addr = fold_atomic_mem_addr(addr, memarg, I32, builder); let res = - environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; - state.push1(res); + environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; + state.push1(res); } Operator::I32AtomicLoad { memarg } => { translate_atomic_load(I32, I32, memarg, builder, state, environ)? diff --git a/lib/compiler/src/engine/resolver.rs b/lib/compiler/src/engine/resolver.rs index 94d40a50229..2b218bde3f4 100644 --- a/lib/compiler/src/engine/resolver.rs +++ b/lib/compiler/src/engine/resolver.rs @@ -4,7 +4,8 @@ use crate::LinkError; use more_asserts::assert_ge; use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ - ExternType, FunctionIndex, ImportError, ImportIndex, MemoryIndex, ModuleInfo, TableIndex, LinearMemory, + ExternType, FunctionIndex, ImportError, ImportIndex, LinearMemory, MemoryIndex, ModuleInfo, + TableIndex, }; use wasmer_vm::{ diff --git a/lib/compiler/src/translator/environ.rs b/lib/compiler/src/translator/environ.rs index cd00a7bc01f..e172d92b063 100644 --- a/lib/compiler/src/translator/environ.rs +++ b/lib/compiler/src/translator/environ.rs @@ -1,8 +1,8 @@ // This file contains code from external sources. // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md use super::state::ModuleTranslationState; -use crate::lib::std::string::ToString; use crate::lib::std::borrow::ToOwned; +use crate::lib::std::string::ToString; use crate::lib::std::{boxed::Box, string::String, vec::Vec}; use crate::translate_module; use crate::wasmparser::{Operator, Range, Type}; diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 3f71079c94c..5a2e57ef04e 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -76,8 +76,8 @@ pub use crate::compilation::target::{ }; pub use crate::serialize::{MetadataHeader, SerializableCompilation, SerializableModule}; pub use error::{ - CompileError, DeserializeError, ImportError, MiddlewareError, ParseCpuFeatureError, - PreInstantiationError, SerializeError, WasmError, WasmResult, MemoryError, + CompileError, DeserializeError, ImportError, MemoryError, MiddlewareError, + ParseCpuFeatureError, PreInstantiationError, SerializeError, WasmError, WasmResult, }; /// The entity module, with common helpers for Rust structures @@ -103,9 +103,7 @@ pub use types::{ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; -pub use crate::memory::{ - MemoryStyle, LinearMemory, VMMemoryDefinition -}; +pub use crate::memory::{LinearMemory, MemoryStyle, VMMemoryDefinition}; pub use crate::table::TableStyle; pub use crate::trapcode::TrapCode; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 6ebac15e7e8..a06b3379c31 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -1,14 +1,14 @@ use crate::{Pages, ValueType}; +use core::ptr::NonNull; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -use core::ptr::NonNull; use std::convert::{TryFrom, TryInto}; use std::iter::Sum; use std::ops::{Add, AddAssign}; -use super::MemoryType; use super::MemoryError; +use super::MemoryType; /// Implementation styles for WebAssembly linear memory. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)] @@ -132,7 +132,8 @@ unsafe impl MemorySize for Memory64 { /// Represents memory that is used by the WebAsssembly module pub trait LinearMemory -where Self: std::fmt::Debug + Send +where + Self: std::fmt::Debug + Send, { /// Returns the type for this memory. fn ty(&self) -> MemoryType; @@ -183,10 +184,10 @@ unsafe impl Sync for VMMemoryDefinition {} #[cfg(test)] mod test_vmmemory_definition { use super::VMMemoryDefinition; + use crate::ModuleInfo; use crate::VMOffsets; use memoffset::offset_of; use std::mem::size_of; - use crate::ModuleInfo; #[test] fn check_vmmemory_definition_offsets() { diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index e00625f5e8c..c6ca741937f 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -5,8 +5,8 @@ use std::convert::TryFrom; use std::mem; use std::ptr::{self, NonNull}; use wasmer_types::entity::EntityRef; -use wasmer_types::{VMOffsets, VMMemoryDefinition}; use wasmer_types::{LocalMemoryIndex, LocalTableIndex, ModuleInfo}; +use wasmer_types::{VMMemoryDefinition, VMOffsets}; /// This is an intermediate type that manages the raw allocation and /// metadata when creating an [`Instance`]. diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index ab11b2b5501..98e2423d1b9 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -14,9 +14,9 @@ use crate::store::{InternalStoreHandle, StoreObjects}; use crate::table::TableElement; use crate::trap::{catch_traps, Trap, TrapCode}; use crate::vmcontext::{ - VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionContext, - VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, - VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, memory_copy, memory_fill, + memory_copy, memory_fill, VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, + VMFunctionContext, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, + VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; use crate::{FunctionBodyPtr, MaybeInstanceOwned, TrapHandlerFn, VMFunctionBody}; use crate::{VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable}; @@ -35,9 +35,9 @@ use std::sync::Arc; use wasmer_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, - LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, - ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, VMOffsets, LinearMemory, - MemoryError, VMMemoryDefinition + LinearMemory, LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, + MemoryError, MemoryIndex, ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, + VMMemoryDefinition, VMOffsets, }; /// A WebAssembly instance. diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 88b209a241d..05ae87d4a11 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -46,7 +46,6 @@ pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{InstanceAllocator, InstanceHandle}; pub use crate::memory::VMMemory; -pub use wasmer_types::MemoryError; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; @@ -57,13 +56,14 @@ pub use crate::table::{TableElement, VMTable}; pub use crate::trap::*; pub use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionContext, - VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, - VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, + VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryImport, + VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; pub use wasmer_types::LibCall; -pub use wasmer_types::{MemoryStyle, VMMemoryDefinition}; +pub use wasmer_types::MemoryError; use wasmer_types::RawValue; pub use wasmer_types::TableStyle; +pub use wasmer_types::{MemoryStyle, VMMemoryDefinition}; pub use wasmer_types::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; #[deprecated( diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 265c5c259b6..5ed4c9cf75c 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -10,7 +10,9 @@ use more_asserts::assert_ge; use std::cell::UnsafeCell; use std::convert::TryInto; use std::ptr::NonNull; -use wasmer_types::{Bytes, MemoryStyle, MemoryType, Pages, MemoryError, LinearMemory, VMMemoryDefinition}; +use wasmer_types::{ + Bytes, LinearMemory, MemoryError, MemoryStyle, MemoryType, Pages, VMMemoryDefinition, +}; // The memory mapped area #[derive(Debug)] @@ -23,8 +25,7 @@ struct WasmMmap { vm_memory_definition: MaybeInstanceOwned, } -impl WasmMmap -{ +impl WasmMmap { fn get_vm_memory_definition(&self) -> NonNull { self.vm_memory_definition.as_ptr() } @@ -92,14 +93,12 @@ impl WasmMmap Mmap::accessible_reserved(new_bytes, request_bytes).map_err(MemoryError::Region)?; let copy_len = self.alloc.len() - conf.offset_guard_size; - new_mmap.as_mut_slice()[..copy_len] - .copy_from_slice(&self.alloc.as_slice()[..copy_len]); + new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&self.alloc.as_slice()[..copy_len]); self.alloc = new_mmap; } else if delta_bytes > 0 { // Make the newly allocated pages accessible. - self - .alloc + self.alloc .make_accessible(prev_bytes, delta_bytes) .map_err(MemoryError::Region)?; } @@ -132,8 +131,7 @@ struct VMMemoryConfig { offset_guard_size: usize, } -impl VMMemoryConfig -{ +impl VMMemoryConfig { fn ty(&self, minimum: Pages) -> MemoryType { let mut out = self.memory; out.minimum = minimum; @@ -155,8 +153,8 @@ pub struct VMOwnedMemory { config: VMMemoryConfig, } -unsafe impl Send for VMOwnedMemory { } -unsafe impl Sync for VMOwnedMemory { } +unsafe impl Send for VMOwnedMemory {} +unsafe impl Sync for VMOwnedMemory {} impl VMOwnedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. @@ -248,7 +246,7 @@ impl VMOwnedMemory { alloc, size: memory.minimum, }; - + Ok(Self { mmap: mmap, config: VMMemoryConfig { @@ -256,14 +254,12 @@ impl VMOwnedMemory { offset_guard_size: offset_guard_bytes, memory: *memory, style: style.clone(), - } + }, }) } } -impl LinearMemory -for VMOwnedMemory -{ +impl LinearMemory for VMOwnedMemory { /// Returns the type for this memory. fn ty(&self) -> MemoryType { let minimum = self.mmap.size(); @@ -299,9 +295,7 @@ for VMOwnedMemory } } -impl Into -for VMOwnedMemory -{ +impl Into for VMOwnedMemory { fn into(self) -> VMMemory { VMMemory(Box::new(self)) } @@ -311,17 +305,13 @@ for VMOwnedMemory #[derive(Debug)] pub struct VMMemory(Box); -impl Into -for Box -{ +impl Into for Box { fn into(self) -> VMMemory { VMMemory(self) } } -impl LinearMemory -for VMMemory -{ +impl LinearMemory for VMMemory { /// Returns the type for this memory. fn ty(&self) -> MemoryType { self.0.ty() @@ -356,17 +346,14 @@ for VMMemory } } -impl VMMemory -{ +impl VMMemory { /// Creates a new linear memory instance of the correct type with specified /// minimum and maximum number of wasm pages. /// /// This creates a `Memory` with owned metadata: this can be used to create a memory /// that will be imported into Wasm modules. pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { - Ok( - Self(Box::new(VMOwnedMemory::new(memory, style)?)) - ) + Ok(Self(Box::new(VMOwnedMemory::new(memory, style)?))) } /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. @@ -381,9 +368,11 @@ impl VMMemory style: &MemoryStyle, vm_memory_location: NonNull, ) -> Result { - Ok( - Self(Box::new(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?)) - ) + Ok(Self(Box::new(VMOwnedMemory::from_definition( + memory, + style, + vm_memory_location, + )?))) } /// Creates VMMemory from a custom implementation - the following into implementations @@ -392,7 +381,8 @@ impl VMMemory /// - VMSharedMemory -> VMMemory /// - Box -> VMMemory pub fn from_custom(memory: IntoVMMemory) -> VMMemory - where IntoVMMemory: Into + where + IntoVMMemory: Into, { memory.into() } diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 25afdb7c1c5..57630f15c02 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -267,9 +267,9 @@ impl MaybeInstanceOwned { } } -impl std::fmt::Debug -for MaybeInstanceOwned -where T: std::fmt::Debug +impl std::fmt::Debug for MaybeInstanceOwned +where + T: std::fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -277,7 +277,7 @@ where T: std::fmt::Debug write!(f, "host(")?; p.as_ref().fmt(f)?; write!(f, ")") - }, + } MaybeInstanceOwned::Instance(p) => { write!(f, "instance(")?; unsafe { p.as_ref().fmt(f)? }; @@ -285,4 +285,4 @@ where T: std::fmt::Debug } } } -} \ No newline at end of file +} diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index 6940f43a265..9d28adae3d4 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -313,7 +313,12 @@ mod test_vmglobal_import { /// # Safety /// The memory is not copied atomically and is not synchronized: it's the /// caller's responsibility to synchronize. -pub(crate) unsafe fn memory_copy(mem: &VMMemoryDefinition, dst: u32, src: u32, len: u32) -> Result<(), Trap> { +pub(crate) unsafe fn memory_copy( + mem: &VMMemoryDefinition, + dst: u32, + src: u32, + len: u32, +) -> Result<(), Trap> { // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy if src .checked_add(len) @@ -347,7 +352,12 @@ pub(crate) unsafe fn memory_copy(mem: &VMMemoryDefinition, dst: u32, src: u32, l /// # Safety /// The memory is not filled atomically and is not synchronized: it's the /// caller's responsibility to synchronize. -pub(crate) unsafe fn memory_fill(mem: &VMMemoryDefinition, dst: u32, val: u32, len: u32) -> Result<(), Trap> { +pub(crate) unsafe fn memory_fill( + mem: &VMMemoryDefinition, + dst: u32, + val: u32, + len: u32, +) -> Result<(), Trap> { if dst .checked_add(len) .map_or(true, |m| usize::try_from(m).unwrap() > mem.current_length) From 7db6d3de78a2d7d66b8e6a83709ad5eea7d0ac17 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 18 Aug 2022 17:40:32 +0200 Subject: [PATCH 03/10] Made clippy happy --- lib/compiler/src/engine/resolver.rs | 2 +- lib/vm/src/memory.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/compiler/src/engine/resolver.rs b/lib/compiler/src/engine/resolver.rs index 2b218bde3f4..561ab998bd0 100644 --- a/lib/compiler/src/engine/resolver.rs +++ b/lib/compiler/src/engine/resolver.rs @@ -150,7 +150,7 @@ pub fn resolve_imports( bound: import_bound, .. }, - ) = (export_memory_style.clone(), &import_memory_style) + ) = (export_memory_style, &import_memory_style) { assert_ge!(bound, *import_bound); } diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 5ed4c9cf75c..7a8205579dc 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -248,12 +248,12 @@ impl VMOwnedMemory { }; Ok(Self { - mmap: mmap, + mmap, config: VMMemoryConfig { maximum: memory.maximum, offset_guard_size: offset_guard_bytes, memory: *memory, - style: style.clone(), + style: *style, }, }) } @@ -295,9 +295,9 @@ impl LinearMemory for VMOwnedMemory { } } -impl Into for VMOwnedMemory { - fn into(self) -> VMMemory { - VMMemory(Box::new(self)) +impl From for VMMemory { + fn from(mem: VMOwnedMemory) -> Self { + Self(Box::new(mem)) } } @@ -305,9 +305,9 @@ impl Into for VMOwnedMemory { #[derive(Debug)] pub struct VMMemory(Box); -impl Into for Box { - fn into(self) -> VMMemory { - VMMemory(self) +impl From> for VMMemory { + fn from(mem: Box) -> Self { + Self(mem) } } From 6cf99a63f6426469f8bc94f6f811ee0036992abc Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Fri, 19 Aug 2022 12:15:55 +0200 Subject: [PATCH 04/10] Removed from_vm_export in favor of from_vm_extern --- lib/api/src/js/externals/memory.rs | 15 ++++++--------- lib/api/src/js/externals/mod.rs | 2 +- lib/api/src/js/instance.rs | 2 +- lib/api/src/sys/externals/memory.rs | 5 ++--- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index 599b261e2cc..015769b896e 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -97,7 +97,8 @@ impl Memory { .map_err(|_e| MemoryError::Generic("Error while creating the memory".to_owned()))?; let vm_memory = VMMemory::new(js_memory, ty); - Ok(Self::from_vm_export(store, vm_memory)) + let handle = StoreHandle::new(store.objects_mut(), vm_memory); + Ok(Self::from_vm_extern(store, handle.internal_handle())) } /// Creates a new host `Memory` from provided JavaScript memory. @@ -107,12 +108,14 @@ impl Memory { ty: MemoryType, ) -> Result { let vm_memory = VMMemory::new(js_memory, ty); - Ok(Self::from_vm_export(store, vm_memory)) + let handle = StoreHandle::new(store.objects_mut(), vm_memory); + Ok(Self::from_vm_extern(store, handle.internal_handle())) } /// Create a memory object from an existing memory and attaches it to the store pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - Self::from_vm_export(new_store, memory) + let handle = StoreHandle::new(new_store.objects_mut(), memory); + Self::from_vm_extern(new_store, handle.internal_handle()) } /// Returns the [`MemoryType`] of the `Memory`. @@ -192,12 +195,6 @@ impl Memory { Ok(Pages(new_pages)) } - pub(crate) fn from_vm_export(store: &mut impl AsStoreMut, vm_memory: VMMemory) -> Self { - Self { - handle: StoreHandle::new(store.objects_mut(), vm_memory), - } - } - pub(crate) fn from_vm_extern( store: &mut impl AsStoreMut, internal: InternalStoreHandle, diff --git a/lib/api/src/js/externals/mod.rs b/lib/api/src/js/externals/mod.rs index 4a3c8031508..2748e08b124 100644 --- a/lib/api/src/js/externals/mod.rs +++ b/lib/api/src/js/externals/mod.rs @@ -46,7 +46,7 @@ impl Extern { } /// Create an `Extern` from an `wasmer_compiler::Export`. - pub fn from_vm_export(store: &mut impl AsStoreMut, export: Export) -> Self { + pub fn from_vm_extern(store: &mut impl AsStoreMut, export: Export) -> Self { match export { Export::Function(f) => Self::Function(Function::from_vm_extern(store, f)), Export::Memory(m) => Self::Memory(Memory::from_vm_extern(store, m)), diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index 6d2762adf66..1b1eb089858 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -105,7 +105,7 @@ impl Instance { })?; let export: Export = Export::from_js_value(js_export, &mut store, extern_type)?.into(); - let extern_ = Extern::from_vm_export(&mut store, export); + let extern_ = Extern::from_vm_extern(&mut store, export); Ok((name.to_string(), extern_)) }) .collect::>()?; diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 4598631553d..688d86c46b9 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -62,9 +62,8 @@ impl Memory { /// Create a memory object from an existing memory and attaches it to the store pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self { - Self { - handle: StoreHandle::new(new_store.objects_mut(), memory), - } + let handle = StoreHandle::new(new_store.objects_mut(), memory); + Self::from_vm_extern(new_store, handle.internal_handle()) } /// Returns the [`MemoryType`] of the `Memory`. From 553745cd3dd06d43694985628cf0e88706d273e5 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Fri, 19 Aug 2022 16:00:38 +0200 Subject: [PATCH 05/10] Move LinearMemory trait and VMMemoryDefinition to wasmer_vm (from wasmer_types) --- lib/api/src/sys/externals/memory.rs | 4 +- lib/api/src/sys/externals/memory_view.rs | 3 +- lib/compiler/src/engine/resolver.rs | 8 +-- lib/types/src/lib.rs | 2 +- lib/types/src/memory.rs | 82 ------------------------ lib/vm/src/instance/allocator.rs | 3 +- lib/vm/src/instance/mod.rs | 6 +- lib/vm/src/lib.rs | 4 +- lib/vm/src/memory.rs | 82 +++++++++++++++++++++++- lib/vm/src/vmcontext.rs | 4 +- 10 files changed, 97 insertions(+), 101 deletions(-) diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 688d86c46b9..285acb231c6 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -10,8 +10,8 @@ use std::mem::MaybeUninit; use std::slice; #[cfg(feature = "tracing")] use tracing::warn; -use wasmer_types::{LinearMemory, Pages}; -use wasmer_vm::{InternalStoreHandle, MemoryError, StoreHandle, VMExtern, VMMemory}; +use wasmer_types::Pages; +use wasmer_vm::{InternalStoreHandle, LinearMemory, MemoryError, StoreHandle, VMExtern, VMMemory}; use super::MemoryView; diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index 5822c4113da..a872ea7c259 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -4,7 +4,8 @@ use std::convert::TryInto; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::slice; -use wasmer_types::{LinearMemory, Pages}; +use wasmer_types::Pages; +use wasmer_vm::LinearMemory; use super::memory::MemoryBuffer; use super::Memory; diff --git a/lib/compiler/src/engine/resolver.rs b/lib/compiler/src/engine/resolver.rs index 561ab998bd0..87fa45f3001 100644 --- a/lib/compiler/src/engine/resolver.rs +++ b/lib/compiler/src/engine/resolver.rs @@ -4,13 +4,13 @@ use crate::LinkError; use more_asserts::assert_ge; use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ - ExternType, FunctionIndex, ImportError, ImportIndex, LinearMemory, MemoryIndex, ModuleInfo, - TableIndex, + ExternType, FunctionIndex, ImportError, ImportIndex, MemoryIndex, ModuleInfo, TableIndex, }; use wasmer_vm::{ - FunctionBodyPtr, Imports, MemoryStyle, StoreObjects, TableStyle, VMExtern, VMFunctionBody, - VMFunctionImport, VMFunctionKind, VMGlobalImport, VMMemoryImport, VMTableImport, + FunctionBodyPtr, Imports, LinearMemory, MemoryStyle, StoreObjects, TableStyle, VMExtern, + VMFunctionBody, VMFunctionImport, VMFunctionKind, VMGlobalImport, VMMemoryImport, + VMTableImport, }; /// Get an `ExternType` given a import index. diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 5a2e57ef04e..4eebc53e25e 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -103,7 +103,7 @@ pub use types::{ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; -pub use crate::memory::{LinearMemory, MemoryStyle, VMMemoryDefinition}; +pub use crate::memory::MemoryStyle; pub use crate::table::TableStyle; pub use crate::trapcode::TrapCode; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index a06b3379c31..2c986d90870 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -1,5 +1,4 @@ use crate::{Pages, ValueType}; -use core::ptr::NonNull; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -7,9 +6,6 @@ use std::convert::{TryFrom, TryInto}; use std::iter::Sum; use std::ops::{Add, AddAssign}; -use super::MemoryError; -use super::MemoryType; - /// Implementation styles for WebAssembly linear memory. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -129,81 +125,3 @@ unsafe impl MemorySize for Memory64 { native as Self::Offset } } - -/// Represents memory that is used by the WebAsssembly module -pub trait LinearMemory -where - Self: std::fmt::Debug + Send, -{ - /// Returns the type for this memory. - fn ty(&self) -> MemoryType; - - /// Returns the size of hte memory in pages - fn size(&self) -> Pages; - - /// Returns the memory style for this memory. - fn style(&self) -> MemoryStyle; - - /// Grow memory by the specified amount of wasm pages. - /// - /// Returns `None` if memory can't be grown by the specified amount - /// of wasm pages. - fn grow(&mut self, delta: Pages) -> Result; - - /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. - fn vmmemory(&self) -> NonNull; - - /// Attempts to clone this memory (if its clonable) - fn try_clone(&self) -> Option>; -} - -/// The fields compiled code needs to access to utilize a WebAssembly linear -/// memory defined within the instance, namely the start address and the -/// size in bytes. -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct VMMemoryDefinition { - /// The start address which is always valid, even if the memory grows. - pub base: *mut u8, - - /// The current logical size of this linear memory in bytes. - pub current_length: usize, -} - -/// # Safety -/// This data is safe to share between threads because it's plain data that -/// is the user's responsibility to synchronize. -unsafe impl Send for VMMemoryDefinition {} -/// # Safety -/// This data is safe to share between threads because it's plain data that -/// is the user's responsibility to synchronize. And it's `Copy` so there's -/// really no difference between passing it by reference or by value as far as -/// correctness in a multi-threaded context is concerned. -unsafe impl Sync for VMMemoryDefinition {} - -#[cfg(test)] -mod test_vmmemory_definition { - use super::VMMemoryDefinition; - use crate::ModuleInfo; - use crate::VMOffsets; - use memoffset::offset_of; - use std::mem::size_of; - - #[test] - fn check_vmmemory_definition_offsets() { - let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); - assert_eq!( - size_of::(), - usize::from(offsets.size_of_vmmemory_definition()) - ); - assert_eq!( - offset_of!(VMMemoryDefinition, base), - usize::from(offsets.vmmemory_definition_base()) - ); - assert_eq!( - offset_of!(VMMemoryDefinition, current_length), - usize::from(offsets.vmmemory_definition_current_length()) - ); - } -} diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index c6ca741937f..e81af255051 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -1,12 +1,13 @@ use super::{Instance, InstanceHandle}; +use crate::memory::VMMemoryDefinition; use crate::vmcontext::VMTableDefinition; use std::alloc::{self, Layout}; use std::convert::TryFrom; use std::mem; use std::ptr::{self, NonNull}; use wasmer_types::entity::EntityRef; +use wasmer_types::VMOffsets; use wasmer_types::{LocalMemoryIndex, LocalTableIndex, ModuleInfo}; -use wasmer_types::{VMMemoryDefinition, VMOffsets}; /// This is an intermediate type that manages the raw allocation and /// metadata when creating an [`Instance`]. diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index 98e2423d1b9..c91a8f63fdc 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -19,6 +19,7 @@ use crate::vmcontext::{ VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; use crate::{FunctionBodyPtr, MaybeInstanceOwned, TrapHandlerFn, VMFunctionBody}; +use crate::{LinearMemory, VMMemoryDefinition}; use crate::{VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable}; pub use allocator::InstanceAllocator; use memoffset::offset_of; @@ -35,9 +36,8 @@ use std::sync::Arc; use wasmer_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, - LinearMemory, LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, - MemoryError, MemoryIndex, ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, - VMMemoryDefinition, VMOffsets, + LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryError, + MemoryIndex, ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, VMOffsets, }; /// A WebAssembly instance. diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 05ae87d4a11..16af4df4b7f 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -45,7 +45,7 @@ pub use crate::function_env::VMFunctionEnvironment; pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{InstanceAllocator, InstanceHandle}; -pub use crate::memory::VMMemory; +pub use crate::memory::{LinearMemory, VMMemory, VMMemoryDefinition}; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; @@ -61,9 +61,9 @@ pub use crate::vmcontext::{ }; pub use wasmer_types::LibCall; pub use wasmer_types::MemoryError; +pub use wasmer_types::MemoryStyle; use wasmer_types::RawValue; pub use wasmer_types::TableStyle; -pub use wasmer_types::{MemoryStyle, VMMemoryDefinition}; pub use wasmer_types::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; #[deprecated( diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 7a8205579dc..a82c48295f0 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -10,9 +10,7 @@ use more_asserts::assert_ge; use std::cell::UnsafeCell; use std::convert::TryInto; use std::ptr::NonNull; -use wasmer_types::{ - Bytes, LinearMemory, MemoryError, MemoryStyle, MemoryType, Pages, VMMemoryDefinition, -}; +use wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages}; // The memory mapped area #[derive(Debug)] @@ -387,3 +385,81 @@ impl VMMemory { memory.into() } } + +/// Represents memory that is used by the WebAsssembly module +pub trait LinearMemory +where + Self: std::fmt::Debug + Send, +{ + /// Returns the type for this memory. + fn ty(&self) -> MemoryType; + + /// Returns the size of hte memory in pages + fn size(&self) -> Pages; + + /// Returns the memory style for this memory. + fn style(&self) -> MemoryStyle; + + /// Grow memory by the specified amount of wasm pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of wasm pages. + fn grow(&mut self, delta: Pages) -> Result; + + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + fn vmmemory(&self) -> NonNull; + + /// Attempts to clone this memory (if its clonable) + fn try_clone(&self) -> Option>; +} + +/// The fields compiled code needs to access to utilize a WebAssembly linear +/// memory defined within the instance, namely the start address and the +/// size in bytes. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMMemoryDefinition { + /// The start address which is always valid, even if the memory grows. + pub base: *mut u8, + + /// The current logical size of this linear memory in bytes. + pub current_length: usize, +} + +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. +unsafe impl Send for VMMemoryDefinition {} +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. And it's `Copy` so there's +/// really no difference between passing it by reference or by value as far as +/// correctness in a multi-threaded context is concerned. +unsafe impl Sync for VMMemoryDefinition {} + +#[cfg(test)] +mod test_vmmemory_definition { + use super::VMMemoryDefinition; + use crate::ModuleInfo; + use crate::VMOffsets; + use memoffset::offset_of; + use std::mem::size_of; + + #[test] + fn check_vmmemory_definition_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmmemory_definition()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, base), + usize::from(offsets.vmmemory_definition_base()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, current_length), + usize::from(offsets.vmmemory_definition_current_length()) + ); + } +} diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index 9d28adae3d4..cea7b116ecc 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -6,7 +6,7 @@ use crate::global::VMGlobal; use crate::instance::Instance; -use crate::memory::VMMemory; +use crate::memory::{VMMemory, VMMemoryDefinition}; use crate::store::InternalStoreHandle; use crate::trap::{Trap, TrapCode}; use crate::VMFunctionBody; @@ -15,7 +15,7 @@ use crate::{VMBuiltinFunctionIndex, VMFunction}; use std::convert::TryFrom; use std::ptr::{self, NonNull}; use std::u32; -use wasmer_types::{RawValue, VMMemoryDefinition}; +use wasmer_types::RawValue; /// Union representing the first parameter passed when calling a function. /// From 12eea421877279c5b4728bb0b03d2e3ab248fc7c Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Fri, 19 Aug 2022 18:13:39 +0200 Subject: [PATCH 06/10] Added some test for LinearMemory trait --- lib/vm/src/memory.rs | 101 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index a82c48295f0..a127b701d2e 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -376,7 +376,6 @@ impl VMMemory { /// Creates VMMemory from a custom implementation - the following into implementations /// are natively supported /// - VMOwnedMemory -> VMMemory - /// - VMSharedMemory -> VMMemory /// - Box -> VMMemory pub fn from_custom(memory: IntoVMMemory) -> VMMemory where @@ -463,3 +462,103 @@ mod test_vmmemory_definition { ); } } + +#[cfg(test)] +mod test_linearmemory { + use super::LinearMemory; + use super::VMMemoryDefinition; + use crate::store::MaybeInstanceOwned; + use crate::VMMemory; + use std::cell::UnsafeCell; + use std::ptr::read_unaligned; + use std::ptr::write_unaligned; + use std::ptr::NonNull; + use wasmer_types::{MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE}; + + #[derive(Debug)] + struct VMTinyMemory { + mem: [u8; WASM_PAGE_SIZE], + } + + unsafe impl Send for VMTinyMemory {} + unsafe impl Sync for VMTinyMemory {} + + impl VMTinyMemory { + pub fn new() -> Result { + Ok(VMTinyMemory { + mem: [0; WASM_PAGE_SIZE], + }) + } + } + + impl LinearMemory for VMTinyMemory { + fn ty(&self) -> MemoryType { + MemoryType { + minimum: Pages::from(1u32), + maximum: Some(Pages::from(1u32)), + shared: false, + } + } + fn size(&self) -> Pages { + Pages::from(1u32) + } + fn style(&self) -> MemoryStyle { + MemoryStyle::Static { + bound: Pages::from(1u32), + offset_guard_size: 0, + } + } + fn grow(&mut self, delta: Pages) -> Result { + Err(MemoryError::CouldNotGrow { + current: Pages::from(1u32), + attempted_delta: delta, + }) + } + fn vmmemory(&self) -> NonNull { + MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMMemoryDefinition { + base: self.mem.as_ptr() as _, + current_length: WASM_PAGE_SIZE, + }))) + .as_ptr() + } + fn try_clone(&self) -> Option> { + None + } + } + + impl From for VMMemory { + fn from(mem: VMTinyMemory) -> Self { + Self(Box::new(mem)) + } + } + + #[test] + fn check_linearmemory() { + let mut memory = VMTinyMemory::new().unwrap(); + assert!(memory.grow(Pages::from(2u32)).is_err()); + assert_eq!(memory.size(), Pages::from(1u32)); + + let vmemdef = memory.vmmemory(); + let raw_ptr: *mut u8 = unsafe { vmemdef.as_ref().base }; + unsafe { + write_unaligned(raw_ptr, 1); + assert_eq!(read_unaligned(raw_ptr), 1); + write_unaligned(raw_ptr.add(100), 200); + assert_eq!(read_unaligned(raw_ptr.add(100)), 200); + } + // re-borrow + let vmemdef = memory.vmmemory(); + let raw_ptr: *mut u8 = unsafe { vmemdef.as_ref().base }; + unsafe { + assert_eq!(read_unaligned(raw_ptr), 1); + } + // borrow as VMMemory + let vmmemory: VMMemory = VMMemory::from_custom(memory); + assert_eq!(vmmemory.size(), Pages::from(1u32)); + let raw_ptr = unsafe { vmmemory.vmmemory().as_ref().base }; + unsafe { + assert_eq!(read_unaligned(raw_ptr), 1); + assert_eq!(read_unaligned(raw_ptr.add(100)), 200); + } + } +} From 73844bb7d2f4761efb44a98edcdbcd6692c54780 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 22 Aug 2022 15:01:28 +0200 Subject: [PATCH 07/10] Move LinearMemory test from vm::Memory to Tunables --- lib/api/src/sys/tunables.rs | 79 ++++++++++++++++++++++++++++ lib/vm/src/memory.rs | 102 +----------------------------------- 2 files changed, 80 insertions(+), 101 deletions(-) diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 00663cab749..1be12e59c52 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -134,6 +134,8 @@ impl Tunables for BaseTunables { #[cfg(test)] mod tests { + use crate::sys::tunables; + use super::*; #[test] @@ -174,4 +176,81 @@ mod tests { s => panic!("Unexpected memory style: {:?}", s), } } + + use std::cell::UnsafeCell; + use std::ptr::read_unaligned; + use std::ptr::write_unaligned; + use std::ptr::NonNull; + use wasmer_types::{MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE}; + use wasmer_vm::{LinearMemory, MaybeInstanceOwned}; + + #[derive(Debug)] + struct VMTinyMemory { + mem: [u8; WASM_PAGE_SIZE], + } + + unsafe impl Send for VMTinyMemory {} + unsafe impl Sync for VMTinyMemory {} + + impl VMTinyMemory { + pub fn new() -> Result { + Ok(VMTinyMemory { + mem: [0; WASM_PAGE_SIZE], + }) + } + } + + impl LinearMemory for VMTinyMemory { + fn ty(&self) -> MemoryType { + MemoryType { + minimum: Pages::from(1u32), + maximum: Some(Pages::from(1u32)), + shared: false, + } + } + fn size(&self) -> Pages { + Pages::from(1u32) + } + fn style(&self) -> MemoryStyle { + MemoryStyle::Static { + bound: Pages::from(1u32), + offset_guard_size: 0, + } + } + fn grow(&mut self, delta: Pages) -> Result { + Err(MemoryError::CouldNotGrow { + current: Pages::from(1u32), + attempted_delta: delta, + }) + } + fn vmmemory(&self) -> NonNull { + MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMMemoryDefinition { + base: self.mem.as_ptr() as _, + current_length: WASM_PAGE_SIZE, + }))) + .as_ptr() + } + fn try_clone(&self) -> Option> { + None + } + } + + impl From for wasmer_vm::VMMemory { + fn from(mem: VMTinyMemory) -> Self { + Self(Box::new(mem)) + } + } + + #[test] + fn check_linearmemory() { + let mut memory = VMTinyMemory::new().unwrap(); + assert!(memory.grow(Pages::from(2u32)).is_err()); + assert_eq!(memory.size(), Pages::from(1u32)); + let target = Target::default(); + let tunables = BaseTunables::for_target(&target); + let vmmemory = + unsafe { tunables.create_vm_memory(&memory.ty(), &memory.style(), memory.vmmemory()) }; + let vmmemory = vmmemory.unwrap(); + assert_eq!(vmmemory.size(), Pages::from(1u32)); + } } diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index a127b701d2e..516da4dee37 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -301,7 +301,7 @@ impl From for VMMemory { /// Represents linear memory that can be either owned or shared #[derive(Debug)] -pub struct VMMemory(Box); +pub struct VMMemory(pub Box); impl From> for VMMemory { fn from(mem: Box) -> Self { @@ -462,103 +462,3 @@ mod test_vmmemory_definition { ); } } - -#[cfg(test)] -mod test_linearmemory { - use super::LinearMemory; - use super::VMMemoryDefinition; - use crate::store::MaybeInstanceOwned; - use crate::VMMemory; - use std::cell::UnsafeCell; - use std::ptr::read_unaligned; - use std::ptr::write_unaligned; - use std::ptr::NonNull; - use wasmer_types::{MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE}; - - #[derive(Debug)] - struct VMTinyMemory { - mem: [u8; WASM_PAGE_SIZE], - } - - unsafe impl Send for VMTinyMemory {} - unsafe impl Sync for VMTinyMemory {} - - impl VMTinyMemory { - pub fn new() -> Result { - Ok(VMTinyMemory { - mem: [0; WASM_PAGE_SIZE], - }) - } - } - - impl LinearMemory for VMTinyMemory { - fn ty(&self) -> MemoryType { - MemoryType { - minimum: Pages::from(1u32), - maximum: Some(Pages::from(1u32)), - shared: false, - } - } - fn size(&self) -> Pages { - Pages::from(1u32) - } - fn style(&self) -> MemoryStyle { - MemoryStyle::Static { - bound: Pages::from(1u32), - offset_guard_size: 0, - } - } - fn grow(&mut self, delta: Pages) -> Result { - Err(MemoryError::CouldNotGrow { - current: Pages::from(1u32), - attempted_delta: delta, - }) - } - fn vmmemory(&self) -> NonNull { - MaybeInstanceOwned::Host(Box::new(UnsafeCell::new(VMMemoryDefinition { - base: self.mem.as_ptr() as _, - current_length: WASM_PAGE_SIZE, - }))) - .as_ptr() - } - fn try_clone(&self) -> Option> { - None - } - } - - impl From for VMMemory { - fn from(mem: VMTinyMemory) -> Self { - Self(Box::new(mem)) - } - } - - #[test] - fn check_linearmemory() { - let mut memory = VMTinyMemory::new().unwrap(); - assert!(memory.grow(Pages::from(2u32)).is_err()); - assert_eq!(memory.size(), Pages::from(1u32)); - - let vmemdef = memory.vmmemory(); - let raw_ptr: *mut u8 = unsafe { vmemdef.as_ref().base }; - unsafe { - write_unaligned(raw_ptr, 1); - assert_eq!(read_unaligned(raw_ptr), 1); - write_unaligned(raw_ptr.add(100), 200); - assert_eq!(read_unaligned(raw_ptr.add(100)), 200); - } - // re-borrow - let vmemdef = memory.vmmemory(); - let raw_ptr: *mut u8 = unsafe { vmemdef.as_ref().base }; - unsafe { - assert_eq!(read_unaligned(raw_ptr), 1); - } - // borrow as VMMemory - let vmmemory: VMMemory = VMMemory::from_custom(memory); - assert_eq!(vmmemory.size(), Pages::from(1u32)); - let raw_ptr = unsafe { vmmemory.vmmemory().as_ref().base }; - unsafe { - assert_eq!(read_unaligned(raw_ptr), 1); - assert_eq!(read_unaligned(raw_ptr.add(100)), 200); - } - } -} From bae459138f0e9a13190af977ad6d9193fb44e5d0 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 22 Aug 2022 16:52:23 +0200 Subject: [PATCH 08/10] LinearMemory test also include custom Tunables now --- lib/api/src/sys/tunables.rs | 81 ++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 1be12e59c52..d27b86266a4 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -178,8 +178,6 @@ mod tests { } use std::cell::UnsafeCell; - use std::ptr::read_unaligned; - use std::ptr::write_unaligned; use std::ptr::NonNull; use wasmer_types::{MemoryError, MemoryStyle, MemoryType, Pages, WASM_PAGE_SIZE}; use wasmer_vm::{LinearMemory, MaybeInstanceOwned}; @@ -219,7 +217,7 @@ mod tests { } fn grow(&mut self, delta: Pages) -> Result { Err(MemoryError::CouldNotGrow { - current: Pages::from(1u32), + current: Pages::from(100u32), attempted_delta: delta, }) } @@ -241,16 +239,77 @@ mod tests { } } + struct TinyTunables; + impl Tunables for TinyTunables { + fn memory_style(&self, _memory: &MemoryType) -> MemoryStyle { + MemoryStyle::Static { + bound: Pages::from(1u32), + offset_guard_size: 0, + } + } + + /// Construct a `TableStyle` for the provided `TableType` + fn table_style(&self, _table: &TableType) -> TableStyle { + TableStyle::CallerChecksSignature + } + fn create_host_memory( + &self, + _ty: &MemoryType, + _style: &MemoryStyle, + ) -> Result { + let memory = VMTinyMemory::new().unwrap(); + Ok(VMMemory::from_custom(memory)) + } + unsafe fn create_vm_memory( + &self, + _ty: &MemoryType, + _style: &MemoryStyle, + _vm_definition_location: NonNull, + ) -> Result { + let memory = VMTinyMemory::new().unwrap(); + Ok(VMMemory::from_custom(memory)) + } + + /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + VMTable::new(ty, style) + } + + /// Create a table owned by the VM given a [`TableType`] and a [`TableStyle`]. + /// + /// # Safety + /// - `vm_definition_location` must point to a valid location in VM memory. + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + VMTable::from_definition(ty, style, vm_definition_location) + } + } + #[test] fn check_linearmemory() { - let mut memory = VMTinyMemory::new().unwrap(); - assert!(memory.grow(Pages::from(2u32)).is_err()); - assert_eq!(memory.size(), Pages::from(1u32)); - let target = Target::default(); - let tunables = BaseTunables::for_target(&target); - let vmmemory = - unsafe { tunables.create_vm_memory(&memory.ty(), &memory.style(), memory.vmmemory()) }; - let vmmemory = vmmemory.unwrap(); + let tunables = TinyTunables {}; + let vmmemory = tunables.create_host_memory( + &MemoryType::new(1u32, Some(100u32), true), + &MemoryStyle::Static { + bound: Pages::from(1u32), + offset_guard_size: 0u64, + }, + ); + let mut vmmemory = vmmemory.unwrap(); + assert!(vmmemory.grow(Pages::from(2u32)).is_err()); assert_eq!(vmmemory.size(), Pages::from(1u32)); + //unsafe { tunables.create_vm_memory(&memory.ty(), &memory.style(), memory.vmmemory()) }; + assert_eq!(vmmemory.size(), Pages::from(1u32)); + assert_eq!( + vmmemory.grow(Pages::from(0u32)).err().unwrap(), + MemoryError::CouldNotGrow { + current: Pages::from(100u32), + attempted_delta: Pages::from(0u32) + } + ); } } From 6e0d58f0d43e04ff10999d327390f0aae8681dd7 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 22 Aug 2022 17:42:07 +0200 Subject: [PATCH 09/10] Cleanup LinearMemory test --- lib/api/src/sys/tunables.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index d27b86266a4..60abdac532a 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -134,8 +134,6 @@ impl Tunables for BaseTunables { #[cfg(test)] mod tests { - use crate::sys::tunables; - use super::*; #[test] @@ -302,8 +300,6 @@ mod tests { let mut vmmemory = vmmemory.unwrap(); assert!(vmmemory.grow(Pages::from(2u32)).is_err()); assert_eq!(vmmemory.size(), Pages::from(1u32)); - //unsafe { tunables.create_vm_memory(&memory.ty(), &memory.style(), memory.vmmemory()) }; - assert_eq!(vmmemory.size(), Pages::from(1u32)); assert_eq!( vmmemory.grow(Pages::from(0u32)).err().unwrap(), MemoryError::CouldNotGrow { From 2953093fb151e177b9cd885750f06c2c050e5dce Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 23 Aug 2022 11:28:43 +0200 Subject: [PATCH 10/10] Some more cleanup --- lib/api/src/sys/tunables.rs | 2 +- lib/vm/src/instance/allocator.rs | 3 +- lib/vm/src/lib.rs | 6 ++-- lib/vm/src/memory.rs | 53 +------------------------------- lib/vm/src/vmcontext.rs | 53 +++++++++++++++++++++++++++++++- 5 files changed, 58 insertions(+), 59 deletions(-) diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 60abdac532a..0a1eadd7609 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -265,7 +265,7 @@ mod tests { _vm_definition_location: NonNull, ) -> Result { let memory = VMTinyMemory::new().unwrap(); - Ok(VMMemory::from_custom(memory)) + Ok(memory.into()) } /// Create a table owned by the host given a [`TableType`] and a [`TableStyle`]. diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index e81af255051..29804c7460e 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -1,6 +1,5 @@ use super::{Instance, InstanceHandle}; -use crate::memory::VMMemoryDefinition; -use crate::vmcontext::VMTableDefinition; +use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition}; use std::alloc::{self, Layout}; use std::convert::TryFrom; use std::mem; diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 16af4df4b7f..19982e54fcc 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -45,7 +45,7 @@ pub use crate::function_env::VMFunctionEnvironment; pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{InstanceAllocator, InstanceHandle}; -pub use crate::memory::{LinearMemory, VMMemory, VMMemoryDefinition}; +pub use crate::memory::{LinearMemory, VMMemory}; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; @@ -56,8 +56,8 @@ pub use crate::table::{TableElement, VMTable}; pub use crate::trap::*; pub use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionContext, - VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryImport, - VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, + VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, + VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; pub use wasmer_types::LibCall; pub use wasmer_types::MemoryError; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 516da4dee37..f071365d19f 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -5,7 +5,7 @@ //! //! `Memory` is to WebAssembly linear memories what `Table` is to WebAssembly tables. -use crate::{mmap::Mmap, store::MaybeInstanceOwned}; +use crate::{mmap::Mmap, store::MaybeInstanceOwned, vmcontext::VMMemoryDefinition}; use more_asserts::assert_ge; use std::cell::UnsafeCell; use std::convert::TryInto; @@ -411,54 +411,3 @@ where /// Attempts to clone this memory (if its clonable) fn try_clone(&self) -> Option>; } - -/// The fields compiled code needs to access to utilize a WebAssembly linear -/// memory defined within the instance, namely the start address and the -/// size in bytes. -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct VMMemoryDefinition { - /// The start address which is always valid, even if the memory grows. - pub base: *mut u8, - - /// The current logical size of this linear memory in bytes. - pub current_length: usize, -} - -/// # Safety -/// This data is safe to share between threads because it's plain data that -/// is the user's responsibility to synchronize. -unsafe impl Send for VMMemoryDefinition {} -/// # Safety -/// This data is safe to share between threads because it's plain data that -/// is the user's responsibility to synchronize. And it's `Copy` so there's -/// really no difference between passing it by reference or by value as far as -/// correctness in a multi-threaded context is concerned. -unsafe impl Sync for VMMemoryDefinition {} - -#[cfg(test)] -mod test_vmmemory_definition { - use super::VMMemoryDefinition; - use crate::ModuleInfo; - use crate::VMOffsets; - use memoffset::offset_of; - use std::mem::size_of; - - #[test] - fn check_vmmemory_definition_offsets() { - let module = ModuleInfo::new(); - let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); - assert_eq!( - size_of::(), - usize::from(offsets.size_of_vmmemory_definition()) - ); - assert_eq!( - offset_of!(VMMemoryDefinition, base), - usize::from(offsets.vmmemory_definition_base()) - ); - assert_eq!( - offset_of!(VMMemoryDefinition, current_length), - usize::from(offsets.vmmemory_definition_current_length()) - ); - } -} diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index cea7b116ecc..766a8708d1d 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -6,7 +6,7 @@ use crate::global::VMGlobal; use crate::instance::Instance; -use crate::memory::{VMMemory, VMMemoryDefinition}; +use crate::memory::VMMemory; use crate::store::InternalStoreHandle; use crate::trap::{Trap, TrapCode}; use crate::VMFunctionBody; @@ -677,3 +677,54 @@ pub type VMTrampoline = unsafe extern "C" fn( *const VMFunctionBody, // function we're actually calling *mut RawValue, // space for arguments and return values ); + +/// The fields compiled code needs to access to utilize a WebAssembly linear +/// memory defined within the instance, namely the start address and the +/// size in bytes. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct VMMemoryDefinition { + /// The start address which is always valid, even if the memory grows. + pub base: *mut u8, + + /// The current logical size of this linear memory in bytes. + pub current_length: usize, +} + +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. +unsafe impl Send for VMMemoryDefinition {} +/// # Safety +/// This data is safe to share between threads because it's plain data that +/// is the user's responsibility to synchronize. And it's `Copy` so there's +/// really no difference between passing it by reference or by value as far as +/// correctness in a multi-threaded context is concerned. +unsafe impl Sync for VMMemoryDefinition {} + +#[cfg(test)] +mod test_vmmemory_definition { + use super::VMMemoryDefinition; + use crate::ModuleInfo; + use crate::VMOffsets; + use memoffset::offset_of; + use std::mem::size_of; + + #[test] + fn check_vmmemory_definition_offsets() { + let module = ModuleInfo::new(); + let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module); + assert_eq!( + size_of::(), + usize::from(offsets.size_of_vmmemory_definition()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, base), + usize::from(offsets.vmmemory_definition_base()) + ); + assert_eq!( + offset_of!(VMMemoryDefinition, current_length), + usize::from(offsets.vmmemory_definition_current_length()) + ); + } +}