1 //! Memory management for linear memories.
2 //!
3 //! This module implements the runtime data structures that manage linear
4 //! memories for WebAssembly. There's a number of types here each with various
5 //! purposes, and this is the high level relationships between types where an
6 //! arrow here means "builds on top of".
7 //!
8 //! ```text
9 //! ┌─────────────────────┐
10 //! │                     │
11 //! │        Memory       ├─────────────┐
12 //! │                     │             │
13 //! └──────────┬──────────┘             │
14 //!            │                        │
15 //!            │                        │
16 //!            ▼                        ▼
17 //! ┌─────────────────────┐     ┌──────────────┐
18 //! │                     │     │              │
19 //! │     LocalMemory     │◄────┤ SharedMemory │
20 //! │                     │     │              │
21 //! └──────────┬──────────┘     └──────────────┘
22 //!            │
23 //!            │
24 //!            ▼
25 //! ┌─────────────────────┐
26 //! │                     │
27 //! │ RuntimeLinearMemory ├─────────────┬───────────────┐
28 //! │                     │             │               │
29 //! └──────────┬──────────┘             │               │
30 //!            │                        │               │
31 //!            │                        │               │
32 //!            ▼                        ▼               ▼
33 //! ┌─────────────────────┐     ┌──────────────┐     ┌─────┐
34 //! │                     │     │              │     │     │
35 //! │      MmapMemory     │     │ StaticMemory │     │ ... │
36 //! │                     │     │              │     │     │
37 //! └─────────────────────┘     └──────────────┘     └─────┘
38 //! ```
39 //!
40 //! In more detail:
41 //!
42 //! * `Memory` - the root of what's actually stored in a wasm instance. This
43 //!   implements the high-level embedder APIs one would expect from a wasm
44 //!   linear memory.
45 //!
46 //! * `SharedMemory` - this is one of the variants of a local memory. A shared
47 //!   memory contains `RwLock<LocalMemory>` where all the real bits happen
48 //!   within the lock.
49 //!
50 //! * `LocalMemory` - this is an owned allocation of a linear memory which
51 //!   maintains low-level state that's shared between `SharedMemory` and the
52 //!   instance-local state of `Memory`. One example is that `LocalMemory::grow`
53 //!   has most of the logic around memory growth.
54 //!
55 //! * `RuntimeLinearMemory` - this is a trait which `LocalMemory` delegates to.
56 //!   This trait is intentionally relatively simple to be exposed in Wasmtime's
57 //!   embedder API. This is exposed all the way through `wasmtime::Config` so
58 //!   embedders can provide arbitrary implementations.
59 //!
60 //! * `MmapMemory` - this is an implementation of `RuntimeLinearMemory` in terms
61 //!   of the platform's mmap primitive.
62 //!
63 //! * `StaticMemory` - this is an implementation of `RuntimeLinearMemory`
64 //!   for the pooling allocator where the base pointer is already allocated
65 //!   and contents are managed through `MemoryImageSlot`.
66 //!
67 //! Other important types for memories are `MemoryImage` and `MemoryImageSlot`
68 //! which manage CoW state for memories. This is implemented at the
69 //! `LocalMemory` layer.
70 //!
71 //! FIXME: don't have both RuntimeLinearMemory and wasmtime::LinearMemory, they
72 //! should be merged together.
73 //!
74 //! FIXME: don't have both RuntimeMemoryCreator and wasmtime::MemoryCreator,
75 //! they should be merged together.
76 
77 use crate::Engine;
78 use crate::prelude::*;
79 use crate::runtime::store::StoreResourceLimiter;
80 use crate::runtime::vm::vmcontext::VMMemoryDefinition;
81 #[cfg(has_virtual_memory)]
82 use crate::runtime::vm::{HostAlignedByteCount, MmapOffset};
83 use crate::runtime::vm::{MemoryImage, MemoryImageSlot, SendSyncPtr};
84 use alloc::sync::Arc;
85 use core::{ops::Range, ptr::NonNull};
86 use wasmtime_environ::Tunables;
87 
88 #[cfg(feature = "threads")]
89 use wasmtime_environ::Trap;
90 
91 #[cfg(has_virtual_memory)]
92 mod mmap;
93 #[cfg(has_virtual_memory)]
94 pub use self::mmap::MmapMemory;
95 
96 mod malloc;
97 pub use self::malloc::MallocMemory;
98 
99 #[cfg(feature = "pooling-allocator")]
100 mod static_;
101 #[cfg(feature = "pooling-allocator")]
102 use self::static_::StaticMemory;
103 
104 #[cfg(feature = "threads")]
105 mod shared_memory;
106 #[cfg(feature = "threads")]
107 pub use shared_memory::SharedMemory;
108 
109 #[cfg(not(feature = "threads"))]
110 mod shared_memory_disabled;
111 #[cfg(not(feature = "threads"))]
112 pub use shared_memory_disabled::SharedMemory;
113 
114 /// A memory allocator
115 pub trait RuntimeMemoryCreator: Send + Sync {
116     /// Create new RuntimeLinearMemory
new_memory( &self, ty: &wasmtime_environ::Memory, tunables: &Tunables, minimum: usize, maximum: Option<usize>, ) -> Result<Box<dyn RuntimeLinearMemory>>117     fn new_memory(
118         &self,
119         ty: &wasmtime_environ::Memory,
120         tunables: &Tunables,
121         minimum: usize,
122         maximum: Option<usize>,
123     ) -> Result<Box<dyn RuntimeLinearMemory>>;
124 }
125 
126 /// A default memory allocator used by Wasmtime
127 pub struct DefaultMemoryCreator;
128 
129 impl RuntimeMemoryCreator for DefaultMemoryCreator {
130     /// Create new MmapMemory
new_memory( &self, ty: &wasmtime_environ::Memory, tunables: &Tunables, minimum: usize, maximum: Option<usize>, ) -> Result<Box<dyn RuntimeLinearMemory>>131     fn new_memory(
132         &self,
133         ty: &wasmtime_environ::Memory,
134         tunables: &Tunables,
135         minimum: usize,
136         maximum: Option<usize>,
137     ) -> Result<Box<dyn RuntimeLinearMemory>> {
138         #[cfg(has_virtual_memory)]
139         if tunables.signals_based_traps
140             || tunables.memory_guard_size > 0
141             || tunables.memory_reservation > 0
142             || tunables.memory_init_cow
143         {
144             return Ok(
145                 try_new::<Box<_>>(MmapMemory::new(ty, tunables, minimum, maximum)?)?
146                     as Box<dyn RuntimeLinearMemory>,
147             );
148         }
149 
150         let _ = maximum;
151         Ok(
152             try_new::<Box<_>>(MallocMemory::new(ty, tunables, minimum)?)?
153                 as Box<dyn RuntimeLinearMemory>,
154         )
155     }
156 }
157 
158 /// A linear memory and its backing storage.
159 pub trait RuntimeLinearMemory: Send + Sync {
160     /// Returns the number bytes that this linear memory can access.
byte_size(&self) -> usize161     fn byte_size(&self) -> usize;
162 
163     /// Returns the maximal number of bytes the current allocation can access.
164     ///
165     /// Growth up to this value should not relocate the base pointer.
byte_capacity(&self) -> usize166     fn byte_capacity(&self) -> usize;
167 
168     /// Grow memory to the specified amount of bytes.
169     ///
170     /// Returns an error if memory can't be grown by the specified amount
171     /// of bytes.
grow_to(&mut self, size: usize) -> Result<()>172     fn grow_to(&mut self, size: usize) -> Result<()>;
173 
174     /// Returns a pointer to the base of this linear memory allocation.
175     ///
176     /// This is either a raw pointer, or a reference to an mmap along with an
177     /// offset within it.
base(&self) -> MemoryBase178     fn base(&self) -> MemoryBase;
179 
180     /// Get a `VMMemoryDefinition` for this linear memory.
vmmemory(&self) -> VMMemoryDefinition181     fn vmmemory(&self) -> VMMemoryDefinition;
182 
183     /// Internal method for Wasmtime when used in conjunction with CoW images.
184     /// This is used to inform the underlying memory that the size of memory has
185     /// changed.
186     ///
187     /// Note that this is hidden and panics by default as embedders using custom
188     /// memory without CoW images shouldn't have to worry about this.
189     #[doc(hidden)]
set_byte_size(&mut self, len: usize)190     fn set_byte_size(&mut self, len: usize) {
191         let _ = len;
192         panic!("CoW images used with this memory and it doesn't support it");
193     }
194 }
195 
196 /// The base pointer of a memory allocation.
197 #[derive(Clone, Debug)]
198 pub enum MemoryBase {
199     /// A raw pointer into memory.
200     ///
201     /// This may or may not be host-page-aligned.
202     Raw(SendSyncPtr<u8>),
203 
204     /// An mmap along with an offset into it.
205     #[cfg(has_virtual_memory)]
206     Mmap(MmapOffset),
207 }
208 
209 impl MemoryBase {
210     /// Creates a new `MemoryBase` from a raw pointer.
211     ///
212     /// The pointer must be non-null, and it must be logically `Send + Sync`.
new_raw(ptr: *mut u8) -> Self213     pub fn new_raw(ptr: *mut u8) -> Self {
214         Self::Raw(NonNull::new(ptr).expect("pointer is non-null").into())
215     }
216 
217     /// Returns the actual memory address in memory that is represented by this
218     /// base.
as_non_null(&self) -> NonNull<u8>219     pub fn as_non_null(&self) -> NonNull<u8> {
220         match self {
221             Self::Raw(ptr) => ptr.as_non_null(),
222             #[cfg(has_virtual_memory)]
223             Self::Mmap(mmap_offset) => mmap_offset.as_non_null(),
224         }
225     }
226 
227     /// Same as `as_non_null`, but different return type.
as_mut_ptr(&self) -> *mut u8228     pub fn as_mut_ptr(&self) -> *mut u8 {
229         self.as_non_null().as_ptr()
230     }
231 }
232 
233 /// Representation of a runtime wasm linear memory.
234 pub enum Memory {
235     Local(LocalMemory),
236     Shared(SharedMemory),
237 }
238 
239 impl Memory {
240     /// Create a new dynamic (movable) memory instance for the specified plan.
new_dynamic( ty: &wasmtime_environ::Memory, engine: &Engine, creator: &dyn RuntimeMemoryCreator, memory_image: Option<&Arc<MemoryImage>>, limiter: Option<&mut StoreResourceLimiter<'_>>, ) -> Result<Self>241     pub async fn new_dynamic(
242         ty: &wasmtime_environ::Memory,
243         engine: &Engine,
244         creator: &dyn RuntimeMemoryCreator,
245         memory_image: Option<&Arc<MemoryImage>>,
246         limiter: Option<&mut StoreResourceLimiter<'_>>,
247     ) -> Result<Self> {
248         let (minimum, maximum) = Self::limit_new(ty, limiter).await?;
249         let tunables = engine.tunables();
250         let allocation = creator.new_memory(ty, tunables, minimum, maximum)?;
251 
252         let memory = LocalMemory::new(ty, tunables, allocation, memory_image)?;
253         Ok(if ty.shared {
254             Memory::Shared(SharedMemory::wrap(engine, ty, memory)?)
255         } else {
256             Memory::Local(memory)
257         })
258     }
259 
260     /// Create a new static (immovable) memory instance for the specified plan.
261     #[cfg(feature = "pooling-allocator")]
new_static( ty: &wasmtime_environ::Memory, tunables: &Tunables, base: MemoryBase, base_capacity: usize, memory_image: MemoryImageSlot, limiter: Option<&mut StoreResourceLimiter<'_>>, ) -> Result<Self>262     pub async fn new_static(
263         ty: &wasmtime_environ::Memory,
264         tunables: &Tunables,
265         base: MemoryBase,
266         base_capacity: usize,
267         memory_image: MemoryImageSlot,
268         limiter: Option<&mut StoreResourceLimiter<'_>>,
269     ) -> Result<Self> {
270         let (minimum, maximum) = Self::limit_new(ty, limiter).await?;
271         let pooled_memory = StaticMemory::new(base, base_capacity, minimum, maximum)?;
272         let allocation = Box::new(pooled_memory);
273 
274         // Configure some defaults a bit differently for this memory within the
275         // `LocalMemory` structure created, notably we already have
276         // `memory_image` and regardless of configuration settings this memory
277         // can't move its base pointer since it's a fixed allocation.
278         let mut memory = LocalMemory::new(ty, tunables, allocation, None)?;
279         assert!(memory.memory_image.is_none());
280         memory.memory_image = Some(memory_image);
281         memory.memory_may_move = false;
282 
283         Ok(if ty.shared {
284             // FIXME(#4244): not supported with the pooling allocator (which
285             // `new_static` is always used with), see `MemoryPool::validate` as
286             // well).
287             todo!("using shared memory with the pooling allocator is a work in progress");
288         } else {
289             Memory::Local(memory)
290         })
291     }
292 
293     /// Calls the `store`'s limiter to optionally prevent a memory from being allocated.
294     ///
295     /// Returns a tuple of the minimum size, optional maximum size, and log(page
296     /// size) of the memory, all in bytes.
limit_new( ty: &wasmtime_environ::Memory, limiter: Option<&mut StoreResourceLimiter<'_>>, ) -> Result<(usize, Option<usize>)>297     pub(crate) async fn limit_new(
298         ty: &wasmtime_environ::Memory,
299         limiter: Option<&mut StoreResourceLimiter<'_>>,
300     ) -> Result<(usize, Option<usize>)> {
301         let page_size = usize::try_from(ty.page_size()).unwrap();
302 
303         // This is the absolute possible maximum that the module can try to
304         // allocate, which is our entire address space minus a wasm page. That
305         // shouldn't ever actually work in terms of an allocation because
306         // presumably the kernel wants *something* for itself, but this is used
307         // to pass to the `store`'s limiter for a requested size
308         // to approximate the scale of the request that the wasm module is
309         // making. This is necessary because the limiter works on `usize` bytes
310         // whereas we're working with possibly-overflowing `u64` calculations
311         // here. To actually faithfully represent the byte requests of modules
312         // we'd have to represent things as `u128`, but that's kinda
313         // overkill for this purpose.
314         let absolute_max = 0usize.wrapping_sub(page_size);
315 
316         // If the minimum memory size overflows the size of our own address
317         // space, then we can't satisfy this request, but defer the error to
318         // later so the `store` can be informed that an effective oom is
319         // happening.
320         let minimum = ty
321             .minimum_byte_size()
322             .ok()
323             .and_then(|m| usize::try_from(m).ok());
324 
325         // The plan stores the maximum size in units of wasm pages, but we
326         // use units of bytes. Unlike for the `minimum` size we silently clamp
327         // the effective maximum size to the limits of what we can track. If the
328         // maximum size exceeds `usize` or `u64` then there's no need to further
329         // keep track of it as some sort of runtime limit will kick in long
330         // before we reach the statically declared maximum size.
331         let maximum = ty
332             .maximum_byte_size()
333             .ok()
334             .and_then(|m| usize::try_from(m).ok());
335 
336         // Inform the store's limiter what's about to happen. This will let the
337         // limiter reject anything if necessary, and this also guarantees that
338         // we should call the limiter for all requested memories, even if our
339         // `minimum` calculation overflowed. This means that the `minimum` we're
340         // informing the limiter is lossy and may not be 100% accurate, but for
341         // now the expected uses of limiter means that's ok.
342         if let Some(limiter) = limiter {
343             if !limiter
344                 .memory_growing(0, minimum.unwrap_or(absolute_max), maximum)
345                 .await?
346             {
347                 bail!(
348                     "memory minimum size of {} pages exceeds memory limits",
349                     ty.limits.min
350                 );
351             }
352         }
353 
354         // At this point we need to actually handle overflows, so bail out with
355         // an error if we made it this far.
356         let minimum = minimum.ok_or_else(|| {
357             format_err!(
358                 "memory minimum size of {} pages exceeds memory limits",
359                 ty.limits.min
360             )
361         })?;
362 
363         if !ty.allow_growth_to(minimum) {
364             bail!(
365                 "memory minimum size of {} pages exceeds memory limits",
366                 ty.limits.min
367             );
368         }
369 
370         Ok((minimum, maximum))
371     }
372 
373     /// Returns this memory's page size, in bytes.
page_size(&self) -> u64374     pub fn page_size(&self) -> u64 {
375         self.ty().page_size()
376     }
377 
378     /// Returns the size of this memory, in bytes.
byte_size(&self) -> usize379     pub fn byte_size(&self) -> usize {
380         match self {
381             Memory::Local(mem) => mem.byte_size(),
382             Memory::Shared(mem) => mem.byte_size(),
383         }
384     }
385 
386     /// Returns whether or not this memory needs initialization. It
387     /// may not if it already has initial content thanks to a CoW
388     /// mechanism.
needs_init(&self) -> bool389     pub(crate) fn needs_init(&self) -> bool {
390         match self {
391             Memory::Local(mem) => mem.needs_init(),
392             Memory::Shared(mem) => mem.needs_init(),
393         }
394     }
395 
396     /// Grow memory by the specified amount of wasm pages.
397     ///
398     /// Returns `None` if memory can't be grown by the specified amount
399     /// of wasm pages. Returns `Some` with the old size of memory, in bytes, on
400     /// successful growth.
401     ///
402     /// # Safety
403     ///
404     /// Resizing the memory can reallocate the memory buffer for dynamic memories.
405     /// An instance's `VMContext` may have pointers to the memory's base and will
406     /// need to be fixed up after growing the memory.
407     ///
408     /// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates
409     /// this unsafety.
410     ///
411     /// Ensure that the provided Store is not used to get access any Memory
412     /// which lives inside it.
grow( &mut self, delta_pages: u64, limiter: Option<&mut StoreResourceLimiter<'_>>, ) -> Result<Option<usize>, Error>413     pub async unsafe fn grow(
414         &mut self,
415         delta_pages: u64,
416         limiter: Option<&mut StoreResourceLimiter<'_>>,
417     ) -> Result<Option<usize>, Error> {
418         let new_size = delta_pages
419             .checked_mul(self.page_size())
420             .and_then(|new_bytes| {
421                 let new_bytes = usize::try_from(new_bytes).ok()?;
422                 self.byte_size().checked_add(new_bytes)
423             });
424         match new_size {
425             Some(new_size) => {
426                 // FIXME(WebAssembly/custom-page-sizes#45) - what should the
427                 // behavior here be exactly? A trap? Return -1? A smaller limit?
428                 // Unclear!
429                 //
430                 // Trap for now to make this as noisy/conservative as possible.
431                 if !self.ty().allow_growth_to(new_size) {
432                     bail!(
433                         "disallowing growth to {new_size:#x} bytes based on \
434                          page size"
435                     )
436                 }
437             }
438 
439             // If the new size in memory isn't representable in a `usize` then
440             // there's no need to actually try to grow it to that size. It's
441             // impossible to succeed so just fail it early.
442             None => {
443                 if let Some(limiter) = limiter {
444                     let err = crate::format_err!("memory growth exceeds address space");
445                     limiter.memory_grow_failed(err)?;
446                 }
447                 return Ok(None);
448             }
449         }
450 
451         let result = match self {
452             Memory::Local(mem) => mem.grow(delta_pages, limiter).await?,
453             Memory::Shared(mem) => mem.grow(delta_pages)?,
454         };
455         match result {
456             Some((old, _new)) => Ok(Some(old)),
457             None => Ok(None),
458         }
459     }
460 
461     /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
vmmemory(&self) -> VMMemoryDefinition462     pub fn vmmemory(&self) -> VMMemoryDefinition {
463         match self {
464             Memory::Local(mem) => mem.vmmemory(),
465             // `vmmemory()` is used for writing the `VMMemoryDefinition` of a
466             // memory into its `VMContext`; this should never be possible for a
467             // shared memory because the only `VMMemoryDefinition` for it should
468             // be stored in its own `def` field.
469             Memory::Shared(_) => unreachable!(),
470         }
471     }
472 
473     /// Consume the memory, returning its [`MemoryImageSlot`] if any is present.
474     /// The image should only be present for a subset of memories created with
475     /// [`Memory::new_static()`].
476     #[cfg(feature = "pooling-allocator")]
unwrap_static_image(self) -> MemoryImageSlot477     pub fn unwrap_static_image(self) -> MemoryImageSlot {
478         match self {
479             Memory::Local(mem) => mem.unwrap_static_image(),
480             Memory::Shared(_) => panic!("expected a local memory"),
481         }
482     }
483 
484     /// Is this a shared memory?
is_shared_memory(&self) -> bool485     pub fn is_shared_memory(&self) -> bool {
486         matches!(self, Memory::Shared(_))
487     }
488 
489     /// If the [Memory] is a [SharedMemory], unwrap it and return a clone to
490     /// that shared memory.
as_shared_memory(&self) -> Option<&SharedMemory>491     pub fn as_shared_memory(&self) -> Option<&SharedMemory> {
492         match self {
493             Memory::Local(_) => None,
494             Memory::Shared(mem) => Some(mem),
495         }
496     }
497 
498     /// Implementation of `memory.atomic.notify` for all memories.
499     #[cfg(feature = "threads")]
atomic_notify(&mut self, addr: u64, count: u32) -> Result<u32, Trap>500     pub fn atomic_notify(&mut self, addr: u64, count: u32) -> Result<u32, Trap> {
501         match self.as_shared_memory() {
502             Some(m) => m.atomic_notify(addr, count),
503             None => {
504                 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?;
505                 Ok(0)
506             }
507         }
508     }
509 
510     /// Implementation of `memory.atomic.wait32` for all memories.
511     #[cfg(feature = "threads")]
atomic_wait32( &mut self, addr: u64, expected: u32, timeout: Option<core::time::Duration>, ) -> Result<crate::WaitResult, Trap>512     pub fn atomic_wait32(
513         &mut self,
514         addr: u64,
515         expected: u32,
516         timeout: Option<core::time::Duration>,
517     ) -> Result<crate::WaitResult, Trap> {
518         match self.as_shared_memory() {
519             Some(m) => m.atomic_wait32(addr, expected, timeout),
520             None => {
521                 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?;
522                 Err(Trap::AtomicWaitNonSharedMemory)
523             }
524         }
525     }
526 
527     /// Implementation of `memory.atomic.wait64` for all memories.
528     #[cfg(feature = "threads")]
atomic_wait64( &mut self, addr: u64, expected: u64, timeout: Option<core::time::Duration>, ) -> Result<crate::WaitResult, Trap>529     pub fn atomic_wait64(
530         &mut self,
531         addr: u64,
532         expected: u64,
533         timeout: Option<core::time::Duration>,
534     ) -> Result<crate::WaitResult, Trap> {
535         match self.as_shared_memory() {
536             Some(m) => m.atomic_wait64(addr, expected, timeout),
537             None => {
538                 validate_atomic_addr(&self.vmmemory(), addr, 8, 8)?;
539                 Err(Trap::AtomicWaitNonSharedMemory)
540             }
541         }
542     }
543 
544     /// Returns the range of bytes that WebAssembly should be able to address in
545     /// this linear memory. Note that this includes guard pages which wasm can
546     /// hit.
wasm_accessible(&self) -> Range<usize>547     pub fn wasm_accessible(&self) -> Range<usize> {
548         match self {
549             Memory::Local(mem) => mem.wasm_accessible(),
550             Memory::Shared(mem) => mem.wasm_accessible(),
551         }
552     }
553 
ty(&self) -> &wasmtime_environ::Memory554     fn ty(&self) -> &wasmtime_environ::Memory {
555         match self {
556             Memory::Local(mem) => mem.ty(),
557             Memory::Shared(mem) => mem.ty(),
558         }
559     }
560 }
561 
562 /// An owned allocation of a wasm linear memory.
563 ///
564 /// This might be part of a `Memory` via `Memory::Local` but it might also be
565 /// the implementation basis for a `SharedMemory` behind an `RwLock` for
566 /// example.
567 pub struct LocalMemory {
568     alloc: Box<dyn RuntimeLinearMemory>,
569     ty: wasmtime_environ::Memory,
570     memory_may_move: bool,
571     memory_guard_size: usize,
572     memory_reservation: usize,
573 
574     /// An optional CoW mapping that provides the initial content of this
575     /// memory.
576     memory_image: Option<MemoryImageSlot>,
577 }
578 
579 impl LocalMemory {
new( ty: &wasmtime_environ::Memory, tunables: &Tunables, alloc: Box<dyn RuntimeLinearMemory>, memory_image: Option<&Arc<MemoryImage>>, ) -> Result<LocalMemory>580     pub fn new(
581         ty: &wasmtime_environ::Memory,
582         tunables: &Tunables,
583         alloc: Box<dyn RuntimeLinearMemory>,
584         memory_image: Option<&Arc<MemoryImage>>,
585     ) -> Result<LocalMemory> {
586         // If a memory image was specified, try to create the MemoryImageSlot on
587         // top of our mmap.
588         let memory_image = match memory_image {
589             #[cfg(has_virtual_memory)]
590             Some(image) => {
591                 // We currently don't support memory_image if
592                 // `RuntimeLinearMemory::byte_size` is not a multiple of the host page
593                 // size. See https://github.com/bytecodealliance/wasmtime/issues/9660.
594                 if let Ok(byte_size) = HostAlignedByteCount::new(alloc.byte_size()) {
595                     // memory_image is CoW-based so it is expected to be backed
596                     // by an mmap.
597                     let mmap_base = match alloc.base() {
598                         MemoryBase::Mmap(offset) => offset,
599                         MemoryBase::Raw { .. } => {
600                             unreachable!("memory_image is Some only for mmap-based memories")
601                         }
602                     };
603 
604                     let mut slot =
605                         MemoryImageSlot::create(mmap_base, byte_size, alloc.byte_capacity());
606                     slot.instantiate(alloc.byte_size(), Some(image), ty, tunables)?;
607                     Some(slot)
608                 } else {
609                     None
610                 }
611             }
612             #[cfg(not(has_virtual_memory))]
613             Some(_) => unreachable!(),
614             None => None,
615         };
616         Ok(LocalMemory {
617             ty: *ty,
618             alloc,
619             memory_may_move: ty.memory_may_move(tunables),
620             memory_image,
621             memory_guard_size: tunables.memory_guard_size.try_into().unwrap(),
622             memory_reservation: tunables.memory_reservation.try_into().unwrap(),
623         })
624     }
625 
ty(&self) -> &wasmtime_environ::Memory626     pub fn ty(&self) -> &wasmtime_environ::Memory {
627         &self.ty
628     }
629 
630     /// Grows a memory by `delta_pages`.
631     ///
632     /// This performs the necessary checks on the growth before delegating to
633     /// the underlying `grow_to` implementation.
634     ///
635     /// The `store` is used only for error reporting.
grow( &mut self, delta_pages: u64, mut limiter: Option<&mut StoreResourceLimiter<'_>>, ) -> Result<Option<(usize, usize)>, Error>636     pub async fn grow(
637         &mut self,
638         delta_pages: u64,
639         mut limiter: Option<&mut StoreResourceLimiter<'_>>,
640     ) -> Result<Option<(usize, usize)>, Error> {
641         let old_byte_size = self.alloc.byte_size();
642 
643         // Wasm spec: when growing by 0 pages, always return the current size.
644         if delta_pages == 0 {
645             return Ok(Some((old_byte_size, old_byte_size)));
646         }
647 
648         let page_size = usize::try_from(self.ty().page_size()).unwrap();
649 
650         // The largest wasm-page-aligned region of memory is possible to
651         // represent in a `usize`. This will be impossible for the system to
652         // actually allocate.
653         let absolute_max = 0usize.wrapping_sub(page_size);
654 
655         // Calculate the byte size of the new allocation. Let it overflow up to
656         // `usize::MAX`, then clamp it down to `absolute_max`.
657         let new_byte_size = usize::try_from(delta_pages)
658             .unwrap_or(usize::MAX)
659             .saturating_mul(page_size)
660             .saturating_add(old_byte_size)
661             .min(absolute_max);
662 
663         let maximum = self
664             .ty
665             .maximum_byte_size()
666             .ok()
667             .and_then(|n| usize::try_from(n).ok());
668 
669         // Store limiter gets first chance to reject memory_growing.
670         if let Some(limiter) = &mut limiter {
671             if !limiter
672                 .memory_growing(old_byte_size, new_byte_size, maximum)
673                 .await?
674             {
675                 return Ok(None);
676             }
677         }
678 
679         // Save the original base pointer to assert the invariant that growth up
680         // to the byte capacity never relocates the base pointer.
681         let base_ptr_before = self.alloc.base().as_mut_ptr();
682         let required_to_not_move_memory = new_byte_size <= self.alloc.byte_capacity();
683 
684         let result = (|| -> Result<()> {
685             // Never exceed maximum, even if limiter permitted it.
686             if let Some(max) = maximum {
687                 if new_byte_size > max {
688                     bail!("Memory maximum size exceeded");
689                 }
690             }
691 
692             // If memory isn't allowed to move then don't let growth happen
693             // beyond the initial capacity
694             if !self.memory_may_move && new_byte_size > self.alloc.byte_capacity() {
695                 bail!("Memory maximum size exceeded");
696             }
697 
698             // If we have a CoW image overlay then let it manage accessible
699             // bytes. Once the heap limit is modified inform the underlying
700             // allocation that the size has changed.
701             //
702             // If the growth is going beyond the size of the heap image then
703             // discard it. This should only happen for `MmapMemory` where
704             // `no_clear_on_drop` is set so the destructor doesn't do anything.
705             // For now be maximally sure about this by asserting that memory can
706             // indeed move and that we're on unix. If this wants to run
707             // somewhere else like Windows or with other allocations this may
708             // need adjusting.
709             if let Some(image) = &mut self.memory_image {
710                 if new_byte_size <= self.alloc.byte_capacity() {
711                     image.set_heap_limit(new_byte_size)?;
712                     self.alloc.set_byte_size(new_byte_size);
713                     return Ok(());
714                 }
715                 assert!(cfg!(unix));
716                 assert!(self.memory_may_move);
717                 self.memory_image = None;
718             }
719 
720             // And failing all that fall back to the underlying allocation to
721             // grow it.
722             self.alloc.grow_to(new_byte_size)
723         })();
724 
725         match result {
726             Ok(()) => {
727                 // On successful growth double-check that the base pointer
728                 // didn't move if it shouldn't have.
729                 if required_to_not_move_memory {
730                     assert_eq!(base_ptr_before, self.alloc.base().as_mut_ptr());
731                 }
732 
733                 Ok(Some((old_byte_size, new_byte_size)))
734             }
735             Err(e) => {
736                 // FIXME: shared memories may not have an associated store to
737                 // report the growth failure to but the error should not be
738                 // dropped
739                 // (https://github.com/bytecodealliance/wasmtime/issues/4240).
740                 if let Some(limiter) = limiter {
741                     limiter.memory_grow_failed(e)?;
742                 }
743                 Ok(None)
744             }
745         }
746     }
747 
vmmemory(&self) -> VMMemoryDefinition748     pub fn vmmemory(&self) -> VMMemoryDefinition {
749         self.alloc.vmmemory()
750     }
751 
byte_size(&self) -> usize752     pub fn byte_size(&self) -> usize {
753         self.alloc.byte_size()
754     }
755 
needs_init(&self) -> bool756     pub fn needs_init(&self) -> bool {
757         match &self.memory_image {
758             Some(image) => !image.has_image(),
759             None => true,
760         }
761     }
762 
wasm_accessible(&self) -> Range<usize>763     pub fn wasm_accessible(&self) -> Range<usize> {
764         let base = self.alloc.base().as_mut_ptr() as usize;
765         // From the base add:
766         //
767         // * max(capacity, reservation) -- all memory is guaranteed to have at
768         //   least `memory_reservation`, but capacity may go beyond that.
769         // * memory_guard_size - wasm is allowed to hit the guard page for
770         //   sigsegv for example.
771         //
772         // and this computes the range that wasm is allowed to load from and
773         // deterministically trap or succeed.
774         let end =
775             base + self.alloc.byte_capacity().max(self.memory_reservation) + self.memory_guard_size;
776         base..end
777     }
778 
779     #[cfg(feature = "pooling-allocator")]
unwrap_static_image(self) -> MemoryImageSlot780     pub fn unwrap_static_image(self) -> MemoryImageSlot {
781         self.memory_image.unwrap()
782     }
783 }
784 
785 /// In the configurations where bounds checks were elided in JIT code (because
786 /// we are using static memories with virtual memory guard pages) this manual
787 /// check is here so we don't segfault from Rust. For other configurations,
788 /// these checks are required anyways.
789 #[cfg(feature = "threads")]
validate_atomic_addr( def: &VMMemoryDefinition, addr: u64, access_size: u64, access_alignment: u64, ) -> Result<*mut u8, Trap>790 pub fn validate_atomic_addr(
791     def: &VMMemoryDefinition,
792     addr: u64,
793     access_size: u64,
794     access_alignment: u64,
795 ) -> Result<*mut u8, Trap> {
796     debug_assert!(access_alignment.is_power_of_two());
797     if !(addr % access_alignment == 0) {
798         return Err(Trap::HeapMisaligned);
799     }
800 
801     let length = u64::try_from(def.current_length()).unwrap();
802     if !(addr.saturating_add(access_size) <= length) {
803         return Err(Trap::MemoryOutOfBounds);
804     }
805 
806     let addr = usize::try_from(addr).unwrap();
807     Ok(def.base.as_ptr().wrapping_add(addr))
808 }
809