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