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