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