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