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