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