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