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