1 //! Memory management for tables.
2 //!
3 //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
4 
5 use crate::prelude::*;
6 use crate::runtime::store::StoreResourceLimiter;
7 use crate::runtime::vm::stack_switching::VMContObj;
8 use crate::runtime::vm::vmcontext::{VMFuncRef, VMTableDefinition};
9 use crate::runtime::vm::{GcStore, SendSyncPtr, VMGcRef, VmPtr};
10 use core::alloc::Layout;
11 use core::mem;
12 use core::ops::Range;
13 use core::ptr::{self, NonNull};
14 use core::slice;
15 use core::{cmp, usize};
16 use wasmtime_environ::{
17     FUNCREF_INIT_BIT, FUNCREF_MASK, IndexType, Trap, Tunables, WasmHeapTopType, WasmRefType,
18 };
19 
20 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
21 pub enum TableElementType {
22     Func,
23     GcRef,
24     Cont,
25 }
26 
27 impl TableElementType {
28     /// Returns the size required to actually store an element of this particular type
29     pub fn element_size(&self) -> usize {
30         match self {
31             TableElementType::Func => core::mem::size_of::<FuncTableElem>(),
32             TableElementType::GcRef => core::mem::size_of::<Option<VMGcRef>>(),
33             TableElementType::Cont => core::mem::size_of::<ContTableElem>(),
34         }
35     }
36 }
37 
38 /// At-rest representation of a function in a funcref table.
39 ///
40 /// Note that whether or not these pointers are tagged is a property of `Engine`
41 /// configuration. Also note that this specifically uses `VmPtr<T>` to handle
42 /// provenance here when loading/storing values to a table.
43 ///
44 /// The possible values here are:
45 ///
46 /// * `None` for untagged tables - a null function element
47 /// * `Some(_)` for untagged tables - a non-null function element
48 /// * `None` for tagged tables - an uninitialized element
49 /// * `Some(1)` for tagged tables - a null function element
50 /// * `Some(addr | 1)` for tagged tables - a non-null function element
51 #[derive(Copy, Clone)]
52 #[repr(transparent)]
53 struct MaybeTaggedFuncRef(Option<VmPtr<VMFuncRef>>);
54 
55 impl MaybeTaggedFuncRef {
56     /// Converts the given `ptr`, a valid funcref pointer, into a tagged pointer
57     /// by adding in the `FUNCREF_INIT_BIT`.
58     fn from(ptr: Option<NonNull<VMFuncRef>>, lazy_init: bool) -> Self {
59         let maybe_tagged = if lazy_init {
60             Some(match ptr {
61                 Some(ptr) => ptr.map_addr(|a| a | FUNCREF_INIT_BIT),
62                 None => NonNull::new(core::ptr::without_provenance_mut(FUNCREF_INIT_BIT)).unwrap(),
63             })
64         } else {
65             ptr
66         };
67         MaybeTaggedFuncRef(maybe_tagged.map(Into::into))
68     }
69 
70     /// Converts a tagged pointer into a `TableElement`, returning `UninitFunc`
71     /// for null (not a tagged value) or `FuncRef` for otherwise tagged values.
72     fn into_funcref(self, lazy_init: bool) -> Option<Option<NonNull<VMFuncRef>>> {
73         let ptr = self.0;
74         if lazy_init && ptr.is_none() {
75             None
76         } else {
77             // Masking off the tag bit is harmless whether the table uses lazy
78             // init or not.
79             Some(ptr.and_then(|ptr| NonNull::new(ptr.as_ptr().map_addr(|a| a & FUNCREF_MASK))))
80         }
81     }
82 }
83 
84 pub type FuncTableElem = Option<SendSyncPtr<VMFuncRef>>;
85 pub type ContTableElem = Option<VMContObj>;
86 
87 /// The maximum of the sizes of any of the table element types
88 #[cfg(feature = "pooling-allocator")]
89 pub const NOMINAL_MAX_TABLE_ELEM_SIZE: usize = {
90     // ContTableElem intentionally excluded for "nominal" calculation.
91     let sizes = [
92         core::mem::size_of::<FuncTableElem>(),
93         core::mem::size_of::<Option<VMGcRef>>(),
94     ];
95 
96     // This is equivalent to `|data| {data.iter().reduce(std::cmp::max).unwrap()}`,
97     // but as a `const` function, so we can use it to define a constant.
98     const fn slice_max(data: &[usize]) -> usize {
99         match data {
100             [] => 0,
101             [head, tail @ ..] => {
102                 let tail_max = slice_max(tail);
103                 if *head >= tail_max { *head } else { tail_max }
104             }
105         }
106     }
107 
108     slice_max(&sizes)
109 };
110 
111 pub enum StaticTable {
112     Func(StaticFuncTable),
113     GcRef(StaticGcRefTable),
114     Cont(StaticContTable),
115 }
116 
117 impl From<StaticFuncTable> for StaticTable {
118     fn from(value: StaticFuncTable) -> Self {
119         Self::Func(value)
120     }
121 }
122 
123 impl From<StaticGcRefTable> for StaticTable {
124     fn from(value: StaticGcRefTable) -> Self {
125         Self::GcRef(value)
126     }
127 }
128 
129 impl From<StaticContTable> for StaticTable {
130     fn from(value: StaticContTable) -> Self {
131         Self::Cont(value)
132     }
133 }
134 
135 pub struct StaticFuncTable {
136     /// Where data for this table is stored. The length of this list is the
137     /// maximum size of the table.
138     data: SendSyncPtr<[FuncTableElem]>,
139     /// The current size of the table.
140     size: usize,
141     /// Whether elements of this table are initialized lazily.
142     lazy_init: bool,
143 }
144 
145 pub struct StaticGcRefTable {
146     /// Where data for this table is stored. The length of this list is the
147     /// maximum size of the table.
148     data: SendSyncPtr<[Option<VMGcRef>]>,
149     /// The current size of the table.
150     size: usize,
151 }
152 
153 pub struct StaticContTable {
154     /// Where data for this table is stored. The length of this list is the
155     /// maximum size of the table.
156     data: SendSyncPtr<[ContTableElem]>,
157     /// The current size of the table.
158     size: usize,
159 }
160 
161 pub enum DynamicTable {
162     Func(DynamicFuncTable),
163     GcRef(DynamicGcRefTable),
164     Cont(DynamicContTable),
165 }
166 
167 impl From<DynamicFuncTable> for DynamicTable {
168     fn from(value: DynamicFuncTable) -> Self {
169         Self::Func(value)
170     }
171 }
172 
173 impl From<DynamicGcRefTable> for DynamicTable {
174     fn from(value: DynamicGcRefTable) -> Self {
175         Self::GcRef(value)
176     }
177 }
178 
179 impl From<DynamicContTable> for DynamicTable {
180     fn from(value: DynamicContTable) -> Self {
181         Self::Cont(value)
182     }
183 }
184 
185 pub struct DynamicFuncTable {
186     /// Dynamically managed storage space for this table. The length of this
187     /// vector is the current size of the table.
188     elements: Vec<FuncTableElem>,
189     /// Maximum size that `elements` can grow to.
190     maximum: Option<usize>,
191     /// Whether elements of this table are initialized lazily.
192     lazy_init: bool,
193 }
194 
195 pub struct DynamicGcRefTable {
196     /// Dynamically managed storage space for this table. The length of this
197     /// vector is the current size of the table.
198     elements: Vec<Option<VMGcRef>>,
199     /// Maximum size that `elements` can grow to.
200     maximum: Option<usize>,
201 }
202 
203 pub struct DynamicContTable {
204     /// Dynamically managed storage space for this table. The length of this
205     /// vector is the current size of the table.
206     elements: Vec<ContTableElem>,
207     /// Maximum size that `elements` can grow to.
208     maximum: Option<usize>,
209 }
210 
211 /// Represents an instance's table.
212 pub enum Table {
213     /// A "static" table where storage space is managed externally, currently
214     /// used with the pooling allocator.
215     Static(StaticTable),
216     /// A "dynamic" table where table storage space is dynamically allocated via
217     /// `malloc` (aka Rust's `Vec`).
218     Dynamic(DynamicTable),
219 }
220 
221 impl From<StaticTable> for Table {
222     fn from(value: StaticTable) -> Self {
223         Self::Static(value)
224     }
225 }
226 
227 impl From<StaticFuncTable> for Table {
228     fn from(value: StaticFuncTable) -> Self {
229         let t: StaticTable = value.into();
230         t.into()
231     }
232 }
233 
234 impl From<StaticGcRefTable> for Table {
235     fn from(value: StaticGcRefTable) -> Self {
236         let t: StaticTable = value.into();
237         t.into()
238     }
239 }
240 
241 impl From<StaticContTable> for Table {
242     fn from(value: StaticContTable) -> Self {
243         let t: StaticTable = value.into();
244         t.into()
245     }
246 }
247 
248 impl From<DynamicTable> for Table {
249     fn from(value: DynamicTable) -> Self {
250         Self::Dynamic(value)
251     }
252 }
253 
254 impl From<DynamicFuncTable> for Table {
255     fn from(value: DynamicFuncTable) -> Self {
256         let t: DynamicTable = value.into();
257         t.into()
258     }
259 }
260 
261 impl From<DynamicGcRefTable> for Table {
262     fn from(value: DynamicGcRefTable) -> Self {
263         let t: DynamicTable = value.into();
264         t.into()
265     }
266 }
267 
268 impl From<DynamicContTable> for Table {
269     fn from(value: DynamicContTable) -> Self {
270         let t: DynamicTable = value.into();
271         t.into()
272     }
273 }
274 
275 pub(crate) fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
276     match ty.heap_type.top() {
277         WasmHeapTopType::Func => TableElementType::Func,
278         WasmHeapTopType::Any | WasmHeapTopType::Extern => TableElementType::GcRef,
279         WasmHeapTopType::Cont => TableElementType::Cont,
280         WasmHeapTopType::Exn => TableElementType::GcRef,
281     }
282 }
283 
284 /// Allocate dynamic table elements of the given length.
285 ///
286 /// Relies on the fact that our tables' elements are initialized to `None`,
287 /// which is represented by zero, to allocate pre-zeroed memory from the global
288 /// allocator and avoid manual zero-initialization.
289 ///
290 /// # Safety
291 ///
292 /// Should only ever be called with a `T` that is a table element type and where
293 /// `Option<T>`'s `None` variant is represented with zero.
294 unsafe fn alloc_dynamic_table_elements<T>(len: usize) -> Result<Vec<Option<T>>> {
295     debug_assert!(
296         unsafe {
297             core::mem::MaybeUninit::<Option<T>>::zeroed()
298                 .assume_init()
299                 .is_none()
300         },
301         "null table elements are represented with zeroed memory"
302     );
303 
304     if len == 0 {
305         return Ok(vec![]);
306     }
307 
308     let align = mem::align_of::<Option<T>>();
309 
310     let size = mem::size_of::<Option<T>>();
311     let size = size.next_multiple_of(align);
312     let size = size.checked_mul(len).unwrap();
313 
314     let layout = Layout::from_size_align(size, align)?;
315 
316     let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
317     ensure!(!ptr.is_null(), "failed to allocate memory for table");
318 
319     let elems = unsafe { Vec::<Option<T>>::from_raw_parts(ptr.cast(), len, len) };
320     debug_assert!(elems.iter().all(|e| e.is_none()));
321 
322     Ok(elems)
323 }
324 
325 impl Table {
326     /// Create a new dynamic (movable) table instance for the specified table plan.
327     pub async fn new_dynamic(
328         ty: &wasmtime_environ::Table,
329         tunables: &Tunables,
330         limiter: Option<&mut StoreResourceLimiter<'_>>,
331     ) -> Result<Self> {
332         let (minimum, maximum) = Self::limit_new(ty, limiter).await?;
333         match wasm_to_table_type(ty.ref_type) {
334             TableElementType::Func => Ok(Self::from(DynamicFuncTable {
335                 elements: unsafe { alloc_dynamic_table_elements(minimum)? },
336                 maximum,
337                 lazy_init: tunables.table_lazy_init,
338             })),
339             TableElementType::GcRef => Ok(Self::from(DynamicGcRefTable {
340                 elements: unsafe { alloc_dynamic_table_elements(minimum)? },
341                 maximum,
342             })),
343             TableElementType::Cont => Ok(Self::from(DynamicContTable {
344                 elements: vec![None; minimum],
345                 maximum,
346             })),
347         }
348     }
349 
350     /// Create a new static (immovable) table instance for the specified table plan.
351     pub async unsafe fn new_static(
352         ty: &wasmtime_environ::Table,
353         tunables: &Tunables,
354         data: SendSyncPtr<[u8]>,
355         limiter: Option<&mut StoreResourceLimiter<'_>>,
356     ) -> Result<Self> {
357         let (minimum, maximum) = Self::limit_new(ty, limiter).await?;
358         let size = minimum;
359         let max = maximum.unwrap_or(usize::MAX);
360 
361         match wasm_to_table_type(ty.ref_type) {
362             TableElementType::Func => {
363                 let len = {
364                     let (before, data, after) = unsafe {
365                         let data = data.as_non_null().as_ref();
366                         data.align_to::<FuncTableElem>()
367                     };
368                     assert!(before.is_empty());
369                     assert!(after.is_empty());
370                     data.len()
371                 };
372                 ensure!(
373                     usize::try_from(ty.limits.min).unwrap() <= len,
374                     "initial table size of {} exceeds the pooling allocator's \
375                      configured maximum table size of {len} elements",
376                     ty.limits.min,
377                 );
378                 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
379                     data.as_non_null().cast::<FuncTableElem>(),
380                     cmp::min(len, max),
381                 ));
382                 Ok(Self::from(StaticFuncTable {
383                     data,
384                     size,
385                     lazy_init: tunables.table_lazy_init,
386                 }))
387             }
388             TableElementType::GcRef => {
389                 let len = {
390                     let (before, data, after) = unsafe {
391                         let data = data.as_non_null().as_ref();
392                         data.align_to::<Option<VMGcRef>>()
393                     };
394                     assert!(before.is_empty());
395                     assert!(after.is_empty());
396                     data.len()
397                 };
398                 ensure!(
399                     usize::try_from(ty.limits.min).unwrap() <= len,
400                     "initial table size of {} exceeds the pooling allocator's \
401                      configured maximum table size of {len} elements",
402                     ty.limits.min,
403                 );
404                 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
405                     data.as_non_null().cast::<Option<VMGcRef>>(),
406                     cmp::min(len, max),
407                 ));
408                 Ok(Self::from(StaticGcRefTable { data, size }))
409             }
410             TableElementType::Cont => {
411                 let len = {
412                     let (before, data, after) = unsafe {
413                         let data = data.as_non_null().as_ref();
414                         data.align_to::<ContTableElem>()
415                     };
416                     assert!(before.is_empty());
417                     assert!(after.is_empty());
418                     data.len()
419                 };
420                 ensure!(
421                     usize::try_from(ty.limits.min).unwrap() <= len,
422                     "initial table size of {} exceeds the pooling allocator's \
423                      configured maximum table size of {len} elements",
424                     ty.limits.min,
425                 );
426                 let data = SendSyncPtr::new(NonNull::slice_from_raw_parts(
427                     data.as_non_null().cast::<ContTableElem>(),
428                     cmp::min(len, max),
429                 ));
430                 Ok(Self::from(StaticContTable { data, size }))
431             }
432         }
433     }
434 
435     // Calls the `store`'s limiter to optionally prevent the table from being created.
436     //
437     // Returns the minimum and maximum size of the table if the table can be created.
438     async fn limit_new(
439         ty: &wasmtime_environ::Table,
440         limiter: Option<&mut StoreResourceLimiter<'_>>,
441     ) -> Result<(usize, Option<usize>)> {
442         // No matter how the table limits are specified
443         // The table size is limited by the host's pointer size
444         let absolute_max = usize::MAX;
445 
446         // If the minimum overflows the host's pointer size, then we can't satisfy this request.
447         // We defer the error to later so the `store` can be informed.
448         let minimum = usize::try_from(ty.limits.min).ok();
449 
450         // The maximum size of the table is limited by:
451         // * the host's pointer size.
452         // * the table's maximum size if defined.
453         // * if the table is 64-bit.
454         let maximum = match (ty.limits.max, ty.idx_type) {
455             (Some(max), _) => usize::try_from(max).ok(),
456             (None, IndexType::I64) => usize::try_from(u64::MAX).ok(),
457             (None, IndexType::I32) => usize::try_from(u32::MAX).ok(),
458         };
459 
460         // Inform the store's limiter what's about to happen.
461         if let Some(limiter) = limiter {
462             if !limiter
463                 .table_growing(0, minimum.unwrap_or(absolute_max), maximum)
464                 .await?
465             {
466                 bail!(
467                     "table minimum size of {} elements exceeds table limits",
468                     ty.limits.min
469                 );
470             }
471         }
472 
473         // At this point we need to actually handle overflows, so bail out with
474         // an error if we made it this far.
475         let minimum = minimum.ok_or_else(|| {
476             format_err!(
477                 "table minimum size of {} elements exceeds table limits",
478                 ty.limits.min
479             )
480         })?;
481         Ok((minimum, maximum))
482     }
483 
484     /// Returns the type of the elements in this table.
485     pub fn element_type(&self) -> TableElementType {
486         match self {
487             Table::Static(StaticTable::Func(_)) | Table::Dynamic(DynamicTable::Func(_)) => {
488                 TableElementType::Func
489             }
490             Table::Static(StaticTable::GcRef(_)) | Table::Dynamic(DynamicTable::GcRef(_)) => {
491                 TableElementType::GcRef
492             }
493             Table::Static(StaticTable::Cont(_)) | Table::Dynamic(DynamicTable::Cont(_)) => {
494                 TableElementType::Cont
495             }
496         }
497     }
498 
499     /// Returns whether or not the underlying storage of the table is "static".
500     #[cfg(feature = "pooling-allocator")]
501     pub(crate) fn is_static(&self) -> bool {
502         matches!(self, Table::Static(_))
503     }
504 
505     /// Returns the number of allocated elements.
506     pub fn size(&self) -> usize {
507         match self {
508             Table::Static(StaticTable::Func(StaticFuncTable { size, .. })) => *size,
509             Table::Static(StaticTable::GcRef(StaticGcRefTable { size, .. })) => *size,
510             Table::Static(StaticTable::Cont(StaticContTable { size, .. })) => *size,
511             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => elements.len(),
512             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
513                 elements.len()
514             }
515             Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => elements.len(),
516         }
517     }
518 
519     /// Returns the maximum number of elements at runtime.
520     ///
521     /// Returns `None` if the table is unbounded.
522     ///
523     /// The runtime maximum may not be equal to the maximum from the table's Wasm type
524     /// when it is being constrained by an instance allocator.
525     pub fn maximum(&self) -> Option<usize> {
526         match self {
527             Table::Static(StaticTable::Cont(StaticContTable { data, .. })) => Some(data.len()),
528             Table::Static(StaticTable::Func(StaticFuncTable { data, .. })) => Some(data.len()),
529             Table::Static(StaticTable::GcRef(StaticGcRefTable { data, .. })) => Some(data.len()),
530             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { maximum, .. })) => *maximum,
531             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { maximum, .. })) => *maximum,
532             Table::Dynamic(DynamicTable::Cont(DynamicContTable { maximum, .. })) => *maximum,
533         }
534     }
535 
536     /// Fill `table[dst..dst + len]` with `val`.
537     ///
538     /// Returns a trap error on out-of-bounds accesses.
539     ///
540     /// # Panics
541     ///
542     /// Panics if `val` does not have a type that matches this table.
543     pub fn fill_func(
544         &mut self,
545         dst: u64,
546         val: Option<NonNull<VMFuncRef>>,
547         len: u64,
548     ) -> Result<(), Trap> {
549         let range = self.validate_fill(dst, len)?;
550         let (funcrefs, lazy_init) = self.funcrefs_mut();
551         funcrefs[range].fill(MaybeTaggedFuncRef::from(val, lazy_init));
552         Ok(())
553     }
554 
555     /// Same as [`Self::fill_func`], but for GC references.
556     ///
557     /// # Panics
558     ///
559     /// Also panics if `gc_store.is_none()` and it's needed.
560     pub fn fill_gc_ref(
561         &mut self,
562         mut gc_store: Option<&mut GcStore>,
563         dst: u64,
564         val: Option<&VMGcRef>,
565         len: u64,
566     ) -> Result<(), Trap> {
567         let range = self.validate_fill(dst, len)?;
568 
569         // Clone the init GC reference into each table slot.
570         for slot in &mut self.gc_refs_mut()[range] {
571             GcStore::write_gc_ref_optional_store(gc_store.as_deref_mut(), slot, val);
572         }
573 
574         Ok(())
575     }
576     /// Same as [`Self::fill_func`], but for continuations.
577     pub fn fill_cont(&mut self, dst: u64, val: Option<VMContObj>, len: u64) -> Result<(), Trap> {
578         let range = self.validate_fill(dst, len)?;
579         self.contrefs_mut()[range].fill(val);
580         Ok(())
581     }
582 
583     fn validate_fill(&mut self, dst: u64, len: u64) -> Result<Range<usize>, Trap> {
584         let start = usize::try_from(dst).map_err(|_| Trap::TableOutOfBounds)?;
585         let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
586         let end = start
587             .checked_add(len)
588             .ok_or_else(|| Trap::TableOutOfBounds)?;
589 
590         if end > self.size() {
591             return Err(Trap::TableOutOfBounds);
592         }
593         Ok(start..end)
594     }
595 
596     /// Grow table by the specified amount of elements.
597     ///
598     /// Returns the previous size of the table if growth is successful.
599     ///
600     /// Returns `None` if table can't be grown by the specified amount of
601     /// elements, or if the `init_value` is the wrong kind of table element.
602     ///
603     /// # Panics
604     ///
605     /// Panics if `init_value` does not have a type that matches this table.
606     ///
607     /// # Unsafety
608     ///
609     /// Resizing the table can reallocate its internal elements buffer. This
610     /// table's instance's `VMContext` has raw pointers to the elements buffer
611     /// that are used by Wasm, and they need to be fixed up before we call into
612     /// Wasm again. Failure to do so will result in use-after-free inside Wasm.
613     ///
614     /// Generally, prefer using `InstanceHandle::table_grow`, which encapsulates
615     /// this unsafety.
616     pub async unsafe fn grow_func(
617         &mut self,
618         limiter: Option<&mut StoreResourceLimiter<'_>>,
619         delta: u64,
620         init_value: Option<SendSyncPtr<VMFuncRef>>,
621     ) -> Result<Option<usize>, Error> {
622         self._grow(delta, limiter, |me, base, len| {
623             me.fill_func(base, init_value.map(|p| p.as_non_null()), len)
624         })
625         .await
626     }
627 
628     /// Same as [`Self::grow_func`], but for GC references.
629     pub async unsafe fn grow_gc_ref(
630         &mut self,
631         limiter: Option<&mut StoreResourceLimiter<'_>>,
632         gc_store: Option<&mut GcStore>,
633         delta: u64,
634         init_value: Option<&VMGcRef>,
635     ) -> Result<Option<usize>, Error> {
636         self._grow(delta, limiter, |me, base, len| {
637             me.fill_gc_ref(gc_store, base, init_value, len)
638         })
639         .await
640     }
641 
642     /// Same as [`Self::grow_func`], but for continuations.
643     pub async unsafe fn grow_cont(
644         &mut self,
645         limiter: Option<&mut StoreResourceLimiter<'_>>,
646         delta: u64,
647         init_value: Option<VMContObj>,
648     ) -> Result<Option<usize>, Error> {
649         self._grow(delta, limiter, |me, base, len| {
650             me.fill_cont(base, init_value, len)
651         })
652         .await
653     }
654 
655     async fn _grow(
656         &mut self,
657         delta: u64,
658         mut limiter: Option<&mut StoreResourceLimiter<'_>>,
659         fill: impl FnOnce(&mut Self, u64, u64) -> Result<(), Trap>,
660     ) -> Result<Option<usize>, Error> {
661         let old_size = self.size();
662 
663         // Don't try to resize the table if its size isn't changing, just return
664         // success.
665         if delta == 0 {
666             return Ok(Some(old_size));
667         }
668         let delta = usize::try_from(delta).map_err(|_| Trap::TableOutOfBounds)?;
669 
670         let new_size = match old_size.checked_add(delta) {
671             Some(s) => s,
672             None => {
673                 if let Some(limiter) = limiter {
674                     limiter
675                         .table_grow_failed(format_err!("overflow calculating new table size"))?;
676                 }
677                 return Ok(None);
678             }
679         };
680 
681         if let Some(limiter) = &mut limiter {
682             if !limiter
683                 .table_growing(old_size, new_size, self.maximum())
684                 .await?
685             {
686                 return Ok(None);
687             }
688         }
689 
690         // The WebAssembly spec requires failing a `table.grow` request if
691         // it exceeds the declared limits of the table. We may have set lower
692         // limits in the instance allocator as well.
693         if let Some(max) = self.maximum() {
694             if new_size > max {
695                 if let Some(limiter) = limiter {
696                     limiter.table_grow_failed(format_err!("Table maximum size exceeded"))?;
697                 }
698                 return Ok(None);
699             }
700         }
701 
702         // First resize the storage and then fill with the init value
703         match self {
704             Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
705                 unsafe {
706                     debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
707                 }
708                 *size = new_size;
709             }
710             Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
711                 unsafe {
712                     debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
713                 }
714                 *size = new_size;
715             }
716             Table::Static(StaticTable::Cont(StaticContTable { data, size })) => {
717                 unsafe {
718                     debug_assert!(data.as_ref()[*size..new_size].iter().all(|x| x.is_none()));
719                 }
720                 *size = new_size;
721             }
722 
723             // These calls to `resize` could move the base address of
724             // `elements`. If this table's limits declare it to be fixed-size,
725             // then during AOT compilation we may have promised Cranelift that
726             // the table base address won't change, so it is allowed to optimize
727             // loading the base address. However, in that case the above checks
728             // that delta is non-zero and the new size doesn't exceed the
729             // maximum mean we can't get here.
730             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
731                 elements.resize(new_size, None);
732             }
733             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
734                 elements.resize_with(new_size, || None);
735             }
736             Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => {
737                 elements.resize(new_size, None);
738             }
739         }
740 
741         fill(
742             self,
743             u64::try_from(old_size).unwrap(),
744             u64::try_from(delta).unwrap(),
745         )
746         .expect("table should not be out of bounds");
747 
748         Ok(Some(old_size))
749     }
750 
751     /// Get reference to the specified element.
752     ///
753     /// Returns `Ok(None)` if the element is null or uninitialized.
754     /// Returns `Err` if the index is out of bounds.
755     ///
756     /// Panics if this is a table of GC references and `gc_store` is `None`.
757     pub fn get_func(&self, index: u64) -> Result<Option<NonNull<VMFuncRef>>, Trap> {
758         match self.get_func_maybe_init(index)? {
759             Some(elem) => Ok(elem),
760             None => panic!("function index should have been initialized"),
761         }
762     }
763 
764     /// Same as [`Self::get_func`], except plumbs through the uninitialized
765     /// variant of functions too as `Ok(None)`. An initialized function element
766     /// is `Ok(Some(element))`
767     pub fn get_func_maybe_init(
768         &self,
769         index: u64,
770     ) -> Result<Option<Option<NonNull<VMFuncRef>>>, Trap> {
771         let index = usize::try_from(index).map_err(|_| Trap::TableOutOfBounds)?;
772         let (funcrefs, lazy_init) = self.funcrefs();
773         Ok(funcrefs
774             .get(index)
775             .ok_or(Trap::TableOutOfBounds)?
776             .into_funcref(lazy_init))
777     }
778 
779     /// Same as [`Self::get_func`], but for GC references.
780     pub fn get_gc_ref(&self, index: u64) -> Result<Option<&VMGcRef>, Trap> {
781         let index = usize::try_from(index).map_err(|_| Trap::TableOutOfBounds)?;
782         let gcref = self.gc_refs().get(index).ok_or(Trap::TableOutOfBounds)?;
783         Ok(gcref.as_ref())
784     }
785 
786     /// Same as [`Self::get_func`], but for continuations.
787     pub fn get_cont(&self, index: u64) -> Result<Option<VMContObj>, Trap> {
788         let index = usize::try_from(index).map_err(|_| Trap::TableOutOfBounds)?;
789         let cont = self.contrefs().get(index).ok_or(Trap::TableOutOfBounds)?;
790         Ok(*cont)
791     }
792 
793     /// Set reference to the specified element.
794     ///
795     /// # Errors
796     ///
797     /// Returns an error if `index` is out of bounds or if this table type does
798     /// not match the element type.
799     ///
800     /// # Panics
801     ///
802     /// Panics if `elem` is not of the right type for this table.
803     pub fn set_func(&mut self, index: u64, elem: Option<NonNull<VMFuncRef>>) -> Result<(), Trap> {
804         let trap = Trap::TableOutOfBounds;
805         let index: usize = index.try_into().map_err(|_| trap)?;
806         let (funcrefs, lazy_init) = self.funcrefs_mut();
807         *funcrefs.get_mut(index).ok_or(trap)? = MaybeTaggedFuncRef::from(elem, lazy_init);
808         Ok(())
809     }
810 
811     /// Same as [`Self::set_func`] except for GC references.
812     pub fn set_gc_ref(
813         &mut self,
814         store: Option<&mut GcStore>,
815         index: u64,
816         elem: Option<&VMGcRef>,
817     ) -> Result<(), Trap> {
818         let trap = Trap::TableOutOfBounds;
819         let index: usize = index.try_into().map_err(|_| trap)?;
820         GcStore::write_gc_ref_optional_store(
821             store,
822             self.gc_refs_mut().get_mut(index).ok_or(trap)?,
823             elem,
824         );
825         Ok(())
826     }
827 
828     /// Copy `len` elements from `self[src_index..][..len]` into
829     /// `dst_table[dst_index..][..len]`.
830     ///
831     /// # Errors
832     ///
833     /// Returns an error if the range is out of bounds of either the source or
834     /// destination tables.
835     pub fn copy_to(
836         &self,
837         dst: &mut Table,
838         gc_store: Option<&mut GcStore>,
839         dst_index: u64,
840         src_index: u64,
841         len: u64,
842     ) -> Result<(), Trap> {
843         let (src_range, dst_range) = Table::validate_copy(self, dst, dst_index, src_index, len)?;
844         Self::copy_elements(gc_store, dst, self, dst_range, src_range);
845         Ok(())
846     }
847 
848     /// Copy `len` elements from `self[src_index..][..len]` into
849     /// `self[dst_index..][..len]`.
850     ///
851     /// # Errors
852     ///
853     /// Returns an error if the range is out of bounds of either the source or
854     /// destination tables.
855     pub fn copy_within(
856         &mut self,
857         gc_store: Option<&mut GcStore>,
858         dst_index: u64,
859         src_index: u64,
860         len: u64,
861     ) -> Result<(), Trap> {
862         let (src_range, dst_range) = Table::validate_copy(self, self, dst_index, src_index, len)?;
863         self.copy_elements_within(gc_store, dst_range, src_range);
864         Ok(())
865     }
866 
867     /// Copy `len` elements from `src_table[src_index..]` into `dst_table[dst_index..]`.
868     ///
869     /// # Errors
870     ///
871     /// Returns an error if the range is out of bounds of either the source or
872     /// destination tables.
873     fn validate_copy(
874         src: &Table,
875         dst: &Table,
876         dst_index: u64,
877         src_index: u64,
878         len: u64,
879     ) -> Result<(Range<usize>, Range<usize>), Trap> {
880         // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
881 
882         let src_index = usize::try_from(src_index).map_err(|_| Trap::TableOutOfBounds)?;
883         let dst_index = usize::try_from(dst_index).map_err(|_| Trap::TableOutOfBounds)?;
884         let len = usize::try_from(len).map_err(|_| Trap::TableOutOfBounds)?;
885 
886         if src_index.checked_add(len).map_or(true, |n| n > src.size())
887             || dst_index.checked_add(len).map_or(true, |m| m > dst.size())
888         {
889             return Err(Trap::TableOutOfBounds);
890         }
891 
892         debug_assert!(
893             dst.element_type() == src.element_type(),
894             "table element type mismatch"
895         );
896 
897         let src_range = src_index..src_index + len;
898         let dst_range = dst_index..dst_index + len;
899 
900         Ok((src_range, dst_range))
901     }
902 
903     /// Return a `VMTableDefinition` for exposing the table to compiled wasm code.
904     pub fn vmtable(&mut self) -> VMTableDefinition {
905         match self {
906             Table::Static(StaticTable::Func(StaticFuncTable { data, size, .. })) => {
907                 VMTableDefinition {
908                     base: data.cast().into(),
909                     current_elements: *size,
910                 }
911             }
912             Table::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => {
913                 VMTableDefinition {
914                     base: data.cast().into(),
915                     current_elements: *size,
916                 }
917             }
918             Table::Static(StaticTable::Cont(StaticContTable { data, size })) => VMTableDefinition {
919                 base: data.cast().into(),
920                 current_elements: *size,
921             },
922             Table::Dynamic(DynamicTable::Func(DynamicFuncTable { elements, .. })) => {
923                 VMTableDefinition {
924                     base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
925                     current_elements: elements.len(),
926                 }
927             }
928             Table::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => {
929                 VMTableDefinition {
930                     base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
931                     current_elements: elements.len(),
932                 }
933             }
934             Table::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => {
935                 VMTableDefinition {
936                     base: NonNull::new(elements.as_mut_ptr()).unwrap().cast().into(),
937                     current_elements: elements.len(),
938                 }
939             }
940         }
941     }
942 
943     fn funcrefs(&self) -> (&[MaybeTaggedFuncRef], bool) {
944         assert_eq!(self.element_type(), TableElementType::Func);
945         match self {
946             Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
947                 elements,
948                 lazy_init,
949                 ..
950             })) => (
951                 unsafe { slice::from_raw_parts(elements.as_ptr().cast(), elements.len()) },
952                 *lazy_init,
953             ),
954             Self::Static(StaticTable::Func(StaticFuncTable {
955                 data,
956                 size,
957                 lazy_init,
958             })) => (
959                 unsafe { slice::from_raw_parts(data.as_ptr().cast(), *size) },
960                 *lazy_init,
961             ),
962             _ => unreachable!(),
963         }
964     }
965 
966     fn funcrefs_mut(&mut self) -> (&mut [MaybeTaggedFuncRef], bool) {
967         assert_eq!(self.element_type(), TableElementType::Func);
968         match self {
969             Self::Dynamic(DynamicTable::Func(DynamicFuncTable {
970                 elements,
971                 lazy_init,
972                 ..
973             })) => (
974                 unsafe { slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len()) },
975                 *lazy_init,
976             ),
977             Self::Static(StaticTable::Func(StaticFuncTable {
978                 data,
979                 size,
980                 lazy_init,
981             })) => (
982                 unsafe { slice::from_raw_parts_mut(data.as_ptr().cast(), *size) },
983                 *lazy_init,
984             ),
985             _ => unreachable!(),
986         }
987     }
988 
989     fn gc_refs(&self) -> &[Option<VMGcRef>] {
990         assert_eq!(self.element_type(), TableElementType::GcRef);
991         match self {
992             Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
993             Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
994                 &data.as_non_null().as_ref()[..*size]
995             },
996             _ => unreachable!(),
997         }
998     }
999 
1000     fn contrefs(&self) -> &[Option<VMContObj>] {
1001         assert_eq!(self.element_type(), TableElementType::Cont);
1002         match self {
1003             Self::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => unsafe {
1004                 slice::from_raw_parts(elements.as_ptr().cast(), elements.len())
1005             },
1006             Self::Static(StaticTable::Cont(StaticContTable { data, size })) => unsafe {
1007                 slice::from_raw_parts(data.as_ptr().cast(), *size)
1008             },
1009             _ => unreachable!(),
1010         }
1011     }
1012 
1013     fn contrefs_mut(&mut self) -> &mut [Option<VMContObj>] {
1014         assert_eq!(self.element_type(), TableElementType::Cont);
1015         match self {
1016             Self::Dynamic(DynamicTable::Cont(DynamicContTable { elements, .. })) => unsafe {
1017                 slice::from_raw_parts_mut(elements.as_mut_ptr().cast(), elements.len())
1018             },
1019             Self::Static(StaticTable::Cont(StaticContTable { data, size })) => unsafe {
1020                 slice::from_raw_parts_mut(data.as_ptr().cast(), *size)
1021             },
1022             _ => unreachable!(),
1023         }
1024     }
1025 
1026     /// Get this table's GC references as a slice.
1027     ///
1028     /// Panics if this is not a table of GC references.
1029     pub fn gc_refs_mut(&mut self) -> &mut [Option<VMGcRef>] {
1030         assert_eq!(self.element_type(), TableElementType::GcRef);
1031         match self {
1032             Self::Dynamic(DynamicTable::GcRef(DynamicGcRefTable { elements, .. })) => elements,
1033             Self::Static(StaticTable::GcRef(StaticGcRefTable { data, size })) => unsafe {
1034                 &mut data.as_non_null().as_mut()[..*size]
1035             },
1036             _ => unreachable!(),
1037         }
1038     }
1039 
1040     fn copy_elements(
1041         mut gc_store: Option<&mut GcStore>,
1042         dst_table: &mut Self,
1043         src_table: &Self,
1044         dst_range: Range<usize>,
1045         src_range: Range<usize>,
1046     ) {
1047         // This can only be used when copying between different tables
1048         debug_assert!(!ptr::eq(dst_table, src_table));
1049 
1050         let ty = dst_table.element_type();
1051 
1052         match ty {
1053             TableElementType::Func => {
1054                 // `funcref` are `Copy`, so just do a mempcy
1055                 let (dst_funcrefs, _lazy_init) = dst_table.funcrefs_mut();
1056                 let (src_funcrefs, _lazy_init) = src_table.funcrefs();
1057                 dst_funcrefs[dst_range].copy_from_slice(&src_funcrefs[src_range]);
1058             }
1059             TableElementType::GcRef => {
1060                 assert_eq!(
1061                     dst_range.end - dst_range.start,
1062                     src_range.end - src_range.start
1063                 );
1064                 assert!(dst_range.end <= dst_table.gc_refs().len());
1065                 assert!(src_range.end <= src_table.gc_refs().len());
1066                 for (dst, src) in dst_range.zip(src_range) {
1067                     GcStore::write_gc_ref_optional_store(
1068                         gc_store.as_deref_mut(),
1069                         &mut dst_table.gc_refs_mut()[dst],
1070                         src_table.gc_refs()[src].as_ref(),
1071                     );
1072                 }
1073             }
1074             TableElementType::Cont => {
1075                 // `contref` are `Copy`, so just do a mempcy
1076                 dst_table.contrefs_mut()[dst_range]
1077                     .copy_from_slice(&src_table.contrefs()[src_range]);
1078             }
1079         }
1080     }
1081 
1082     fn copy_elements_within(
1083         &mut self,
1084         mut gc_store: Option<&mut GcStore>,
1085         dst_range: Range<usize>,
1086         src_range: Range<usize>,
1087     ) {
1088         assert_eq!(
1089             dst_range.end - dst_range.start,
1090             src_range.end - src_range.start
1091         );
1092 
1093         // This is a no-op.
1094         if src_range.start == dst_range.start {
1095             return;
1096         }
1097 
1098         let ty = self.element_type();
1099         match ty {
1100             TableElementType::Func => {
1101                 // `funcref` are `Copy`, so just do a memmove
1102                 let (funcrefs, _lazy_init) = self.funcrefs_mut();
1103                 funcrefs.copy_within(src_range, dst_range.start);
1104             }
1105             TableElementType::GcRef => {
1106                 // We need to clone each `externref` while handling overlapping
1107                 // ranges
1108                 let elements = self.gc_refs_mut();
1109                 if dst_range.start < src_range.start {
1110                     for (d, s) in dst_range.zip(src_range) {
1111                         let (ds, ss) = elements.split_at_mut(s);
1112                         let dst = &mut ds[d];
1113                         let src = ss[0].as_ref();
1114                         GcStore::write_gc_ref_optional_store(gc_store.as_deref_mut(), dst, src);
1115                     }
1116                 } else {
1117                     for (s, d) in src_range.rev().zip(dst_range.rev()) {
1118                         let (ss, ds) = elements.split_at_mut(d);
1119                         let dst = &mut ds[0];
1120                         let src = ss[s].as_ref();
1121                         GcStore::write_gc_ref_optional_store(gc_store.as_deref_mut(), dst, src);
1122                     }
1123                 }
1124             }
1125             TableElementType::Cont => {
1126                 // `contref` are `Copy`, so just do a memmove
1127                 self.contrefs_mut().copy_within(src_range, dst_range.start);
1128             }
1129         }
1130     }
1131 }
1132 
1133 // The default table representation is an empty funcref table that cannot grow.
1134 impl Default for Table {
1135     fn default() -> Self {
1136         Self::from(StaticFuncTable {
1137             data: SendSyncPtr::new(NonNull::from(&mut [])),
1138             size: 0,
1139             lazy_init: false,
1140         })
1141     }
1142 }
1143