wasmtime/runtime/vm/
memory.rs

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
77use crate::prelude::*;
78use crate::runtime::store::StoreResourceLimiter;
79use crate::runtime::vm::vmcontext::VMMemoryDefinition;
80#[cfg(has_virtual_memory)]
81use crate::runtime::vm::{HostAlignedByteCount, MmapOffset};
82use crate::runtime::vm::{MemoryImage, MemoryImageSlot, SendSyncPtr};
83use alloc::sync::Arc;
84use core::{ops::Range, ptr::NonNull};
85use wasmtime_environ::Tunables;
86
87#[cfg(feature = "threads")]
88use wasmtime_environ::Trap;
89
90#[cfg(has_virtual_memory)]
91mod mmap;
92#[cfg(has_virtual_memory)]
93pub use self::mmap::MmapMemory;
94
95mod malloc;
96pub use self::malloc::MallocMemory;
97
98#[cfg(feature = "pooling-allocator")]
99mod static_;
100#[cfg(feature = "pooling-allocator")]
101use self::static_::StaticMemory;
102
103#[cfg(feature = "threads")]
104mod shared_memory;
105#[cfg(feature = "threads")]
106pub use shared_memory::SharedMemory;
107
108#[cfg(not(feature = "threads"))]
109mod shared_memory_disabled;
110#[cfg(not(feature = "threads"))]
111pub use shared_memory_disabled::SharedMemory;
112
113/// A memory allocator
114pub 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
126pub struct DefaultMemoryCreator;
127
128impl 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.
152pub 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)]
191pub 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
202impl 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.
227pub enum Memory {
228    Local(LocalMemory),
229    Shared(SharedMemory),
230}
231
232impl 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.
515pub 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
527impl 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                    slot.instantiate(alloc.byte_size(), Some(image), ty, tunables)?;
555                    Some(slot)
556                } else {
557                    None
558                }
559            }
560            #[cfg(not(has_virtual_memory))]
561            Some(_) => unreachable!(),
562            None => None,
563        };
564        Ok(LocalMemory {
565            ty: *ty,
566            alloc,
567            memory_may_move: ty.memory_may_move(tunables),
568            memory_image,
569            memory_guard_size: tunables.memory_guard_size.try_into().unwrap(),
570            memory_reservation: tunables.memory_reservation.try_into().unwrap(),
571        })
572    }
573
574    pub fn page_size(&self) -> u64 {
575        self.ty.page_size()
576    }
577
578    /// Grows a memory by `delta_pages`.
579    ///
580    /// This performs the necessary checks on the growth before delegating to
581    /// the underlying `grow_to` implementation.
582    ///
583    /// The `store` is used only for error reporting.
584    pub async fn grow(
585        &mut self,
586        delta_pages: u64,
587        mut limiter: Option<&mut StoreResourceLimiter<'_>>,
588    ) -> Result<Option<(usize, usize)>, Error> {
589        let old_byte_size = self.alloc.byte_size();
590
591        // Wasm spec: when growing by 0 pages, always return the current size.
592        if delta_pages == 0 {
593            return Ok(Some((old_byte_size, old_byte_size)));
594        }
595
596        let page_size = usize::try_from(self.page_size()).unwrap();
597
598        // The largest wasm-page-aligned region of memory is possible to
599        // represent in a `usize`. This will be impossible for the system to
600        // actually allocate.
601        let absolute_max = 0usize.wrapping_sub(page_size);
602
603        // Calculate the byte size of the new allocation. Let it overflow up to
604        // `usize::MAX`, then clamp it down to `absolute_max`.
605        let new_byte_size = usize::try_from(delta_pages)
606            .unwrap_or(usize::MAX)
607            .saturating_mul(page_size)
608            .saturating_add(old_byte_size)
609            .min(absolute_max);
610
611        let maximum = self
612            .ty
613            .maximum_byte_size()
614            .ok()
615            .and_then(|n| usize::try_from(n).ok());
616
617        // Store limiter gets first chance to reject memory_growing.
618        if let Some(limiter) = &mut limiter {
619            if !limiter
620                .memory_growing(old_byte_size, new_byte_size, maximum)
621                .await?
622            {
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(limiter) = limiter {
689                    limiter.memory_grow_failed(e)?;
690                }
691                Ok(None)
692            }
693        }
694    }
695
696    pub fn vmmemory(&self) -> VMMemoryDefinition {
697        self.alloc.vmmemory()
698    }
699
700    pub fn byte_size(&self) -> usize {
701        self.alloc.byte_size()
702    }
703
704    pub fn needs_init(&self) -> bool {
705        match &self.memory_image {
706            Some(image) => !image.has_image(),
707            None => true,
708        }
709    }
710
711    pub fn wasm_accessible(&self) -> Range<usize> {
712        let base = self.alloc.base().as_mut_ptr() as usize;
713        // From the base add:
714        //
715        // * max(capacity, reservation) -- all memory is guaranteed to have at
716        //   least `memory_reservation`, but capacity may go beyond that.
717        // * memory_guard_size - wasm is allowed to hit the guard page for
718        //   sigsegv for example.
719        //
720        // and this computes the range that wasm is allowed to load from and
721        // deterministically trap or succeed.
722        let end =
723            base + self.alloc.byte_capacity().max(self.memory_reservation) + self.memory_guard_size;
724        base..end
725    }
726
727    #[cfg(feature = "pooling-allocator")]
728    pub fn unwrap_static_image(self) -> MemoryImageSlot {
729        self.memory_image.unwrap()
730    }
731}
732
733/// In the configurations where bounds checks were elided in JIT code (because
734/// we are using static memories with virtual memory guard pages) this manual
735/// check is here so we don't segfault from Rust. For other configurations,
736/// these checks are required anyways.
737#[cfg(feature = "threads")]
738pub fn validate_atomic_addr(
739    def: &VMMemoryDefinition,
740    addr: u64,
741    access_size: u64,
742    access_alignment: u64,
743) -> Result<*mut u8, Trap> {
744    debug_assert!(access_alignment.is_power_of_two());
745    if !(addr % access_alignment == 0) {
746        return Err(Trap::HeapMisaligned);
747    }
748
749    let length = u64::try_from(def.current_length()).unwrap();
750    if !(addr.saturating_add(access_size) < length) {
751        return Err(Trap::MemoryOutOfBounds);
752    }
753
754    let addr = usize::try_from(addr).unwrap();
755    Ok(def.base.as_ptr().wrapping_add(addr))
756}