wasmer_runtime_core_fl/memory/
mod.rs

1//! The memory module contains the implementation data structures and helper functions used to
2//! manipulate and access wasm memory.
3use crate::{
4    error::{CreationError, GrowError},
5    export::Export,
6    import::IsExport,
7    memory::dynamic::DYNAMIC_GUARD_SIZE,
8    memory::static_::{SAFE_STATIC_GUARD_SIZE, SAFE_STATIC_HEAP_SIZE},
9    types::{MemoryDescriptor, ValueType},
10    units::Pages,
11    vm,
12};
13use std::{cell::Cell, fmt, mem, sync::Arc};
14
15use std::sync::Mutex as StdMutex;
16
17pub use self::dynamic::DynamicMemory;
18pub use self::static_::StaticMemory;
19pub use self::view::{Atomically, MemoryView};
20
21use parking_lot::Mutex;
22
23mod dynamic;
24pub mod ptr;
25mod static_;
26mod view;
27
28#[derive(Clone)]
29enum MemoryVariant {
30    Unshared(UnsharedMemory),
31    Shared(SharedMemory),
32}
33
34/// A shared or unshared wasm linear memory.
35///
36/// A `Memory` represents the memory used by a wasm instance.
37#[derive(Clone)]
38pub struct Memory {
39    desc: MemoryDescriptor,
40    variant: MemoryVariant,
41}
42
43impl Memory {
44    /// Create a new `Memory` from a [`MemoryDescriptor`]
45    ///
46    /// [`MemoryDescriptor`]: struct.MemoryDescriptor.html
47    ///
48    /// Usage:
49    ///
50    /// ```
51    /// # use wasmer_runtime_core::types::MemoryDescriptor;
52    /// # use wasmer_runtime_core::memory::Memory;
53    /// # use wasmer_runtime_core::error::Result;
54    /// # use wasmer_runtime_core::units::Pages;
55    /// fn create_memory() -> Result<()> {
56    ///     let descriptor = MemoryDescriptor::new(Pages(10), None, false).unwrap();
57    ///
58    ///     let memory = Memory::new(descriptor)?;
59    ///     Ok(())
60    /// }
61    /// ```
62    pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
63        if let Some(max) = desc.maximum {
64            if max < desc.minimum {
65                return Err(CreationError::InvalidDescriptor(
66                    "Max number of memory pages is less than the minimum number of pages"
67                        .to_string(),
68                ));
69            }
70        }
71
72        if desc.shared && desc.maximum.is_none() {
73            return Err(CreationError::InvalidDescriptor(
74                "Max number of pages is required for shared memory".to_string(),
75            ));
76        }
77
78        let variant = if !desc.shared {
79            MemoryVariant::Unshared(UnsharedMemory::new(desc)?)
80        } else {
81            MemoryVariant::Shared(SharedMemory::new(desc)?)
82        };
83
84        Ok(Memory { desc, variant })
85    }
86
87    /// Return the [`MemoryDescriptor`] that this memory
88    /// was created with.
89    ///
90    /// [`MemoryDescriptor`]: struct.MemoryDescriptor.html
91    pub fn descriptor(&self) -> MemoryDescriptor {
92        self.desc
93    }
94
95    /// Grow this memory by the specified number of pages.
96    pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
97        match &self.variant {
98            MemoryVariant::Unshared(unshared_mem) => unshared_mem.grow(delta),
99            MemoryVariant::Shared(shared_mem) => shared_mem.grow(delta),
100        }
101    }
102
103    /// The size, in wasm pages, of this memory.
104    pub fn size(&self) -> Pages {
105        match &self.variant {
106            MemoryVariant::Unshared(unshared_mem) => unshared_mem.size(),
107            MemoryVariant::Shared(shared_mem) => shared_mem.size(),
108        }
109    }
110
111    /// Return a "view" of the currently accessible memory. By
112    /// default, the view is unsynchronized, using regular memory
113    /// accesses. You can force a memory view to use atomic accesses
114    /// by calling the [`atomically`] method.
115    ///
116    /// [`atomically`]: memory/struct.MemoryView.html#method.atomically
117    ///
118    /// # Notes:
119    ///
120    /// This method is safe (as in, it won't cause the host to crash or have UB),
121    /// but it doesn't obey rust's rules involving data races, especially concurrent ones.
122    /// Therefore, if this memory is shared between multiple threads, a single memory
123    /// location can be mutated concurrently without synchronization.
124    ///
125    /// # Usage:
126    ///
127    /// ```
128    /// # use wasmer_runtime_core::memory::{Memory, MemoryView};
129    /// # use std::{cell::Cell, sync::atomic::Ordering};
130    /// # fn view_memory(memory: Memory) {
131    /// // Without synchronization.
132    /// let view: MemoryView<u8> = memory.view();
133    /// for byte in view[0x1000 .. 0x1010].iter().map(Cell::get) {
134    ///     println!("byte: {}", byte);
135    /// }
136    ///
137    /// // With synchronization.
138    /// let atomic_view = view.atomically();
139    /// for byte in atomic_view[0x1000 .. 0x1010].iter().map(|atom| atom.load(Ordering::SeqCst)) {
140    ///     println!("byte: {}", byte);
141    /// }
142    /// # }
143    /// ```
144    pub fn view<T: ValueType>(&self) -> MemoryView<T> {
145        let vm::LocalMemory { base, .. } = unsafe { *self.vm_local_memory() };
146
147        let length = self.size().bytes().0 / mem::size_of::<T>();
148
149        unsafe { MemoryView::new(base as _, length as u32) }
150    }
151
152    /// Returns the module local memory.
153    pub fn vm_local_memory(&self) -> *mut vm::LocalMemory {
154        match &self.variant {
155            MemoryVariant::Unshared(unshared_mem) => unshared_mem.vm_local_memory(),
156            MemoryVariant::Shared(shared_mem) => shared_mem.vm_local_memory(),
157        }
158    }
159}
160
161impl IsExport for Memory {
162    fn to_export(&self) -> Export {
163        Export::Memory(self.clone())
164    }
165}
166
167impl fmt::Debug for Memory {
168    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169        f.debug_struct("Memory")
170            .field("desc", &self.desc)
171            .field("size", &self.size())
172            .finish()
173    }
174}
175
176/// A kind a memory.
177#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
178pub enum MemoryType {
179    /// A dynamic memory.
180    Dynamic,
181    /// A static memory.
182    Static,
183    /// A shared static memory.
184    SharedStatic,
185}
186
187impl MemoryType {
188    #[doc(hidden)]
189    pub fn guard_size(self) -> u64 {
190        match self {
191            MemoryType::Dynamic => DYNAMIC_GUARD_SIZE as u64,
192            MemoryType::Static | MemoryType::SharedStatic => SAFE_STATIC_GUARD_SIZE as u64,
193        }
194    }
195
196    #[doc(hidden)]
197    pub fn bounds(self) -> Option<u64> {
198        match self {
199            MemoryType::Dynamic => None,
200            MemoryType::Static | MemoryType::SharedStatic => Some(SAFE_STATIC_HEAP_SIZE as u64),
201        }
202    }
203}
204
205enum UnsharedMemoryStorage {
206    Dynamic(Box<DynamicMemory>),
207    Static(Box<StaticMemory>),
208}
209
210/// A reference to an unshared memory.
211pub struct UnsharedMemory {
212    internal: Arc<UnsharedMemoryInternal>,
213}
214
215struct UnsharedMemoryInternal {
216    storage: StdMutex<UnsharedMemoryStorage>,
217    local: Cell<vm::LocalMemory>,
218}
219
220// Manually implemented because UnsharedMemoryInternal uses `Cell` and is used in an Arc;
221// this is safe because the lock for storage can be used to protect (seems like a weak reason: PLEASE REVIEW!)
222unsafe impl Sync for UnsharedMemoryInternal {}
223
224impl UnsharedMemory {
225    /// Create a new `UnsharedMemory` from the given memory descriptor.
226    pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
227        let mut local = vm::LocalMemory {
228            base: std::ptr::null_mut(),
229            bound: 0,
230            memory: std::ptr::null_mut(),
231        };
232
233        let storage = match desc.memory_type() {
234            MemoryType::Dynamic => {
235                UnsharedMemoryStorage::Dynamic(DynamicMemory::new(desc, &mut local)?)
236            }
237            MemoryType::Static => {
238                UnsharedMemoryStorage::Static(StaticMemory::new(desc, &mut local)?)
239            }
240            MemoryType::SharedStatic => {
241                return Err(CreationError::InvalidDescriptor(
242                    "attempting to create shared unshared memory".to_string(),
243                ));
244            }
245        };
246
247        Ok(Self {
248            internal: Arc::new(UnsharedMemoryInternal {
249                storage: StdMutex::new(storage),
250                local: Cell::new(local),
251            }),
252        })
253    }
254
255    /// Try to grow this memory by the given number of delta pages.
256    pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
257        let mut storage = self.internal.storage.lock().unwrap();
258
259        let mut local = self.internal.local.get();
260
261        let pages = match &mut *storage {
262            UnsharedMemoryStorage::Dynamic(dynamic_memory) => {
263                dynamic_memory.grow(delta, &mut local)
264            }
265            UnsharedMemoryStorage::Static(static_memory) => static_memory.grow(delta, &mut local),
266        };
267
268        self.internal.local.set(local);
269
270        pages
271    }
272
273    /// Size of this memory in pages.
274    pub fn size(&self) -> Pages {
275        let storage = self.internal.storage.lock().unwrap();
276
277        match &*storage {
278            UnsharedMemoryStorage::Dynamic(ref dynamic_memory) => dynamic_memory.size(),
279            UnsharedMemoryStorage::Static(ref static_memory) => static_memory.size(),
280        }
281    }
282
283    pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory {
284        self.internal.local.as_ptr()
285    }
286}
287
288impl Clone for UnsharedMemory {
289    fn clone(&self) -> Self {
290        UnsharedMemory {
291            internal: Arc::clone(&self.internal),
292        }
293    }
294}
295
296/// A reference to a shared memory.
297pub struct SharedMemory {
298    internal: Arc<SharedMemoryInternal>,
299}
300
301/// Data structure for a shared internal memory.
302pub struct SharedMemoryInternal {
303    memory: StdMutex<Box<StaticMemory>>,
304    local: Cell<vm::LocalMemory>,
305    lock: Mutex<()>,
306}
307
308// Manually implemented because SharedMemoryInternal uses `Cell` and is used in Arc;
309// this is safe because of `lock`; accesing `local` without locking `lock` is not safe (Maybe we could put the lock on Local then?)
310unsafe impl Sync for SharedMemoryInternal {}
311
312impl SharedMemory {
313    fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
314        let mut local = vm::LocalMemory {
315            base: std::ptr::null_mut(),
316            bound: 0,
317            memory: std::ptr::null_mut(),
318        };
319
320        let memory = StaticMemory::new(desc, &mut local)?;
321
322        Ok(Self {
323            internal: Arc::new(SharedMemoryInternal {
324                memory: StdMutex::new(memory),
325                local: Cell::new(local),
326                lock: Mutex::new(()),
327            }),
328        })
329    }
330
331    /// Try to grow this memory by the given number of delta pages.
332    pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
333        let _guard = self.internal.lock.lock();
334        let mut local = self.internal.local.get();
335        let mut memory = self.internal.memory.lock().unwrap();
336        let pages = memory.grow(delta, &mut local);
337        pages
338    }
339
340    /// Size of this memory in pages.
341    pub fn size(&self) -> Pages {
342        let _guard = self.internal.lock.lock();
343        let memory = self.internal.memory.lock().unwrap();
344        memory.size()
345    }
346
347    /// Gets a mutable pointer to the `LocalMemory`.
348    // This function is scary, because the mutex is not locked here
349    pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory {
350        self.internal.local.as_ptr()
351    }
352}
353
354impl Clone for SharedMemory {
355    fn clone(&self) -> Self {
356        SharedMemory {
357            internal: Arc::clone(&self.internal),
358        }
359    }
360}
361
362#[cfg(test)]
363mod memory_tests {
364
365    use super::{Memory, MemoryDescriptor, Pages};
366
367    #[test]
368    fn test_initial_memory_size() {
369        let memory_desc = MemoryDescriptor::new(Pages(10), Some(Pages(20)), false).unwrap();
370        let unshared_memory = Memory::new(memory_desc).unwrap();
371        assert_eq!(unshared_memory.size(), Pages(10));
372    }
373
374    #[test]
375    fn test_invalid_descriptor_returns_error() {
376        let memory_desc = MemoryDescriptor::new(Pages(10), None, true);
377        assert!(
378            memory_desc.is_err(),
379            "Max number of pages is required for shared memory"
380        )
381    }
382}