1 //! Memory management for linear memories.
2 //!
3 //! `RuntimeLinearMemory` is to WebAssembly linear memories what `Table` is to WebAssembly tables.
4 
5 use crate::runtime::vm::mmap::Mmap;
6 use crate::runtime::vm::vmcontext::VMMemoryDefinition;
7 use crate::runtime::vm::{
8     MemoryImage, MemoryImageSlot, SendSyncPtr, SharedMemory, Store, WaitResult,
9 };
10 use anyhow::Error;
11 use anyhow::{bail, format_err, Result};
12 use std::ops::Range;
13 use std::ptr::NonNull;
14 use std::sync::Arc;
15 use std::time::Instant;
16 use wasmtime_environ::{MemoryPlan, MemoryStyle, Trap, WASM32_MAX_PAGES, WASM64_MAX_PAGES};
17 
18 const WASM_PAGE_SIZE: usize = wasmtime_environ::WASM_PAGE_SIZE as usize;
19 const WASM_PAGE_SIZE_U64: u64 = wasmtime_environ::WASM_PAGE_SIZE as u64;
20 
21 /// A memory allocator
22 pub trait RuntimeMemoryCreator: Send + Sync {
23     /// Create new RuntimeLinearMemory
24     fn new_memory(
25         &self,
26         plan: &MemoryPlan,
27         minimum: usize,
28         maximum: Option<usize>,
29         // Optionally, a memory image for CoW backing.
30         memory_image: Option<&Arc<MemoryImage>>,
31     ) -> Result<Box<dyn RuntimeLinearMemory>>;
32 }
33 
34 /// A default memory allocator used by Wasmtime
35 pub struct DefaultMemoryCreator;
36 
37 impl RuntimeMemoryCreator for DefaultMemoryCreator {
38     /// Create new MmapMemory
39     fn new_memory(
40         &self,
41         plan: &MemoryPlan,
42         minimum: usize,
43         maximum: Option<usize>,
44         memory_image: Option<&Arc<MemoryImage>>,
45     ) -> Result<Box<dyn RuntimeLinearMemory>> {
46         Ok(Box::new(MmapMemory::new(
47             plan,
48             minimum,
49             maximum,
50             memory_image,
51         )?))
52     }
53 }
54 
55 /// A linear memory
56 pub trait RuntimeLinearMemory: Send + Sync {
57     /// Returns the number of allocated bytes.
58     fn byte_size(&self) -> usize;
59 
60     /// Returns the maximum number of bytes the memory can grow to.
61     /// Returns `None` if the memory is unbounded.
62     fn maximum_byte_size(&self) -> Option<usize>;
63 
64     /// Grows a memory by `delta_pages`.
65     ///
66     /// This performs the necessary checks on the growth before delegating to
67     /// the underlying `grow_to` implementation. A default implementation of
68     /// this memory is provided here since this is assumed to be the same for
69     /// most kinds of memory; one exception is shared memory, which must perform
70     /// all the steps of the default implementation *plus* the required locking.
71     ///
72     /// The `store` is used only for error reporting.
73     fn grow(
74         &mut self,
75         delta_pages: u64,
76         mut store: Option<&mut dyn Store>,
77     ) -> Result<Option<(usize, usize)>, Error> {
78         let old_byte_size = self.byte_size();
79 
80         // Wasm spec: when growing by 0 pages, always return the current size.
81         if delta_pages == 0 {
82             return Ok(Some((old_byte_size, old_byte_size)));
83         }
84 
85         // The largest wasm-page-aligned region of memory is possible to
86         // represent in a `usize`. This will be impossible for the system to
87         // actually allocate.
88         let absolute_max = 0usize.wrapping_sub(WASM_PAGE_SIZE);
89 
90         // Calculate the byte size of the new allocation. Let it overflow up to
91         // `usize::MAX`, then clamp it down to `absolute_max`.
92         let new_byte_size = usize::try_from(delta_pages)
93             .unwrap_or(usize::MAX)
94             .saturating_mul(WASM_PAGE_SIZE)
95             .saturating_add(old_byte_size);
96         let new_byte_size = if new_byte_size > absolute_max {
97             absolute_max
98         } else {
99             new_byte_size
100         };
101 
102         let maximum = self.maximum_byte_size();
103         // Store limiter gets first chance to reject memory_growing.
104         if let Some(store) = &mut store {
105             if !store.memory_growing(old_byte_size, new_byte_size, maximum)? {
106                 return Ok(None);
107             }
108         }
109 
110         // Never exceed maximum, even if limiter permitted it.
111         if let Some(max) = maximum {
112             if new_byte_size > max {
113                 if let Some(store) = store {
114                     // FIXME: shared memories may not have an associated store
115                     // to report the growth failure to but the error should not
116                     // be dropped
117                     // (https://github.com/bytecodealliance/wasmtime/issues/4240).
118                     store.memory_grow_failed(format_err!("Memory maximum size exceeded"))?;
119                 }
120                 return Ok(None);
121             }
122         }
123 
124         match self.grow_to(new_byte_size) {
125             Ok(_) => Ok(Some((old_byte_size, new_byte_size))),
126             Err(e) => {
127                 // FIXME: shared memories may not have an associated store to
128                 // report the growth failure to but the error should not be
129                 // dropped
130                 // (https://github.com/bytecodealliance/wasmtime/issues/4240).
131                 if let Some(store) = store {
132                     store.memory_grow_failed(e)?;
133                 }
134                 Ok(None)
135             }
136         }
137     }
138 
139     /// Grow memory to the specified amount of bytes.
140     ///
141     /// Returns an error if memory can't be grown by the specified amount
142     /// of bytes.
143     fn grow_to(&mut self, size: usize) -> Result<()>;
144 
145     /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm
146     /// code.
147     fn vmmemory(&mut self) -> VMMemoryDefinition;
148 
149     /// Does this memory need initialization? It may not if it already
150     /// has initial contents courtesy of the `MemoryImage` passed to
151     /// `RuntimeMemoryCreator::new_memory()`.
152     fn needs_init(&self) -> bool;
153 
154     /// Used for optional dynamic downcasting.
155     fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
156 
157     /// Returns the range of addresses that may be reached by WebAssembly.
158     ///
159     /// This starts at the base of linear memory and ends at the end of the
160     /// guard pages, if any.
161     fn wasm_accessible(&self) -> Range<usize>;
162 }
163 
164 /// A linear memory instance.
165 #[derive(Debug)]
166 pub struct MmapMemory {
167     // The underlying allocation.
168     mmap: Mmap,
169 
170     // The number of bytes that are accessible in `mmap` and available for
171     // reading and writing.
172     //
173     // This region starts at `pre_guard_size` offset from the base of `mmap`.
174     accessible: usize,
175 
176     // The optional maximum accessible size, in bytes, for this linear memory.
177     //
178     // Note that this maximum does not factor in guard pages, so this isn't the
179     // maximum size of the linear address space reservation for this memory.
180     maximum: Option<usize>,
181 
182     // The amount of extra bytes to reserve whenever memory grows. This is
183     // specified so that the cost of repeated growth is amortized.
184     extra_to_reserve_on_growth: usize,
185 
186     // Size in bytes of extra guard pages before the start and after the end to
187     // optimize loads and stores with constant offsets.
188     pre_guard_size: usize,
189     offset_guard_size: usize,
190 
191     // An optional CoW mapping that provides the initial content of this
192     // MmapMemory, if mapped.
193     memory_image: Option<MemoryImageSlot>,
194 }
195 
196 impl MmapMemory {
197     /// Create a new linear memory instance with specified minimum and maximum
198     /// number of wasm pages.
199     pub fn new(
200         plan: &MemoryPlan,
201         minimum: usize,
202         mut maximum: Option<usize>,
203         memory_image: Option<&Arc<MemoryImage>>,
204     ) -> Result<Self> {
205         // It's a programmer error for these two configuration values to exceed
206         // the host available address space, so panic if such a configuration is
207         // found (mostly an issue for hypothetical 32-bit hosts).
208         let offset_guard_bytes = usize::try_from(plan.offset_guard_size).unwrap();
209         let pre_guard_bytes = usize::try_from(plan.pre_guard_size).unwrap();
210 
211         let (alloc_bytes, extra_to_reserve_on_growth) = match plan.style {
212             // Dynamic memories start with the minimum size plus the `reserve`
213             // amount specified to grow into.
214             MemoryStyle::Dynamic { reserve } => (minimum, usize::try_from(reserve).unwrap()),
215 
216             // Static memories will never move in memory and consequently get
217             // their entire allocation up-front with no extra room to grow into.
218             // Note that the `maximum` is adjusted here to whatever the smaller
219             // of the two is, the `maximum` given or the `bound` specified for
220             // this memory.
221             MemoryStyle::Static { bound } => {
222                 assert!(bound >= plan.memory.minimum);
223                 let bound_bytes =
224                     usize::try_from(bound.checked_mul(WASM_PAGE_SIZE_U64).unwrap()).unwrap();
225                 maximum = Some(bound_bytes.min(maximum.unwrap_or(usize::MAX)));
226                 (bound_bytes, 0)
227             }
228         };
229 
230         let request_bytes = pre_guard_bytes
231             .checked_add(alloc_bytes)
232             .and_then(|i| i.checked_add(extra_to_reserve_on_growth))
233             .and_then(|i| i.checked_add(offset_guard_bytes))
234             .ok_or_else(|| format_err!("cannot allocate {} with guard regions", minimum))?;
235         let mut mmap = Mmap::accessible_reserved(0, request_bytes)?;
236 
237         if minimum > 0 {
238             mmap.make_accessible(pre_guard_bytes, minimum)?;
239         }
240 
241         // If a memory image was specified, try to create the MemoryImageSlot on
242         // top of our mmap.
243         let memory_image = match memory_image {
244             Some(image) => {
245                 let base = unsafe { mmap.as_mut_ptr().add(pre_guard_bytes) };
246                 let mut slot = MemoryImageSlot::create(
247                     base.cast(),
248                     minimum,
249                     alloc_bytes + extra_to_reserve_on_growth,
250                 );
251                 slot.instantiate(minimum, Some(image), &plan)?;
252                 // On drop, we will unmap our mmap'd range that this slot was
253                 // mapped on top of, so there is no need for the slot to wipe
254                 // it with an anonymous mapping first.
255                 slot.no_clear_on_drop();
256                 Some(slot)
257             }
258             None => None,
259         };
260 
261         Ok(Self {
262             mmap,
263             accessible: minimum,
264             maximum,
265             pre_guard_size: pre_guard_bytes,
266             offset_guard_size: offset_guard_bytes,
267             extra_to_reserve_on_growth,
268             memory_image,
269         })
270     }
271 }
272 
273 impl RuntimeLinearMemory for MmapMemory {
274     fn byte_size(&self) -> usize {
275         self.accessible
276     }
277 
278     fn maximum_byte_size(&self) -> Option<usize> {
279         self.maximum
280     }
281 
282     fn grow_to(&mut self, new_size: usize) -> Result<()> {
283         if new_size > self.mmap.len() - self.offset_guard_size - self.pre_guard_size {
284             // If the new size of this heap exceeds the current size of the
285             // allocation we have, then this must be a dynamic heap. Use
286             // `new_size` to calculate a new size of an allocation, allocate it,
287             // and then copy over the memory from before.
288             let request_bytes = self
289                 .pre_guard_size
290                 .checked_add(new_size)
291                 .and_then(|s| s.checked_add(self.extra_to_reserve_on_growth))
292                 .and_then(|s| s.checked_add(self.offset_guard_size))
293                 .ok_or_else(|| format_err!("overflow calculating size of memory allocation"))?;
294 
295             let mut new_mmap = Mmap::accessible_reserved(0, request_bytes)?;
296             new_mmap.make_accessible(self.pre_guard_size, new_size)?;
297 
298             // This method has an exclusive reference to `self.mmap` and just
299             // created `new_mmap` so it should be safe to acquire references
300             // into both of them and copy between them.
301             unsafe {
302                 let range = self.pre_guard_size..self.pre_guard_size + self.accessible;
303                 let src = self.mmap.slice(range.clone());
304                 let dst = new_mmap.slice_mut(range);
305                 dst.copy_from_slice(src);
306             }
307 
308             // Now drop the MemoryImageSlot, if any. We've lost the CoW
309             // advantages by explicitly copying all data, but we have
310             // preserved all of its content; so we no longer need the
311             // mapping. We need to do this before we (implicitly) drop the
312             // `mmap` field by overwriting it below.
313             drop(self.memory_image.take());
314 
315             self.mmap = new_mmap;
316         } else if let Some(image) = self.memory_image.as_mut() {
317             // MemoryImageSlot has its own growth mechanisms; defer to its
318             // implementation.
319             image.set_heap_limit(new_size)?;
320         } else {
321             // If the new size of this heap fits within the existing allocation
322             // then all we need to do is to make the new pages accessible. This
323             // can happen either for "static" heaps which always hit this case,
324             // or "dynamic" heaps which have some space reserved after the
325             // initial allocation to grow into before the heap is moved in
326             // memory.
327             assert!(new_size > self.accessible);
328             self.mmap.make_accessible(
329                 self.pre_guard_size + self.accessible,
330                 new_size - self.accessible,
331             )?;
332         }
333 
334         self.accessible = new_size;
335 
336         Ok(())
337     }
338 
339     fn vmmemory(&mut self) -> VMMemoryDefinition {
340         VMMemoryDefinition {
341             base: unsafe { self.mmap.as_mut_ptr().add(self.pre_guard_size) },
342             current_length: self.accessible.into(),
343         }
344     }
345 
346     fn needs_init(&self) -> bool {
347         // If we're using a CoW mapping, then no initialization
348         // is needed.
349         self.memory_image.is_none()
350     }
351 
352     fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
353         self
354     }
355 
356     fn wasm_accessible(&self) -> Range<usize> {
357         let base = self.mmap.as_ptr() as usize + self.pre_guard_size;
358         let end = base + (self.mmap.len() - self.pre_guard_size);
359         base..end
360     }
361 }
362 
363 /// A "static" memory where the lifetime of the backing memory is managed
364 /// elsewhere. Currently used with the pooling allocator.
365 struct StaticMemory {
366     /// The base pointer of this static memory, wrapped up in a send/sync
367     /// wrapper.
368     base: SendSyncPtr<u8>,
369 
370     /// The byte capacity of the `base` pointer.
371     capacity: usize,
372 
373     /// The current size, in bytes, of this memory.
374     size: usize,
375 
376     /// The size, in bytes, of the virtual address allocation starting at `base`
377     /// and going to the end of the guard pages at the end of the linear memory.
378     memory_and_guard_size: usize,
379 
380     /// The image management, if any, for this memory. Owned here and
381     /// returned to the pooling allocator when termination occurs.
382     memory_image: MemoryImageSlot,
383 }
384 
385 impl StaticMemory {
386     fn new(
387         base_ptr: *mut u8,
388         base_capacity: usize,
389         initial_size: usize,
390         maximum_size: Option<usize>,
391         memory_image: MemoryImageSlot,
392         memory_and_guard_size: usize,
393     ) -> Result<Self> {
394         if base_capacity < initial_size {
395             bail!(
396                 "initial memory size of {} exceeds the pooling allocator's \
397                  configured maximum memory size of {} bytes",
398                 initial_size,
399                 base_capacity,
400             );
401         }
402 
403         // Only use the part of the slice that is necessary.
404         let base_capacity = match maximum_size {
405             Some(max) if max < base_capacity => max,
406             _ => base_capacity,
407         };
408 
409         Ok(Self {
410             base: SendSyncPtr::new(NonNull::new(base_ptr).unwrap()),
411             capacity: base_capacity,
412             size: initial_size,
413             memory_image,
414             memory_and_guard_size,
415         })
416     }
417 }
418 
419 impl RuntimeLinearMemory for StaticMemory {
420     fn byte_size(&self) -> usize {
421         self.size
422     }
423 
424     fn maximum_byte_size(&self) -> Option<usize> {
425         Some(self.capacity)
426     }
427 
428     fn grow_to(&mut self, new_byte_size: usize) -> Result<()> {
429         // Never exceed the static memory size; this check should have been made
430         // prior to arriving here.
431         assert!(new_byte_size <= self.capacity);
432 
433         self.memory_image.set_heap_limit(new_byte_size)?;
434 
435         // Update our accounting of the available size.
436         self.size = new_byte_size;
437         Ok(())
438     }
439 
440     fn vmmemory(&mut self) -> VMMemoryDefinition {
441         VMMemoryDefinition {
442             base: self.base.as_ptr(),
443             current_length: self.size.into(),
444         }
445     }
446 
447     fn needs_init(&self) -> bool {
448         !self.memory_image.has_image()
449     }
450 
451     fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
452         self
453     }
454 
455     fn wasm_accessible(&self) -> Range<usize> {
456         let base = self.base.as_ptr() as usize;
457         let end = base + self.memory_and_guard_size;
458         base..end
459     }
460 }
461 
462 /// Representation of a runtime wasm linear memory.
463 pub struct Memory(pub(crate) Box<dyn RuntimeLinearMemory>);
464 
465 impl Memory {
466     /// Create a new dynamic (movable) memory instance for the specified plan.
467     pub fn new_dynamic(
468         plan: &MemoryPlan,
469         creator: &dyn RuntimeMemoryCreator,
470         store: &mut dyn Store,
471         memory_image: Option<&Arc<MemoryImage>>,
472     ) -> Result<Self> {
473         let (minimum, maximum) = Self::limit_new(plan, Some(store))?;
474         let allocation = creator.new_memory(plan, minimum, maximum, memory_image)?;
475         let allocation = if plan.memory.shared {
476             Box::new(SharedMemory::wrap(plan, allocation, plan.memory)?)
477         } else {
478             allocation
479         };
480         Ok(Memory(allocation))
481     }
482 
483     /// Create a new static (immovable) memory instance for the specified plan.
484     pub fn new_static(
485         plan: &MemoryPlan,
486         base_ptr: *mut u8,
487         base_capacity: usize,
488         memory_image: MemoryImageSlot,
489         memory_and_guard_size: usize,
490         store: &mut dyn Store,
491     ) -> Result<Self> {
492         let (minimum, maximum) = Self::limit_new(plan, Some(store))?;
493         let pooled_memory = StaticMemory::new(
494             base_ptr,
495             base_capacity,
496             minimum,
497             maximum,
498             memory_image,
499             memory_and_guard_size,
500         )?;
501         let allocation = Box::new(pooled_memory);
502         let allocation: Box<dyn RuntimeLinearMemory> = if plan.memory.shared {
503             // FIXME: since the pooling allocator owns the memory allocation
504             // (which is torn down with the instance), the current shared memory
505             // implementation will cause problems; see
506             // https://github.com/bytecodealliance/wasmtime/issues/4244.
507             todo!("using shared memory with the pooling allocator is a work in progress");
508         } else {
509             allocation
510         };
511         Ok(Memory(allocation))
512     }
513 
514     /// Calls the `store`'s limiter to optionally prevent a memory from being allocated.
515     ///
516     /// Returns the minimum size and optional maximum size of the memory, in
517     /// bytes.
518     pub(crate) fn limit_new(
519         plan: &MemoryPlan,
520         store: Option<&mut dyn Store>,
521     ) -> Result<(usize, Option<usize>)> {
522         // Sanity-check what should already be true from wasm module validation.
523         let absolute_max = if plan.memory.memory64 {
524             WASM64_MAX_PAGES
525         } else {
526             WASM32_MAX_PAGES
527         };
528         assert!(plan.memory.minimum <= absolute_max);
529         assert!(plan.memory.maximum.is_none() || plan.memory.maximum.unwrap() <= absolute_max);
530 
531         // This is the absolute possible maximum that the module can try to
532         // allocate, which is our entire address space minus a wasm page. That
533         // shouldn't ever actually work in terms of an allocation because
534         // presumably the kernel wants *something* for itself, but this is used
535         // to pass to the `store`'s limiter for a requested size
536         // to approximate the scale of the request that the wasm module is
537         // making. This is necessary because the limiter works on `usize` bytes
538         // whereas we're working with possibly-overflowing `u64` calculations
539         // here. To actually faithfully represent the byte requests of modules
540         // we'd have to represent things as `u128`, but that's kinda
541         // overkill for this purpose.
542         let absolute_max = 0usize.wrapping_sub(WASM_PAGE_SIZE);
543 
544         // If the minimum memory size overflows the size of our own address
545         // space, then we can't satisfy this request, but defer the error to
546         // later so the `store` can be informed that an effective oom is
547         // happening.
548         let minimum = plan
549             .memory
550             .minimum
551             .checked_mul(WASM_PAGE_SIZE_U64)
552             .and_then(|m| usize::try_from(m).ok());
553 
554         // The plan stores the maximum size in units of wasm pages, but we
555         // use units of bytes. Unlike for the `minimum` size we silently clamp
556         // the effective maximum size to `absolute_max` above if the maximum is
557         // too large. This should be ok since as a wasm runtime we get to
558         // arbitrarily decide the actual maximum size of memory, regardless of
559         // what's actually listed on the memory itself.
560         let mut maximum = plan.memory.maximum.map(|max| {
561             usize::try_from(max)
562                 .ok()
563                 .and_then(|m| m.checked_mul(WASM_PAGE_SIZE))
564                 .unwrap_or(absolute_max)
565         });
566 
567         // If this is a 32-bit memory and no maximum is otherwise listed then we
568         // need to still specify a maximum size of 4GB. If the host platform is
569         // 32-bit then there's no need to limit the maximum this way since no
570         // allocation of 4GB can succeed, but for 64-bit platforms this is
571         // required to limit memories to 4GB.
572         if !plan.memory.memory64 && maximum.is_none() {
573             maximum = usize::try_from(1u64 << 32).ok();
574         }
575 
576         // Inform the store's limiter what's about to happen. This will let the
577         // limiter reject anything if necessary, and this also guarantees that
578         // we should call the limiter for all requested memories, even if our
579         // `minimum` calculation overflowed. This means that the `minimum` we're
580         // informing the limiter is lossy and may not be 100% accurate, but for
581         // now the expected uses of limiter means that's ok.
582         if let Some(store) = store {
583             // We ignore the store limits for shared memories since they are
584             // technically not created within a store (though, trickily, they
585             // may be associated with one in order to get a `vmctx`).
586             if !plan.memory.shared {
587                 if !store.memory_growing(0, minimum.unwrap_or(absolute_max), maximum)? {
588                     bail!(
589                         "memory minimum size of {} pages exceeds memory limits",
590                         plan.memory.minimum
591                     );
592                 }
593             }
594         }
595 
596         // At this point we need to actually handle overflows, so bail out with
597         // an error if we made it this far.
598         let minimum = minimum.ok_or_else(|| {
599             format_err!(
600                 "memory minimum size of {} pages exceeds memory limits",
601                 plan.memory.minimum
602             )
603         })?;
604         Ok((minimum, maximum))
605     }
606 
607     /// Returns the number of allocated wasm pages.
608     pub fn byte_size(&self) -> usize {
609         self.0.byte_size()
610     }
611 
612     /// Returns whether or not this memory needs initialization. It
613     /// may not if it already has initial content thanks to a CoW
614     /// mechanism.
615     pub(crate) fn needs_init(&self) -> bool {
616         self.0.needs_init()
617     }
618 
619     /// Grow memory by the specified amount of wasm pages.
620     ///
621     /// Returns `None` if memory can't be grown by the specified amount
622     /// of wasm pages. Returns `Some` with the old size of memory, in bytes, on
623     /// successful growth.
624     ///
625     /// # Safety
626     ///
627     /// Resizing the memory can reallocate the memory buffer for dynamic memories.
628     /// An instance's `VMContext` may have pointers to the memory's base and will
629     /// need to be fixed up after growing the memory.
630     ///
631     /// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates
632     /// this unsafety.
633     ///
634     /// Ensure that the provided Store is not used to get access any Memory
635     /// which lives inside it.
636     pub unsafe fn grow(
637         &mut self,
638         delta_pages: u64,
639         store: Option<&mut dyn Store>,
640     ) -> Result<Option<usize>, Error> {
641         self.0
642             .grow(delta_pages, store)
643             .map(|opt| opt.map(|(old, _new)| old))
644     }
645 
646     /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.
647     pub fn vmmemory(&mut self) -> VMMemoryDefinition {
648         self.0.vmmemory()
649     }
650 
651     /// Consume the memory, returning its [`MemoryImageSlot`] if any is present.
652     /// The image should only be present for a subset of memories created with
653     /// [`Memory::new_static()`].
654     #[cfg(feature = "pooling-allocator")]
655     pub fn unwrap_static_image(mut self) -> MemoryImageSlot {
656         let mem = self.0.as_any_mut().downcast_mut::<StaticMemory>().unwrap();
657         std::mem::replace(&mut mem.memory_image, MemoryImageSlot::dummy())
658     }
659 
660     /// If the [Memory] is a [SharedMemory], unwrap it and return a clone to
661     /// that shared memory.
662     pub fn as_shared_memory(&mut self) -> Option<&mut SharedMemory> {
663         let as_any = self.0.as_any_mut();
664         if let Some(m) = as_any.downcast_mut::<SharedMemory>() {
665             Some(m)
666         } else {
667             None
668         }
669     }
670 
671     /// Implementation of `memory.atomic.notify` for all memories.
672     pub fn atomic_notify(&mut self, addr: u64, count: u32) -> Result<u32, Trap> {
673         match self.0.as_any_mut().downcast_mut::<SharedMemory>() {
674             Some(m) => m.atomic_notify(addr, count),
675             None => {
676                 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?;
677                 Ok(0)
678             }
679         }
680     }
681 
682     /// Implementation of `memory.atomic.wait32` for all memories.
683     pub fn atomic_wait32(
684         &mut self,
685         addr: u64,
686         expected: u32,
687         deadline: Option<Instant>,
688     ) -> Result<WaitResult, Trap> {
689         match self.0.as_any_mut().downcast_mut::<SharedMemory>() {
690             Some(m) => m.atomic_wait32(addr, expected, deadline),
691             None => {
692                 validate_atomic_addr(&self.vmmemory(), addr, 4, 4)?;
693                 Err(Trap::AtomicWaitNonSharedMemory)
694             }
695         }
696     }
697 
698     /// Implementation of `memory.atomic.wait64` for all memories.
699     pub fn atomic_wait64(
700         &mut self,
701         addr: u64,
702         expected: u64,
703         deadline: Option<Instant>,
704     ) -> Result<WaitResult, Trap> {
705         match self.0.as_any_mut().downcast_mut::<SharedMemory>() {
706             Some(m) => m.atomic_wait64(addr, expected, deadline),
707             None => {
708                 validate_atomic_addr(&self.vmmemory(), addr, 8, 8)?;
709                 Err(Trap::AtomicWaitNonSharedMemory)
710             }
711         }
712     }
713 
714     /// Returns the range of bytes that WebAssembly should be able to address in
715     /// this linear memory. Note that this includes guard pages which wasm can
716     /// hit.
717     pub fn wasm_accessible(&self) -> Range<usize> {
718         self.0.wasm_accessible()
719     }
720 }
721 
722 /// In the configurations where bounds checks were elided in JIT code (because
723 /// we are using static memories with virtual memory guard pages) this manual
724 /// check is here so we don't segfault from Rust. For other configurations,
725 /// these checks are required anyways.
726 pub fn validate_atomic_addr(
727     def: &VMMemoryDefinition,
728     addr: u64,
729     access_size: u64,
730     access_alignment: u64,
731 ) -> Result<*mut u8, Trap> {
732     debug_assert!(access_alignment.is_power_of_two());
733     if !(addr % access_alignment == 0) {
734         return Err(Trap::HeapMisaligned);
735     }
736 
737     let length = u64::try_from(def.current_length()).unwrap();
738     if !(addr.saturating_add(access_size) < length) {
739         return Err(Trap::MemoryOutOfBounds);
740     }
741 
742     Ok(def.base.wrapping_add(addr as usize))
743 }
744