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