Skip to main content

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