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