reference types: Implement the table.size and table.grow instructions (#1894)

Part of #929
This commit is contained in:
Nick Fitzgerald
2020-06-18 06:57:18 -07:00
committed by GitHub
parent 06a69d18fa
commit bbd99c5bfa
8 changed files with 441 additions and 383 deletions

View File

@@ -448,21 +448,41 @@ impl Instance {
foreign_instance.memory_size(foreign_index)
}
/// Grow table by the specified amount of elements.
/// Grow table by the specified amount of elements, filling them with
/// `init_value`.
///
/// Returns `None` if table can't be grown by the specified amount
/// of elements.
pub(crate) fn table_grow(&self, table_index: DefinedTableIndex, delta: u32) -> Option<u32> {
let result = self
.tables
.get(table_index)
.unwrap_or_else(|| panic!("no table for index {}", table_index.index()))
.grow(delta);
/// Returns `None` if table can't be grown by the specified amount of
/// elements, or if `init_value` is the wrong type of table element.
pub(crate) fn table_grow(
&self,
table_index: TableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
let (defined_table_index, instance) =
self.get_defined_table_index_and_instance(table_index);
instance.defined_table_grow(defined_table_index, delta, init_value)
}
// Keep current the VMContext pointers used by compiled wasm code.
self.set_table(table_index, self.tables[table_index].vmtable());
fn defined_table_grow(
&self,
table_index: DefinedTableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
unsafe {
let orig_size = self
.tables
.get(table_index)
.unwrap_or_else(|| panic!("no table for index {}", table_index.index()))
.grow(delta, init_value)?;
result
// Keep the `VMContext` pointers used by compiled Wasm code up to
// date.
self.set_table(table_index, self.tables[table_index].vmtable());
Some(orig_size)
}
}
// Get table element by index.
@@ -757,6 +777,21 @@ impl Instance {
let foreign_index = foreign_instance.table_index(foreign_table);
&foreign_instance.tables[foreign_index]
}
pub(crate) fn get_defined_table_index_and_instance(
&self,
index: TableIndex,
) -> (DefinedTableIndex, &Instance) {
if let Some(defined_table_index) = self.module.local.defined_table_index(index) {
(defined_table_index, self)
} else {
let import = self.imported_table(index);
let foreign_instance = unsafe { (&mut *(import).vmctx).instance() };
let foreign_table_def = unsafe { &mut *(import).from };
let foreign_table_index = foreign_instance.table_index(foreign_table_def);
(foreign_table_index, foreign_instance)
}
}
}
/// A handle holding an `Instance` of a WebAssembly module.
@@ -1000,12 +1035,37 @@ impl InstanceHandle {
self.instance().table_index(table)
}
/// Grow table in this instance by the specified amount of pages.
/// Grow table in this instance by the specified amount of elements.
///
/// Returns `None` if memory can't be grown by the specified amount
/// of pages.
pub fn table_grow(&self, table_index: DefinedTableIndex, delta: u32) -> Option<u32> {
self.instance().table_grow(table_index, delta)
/// When the table is successfully grown, returns the original size of the
/// table.
///
/// Returns `None` if memory can't be grown by the specified amount of pages
/// or if the `init_value` is the incorrect table element type.
pub fn table_grow(
&self,
table_index: TableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
self.instance().table_grow(table_index, delta, init_value)
}
/// Grow table in this instance by the specified amount of elements.
///
/// When the table is successfully grown, returns the original size of the
/// table.
///
/// Returns `None` if memory can't be grown by the specified amount of pages
/// or if the `init_value` is the incorrect table element type.
pub fn defined_table_grow(
&self,
table_index: DefinedTableIndex,
delta: u32,
init_value: TableElement,
) -> Option<u32> {
self.instance()
.defined_table_grow(table_index, delta, init_value)
}
/// Get table element reference.

View File

@@ -31,8 +31,33 @@
//! }
//! }
//! ```
//!
//! * When receiving a raw `*mut u8` that is actually a `VMExternRef` reference,
//! convert it into a proper `VMExternRef` with `VMExternRef::clone_from_raw`
//! as soon as apossible. Any GC before raw pointer is converted into a
//! reference can potentially collect the referenced object, which could lead
//! to use after free. Avoid this by eagerly converting into a proper
//! `VMExternRef`!
//!
//! ```ignore
//! pub unsafe extern "C" my_lib_takes_ref(raw_extern_ref: *mut u8) {
//! // Before `clone_from_raw`, `raw_extern_ref` is potentially unrooted,
//! // and doing GC here could lead to use after free!
//!
//! let my_extern_ref = if raw_extern_ref.is_null() {
//! None
//! } else {
//! Some(VMExternRef::clone_from_raw(raw_extern_ref))
//! };
//!
//! // Now that we did `clone_from_raw`, it is safe to do a GC (or do
//! // anything else that might transitively GC, like call back into
//! // Wasm!)
//! }
//! ```
use crate::table::Table;
use crate::externref::VMExternRef;
use crate::table::{Table, TableElement};
use crate::traphandlers::raise_lib_trap;
use crate::vmcontext::VMContext;
use wasmtime_environ::wasm::{DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableIndex};
@@ -201,6 +226,26 @@ pub unsafe extern "C" fn wasmtime_imported_memory32_size(
instance.imported_memory_size(memory_index)
}
/// Implementation of `table.grow` for `externref`s.
pub unsafe extern "C" fn wasmtime_table_grow_extern_ref(
vmctx: *mut VMContext,
table_index: u32,
delta: u32,
init_value: *mut u8,
) -> u32 {
let init_value = if init_value.is_null() {
None
} else {
Some(VMExternRef::clone_from_raw(init_value))
};
let instance = (&mut *vmctx).instance();
let table_index = TableIndex::from_u32(table_index);
instance
.table_grow(table_index, delta, TableElement::ExternRef(init_value))
.unwrap_or(-1_i32 as u32)
}
/// Implementation of `table.copy`.
pub unsafe extern "C" fn wasmtime_table_copy(
vmctx: *mut VMContext,

View File

@@ -67,29 +67,42 @@ impl Table {
/// Grow table by the specified amount of elements.
///
/// Returns `None` if table can't be grown by the specified amount
/// of elements. Returns the previous size of the table if growth is
/// successful.
pub fn grow(&self, delta: u32) -> Option<u32> {
/// Returns the previous size of the table if growth is successful.
///
/// Returns `None` if table can't be grown by the specified amount of
/// elements, or if the `init_value` is the wrong kind of table element.
///
/// # Unsafety
///
/// Resizing the table can reallocate its internal elements buffer. This
/// table's instance's `VMContext` has raw pointers to the elements buffer
/// that are used by Wasm, and they need to be fixed up before we call into
/// Wasm again. Failure to do so will result in use-after-free inside Wasm.
///
/// Generally, prefer using `InstanceHandle::table_grow`, which encapsulates
/// this unsafety.
pub unsafe fn grow(&self, delta: u32, init_value: TableElement) -> Option<u32> {
let size = self.size();
let new_len = match size.checked_add(delta) {
Some(len) => {
if let Some(max) = self.maximum {
if len > max {
return None;
}
}
len
}
None => {
let new_len = size.checked_add(delta)?;
if let Some(max) = self.maximum {
if new_len > max {
return None;
}
};
let new_len = usize::try_from(new_len).unwrap();
match &mut *self.elements.borrow_mut() {
TableElements::FuncRefs(x) => x.resize(new_len, VMCallerCheckedAnyfunc::default()),
TableElements::ExternRefs(x) => x.resize(new_len, None),
}
let new_len = usize::try_from(new_len).unwrap();
match &mut *self.elements.borrow_mut() {
TableElements::FuncRefs(x) => {
let init_value = init_value.try_into().ok()?;
x.resize(new_len, init_value)
}
TableElements::ExternRefs(x) => {
let init_value = init_value.try_into().ok()?;
x.resize(new_len, init_value)
}
}
Some(size)
}
@@ -207,3 +220,21 @@ impl TryFrom<TableElement> for Option<VMExternRef> {
}
}
}
impl From<VMCallerCheckedAnyfunc> for TableElement {
fn from(f: VMCallerCheckedAnyfunc) -> TableElement {
TableElement::FuncRef(f)
}
}
impl From<Option<VMExternRef>> for TableElement {
fn from(x: Option<VMExternRef>) -> TableElement {
TableElement::ExternRef(x)
}
}
impl From<VMExternRef> for TableElement {
fn from(x: VMExternRef) -> TableElement {
TableElement::ExternRef(Some(x))
}
}

View File

@@ -540,39 +540,34 @@ impl VMBuiltinFunctionsArray {
let mut ptrs = [0; Self::len()];
ptrs[BuiltinFunctionIndex::get_memory32_grow_index().index() as usize] =
ptrs[BuiltinFunctionIndex::memory32_grow().index() as usize] =
wasmtime_memory32_grow as usize;
ptrs[BuiltinFunctionIndex::get_imported_memory32_grow_index().index() as usize] =
ptrs[BuiltinFunctionIndex::imported_memory32_grow().index() as usize] =
wasmtime_imported_memory32_grow as usize;
ptrs[BuiltinFunctionIndex::get_memory32_size_index().index() as usize] =
ptrs[BuiltinFunctionIndex::memory32_size().index() as usize] =
wasmtime_memory32_size as usize;
ptrs[BuiltinFunctionIndex::get_imported_memory32_size_index().index() as usize] =
ptrs[BuiltinFunctionIndex::imported_memory32_size().index() as usize] =
wasmtime_imported_memory32_size as usize;
ptrs[BuiltinFunctionIndex::get_table_copy_index().index() as usize] =
wasmtime_table_copy as usize;
ptrs[BuiltinFunctionIndex::get_table_init_index().index() as usize] =
wasmtime_table_init as usize;
ptrs[BuiltinFunctionIndex::get_elem_drop_index().index() as usize] =
wasmtime_elem_drop as usize;
ptrs[BuiltinFunctionIndex::get_defined_memory_copy_index().index() as usize] =
ptrs[BuiltinFunctionIndex::table_copy().index() as usize] = wasmtime_table_copy as usize;
ptrs[BuiltinFunctionIndex::table_grow_extern_ref().index() as usize] =
wasmtime_table_grow_extern_ref as usize;
ptrs[BuiltinFunctionIndex::table_init().index() as usize] = wasmtime_table_init as usize;
ptrs[BuiltinFunctionIndex::elem_drop().index() as usize] = wasmtime_elem_drop as usize;
ptrs[BuiltinFunctionIndex::defined_memory_copy().index() as usize] =
wasmtime_defined_memory_copy as usize;
ptrs[BuiltinFunctionIndex::get_imported_memory_copy_index().index() as usize] =
ptrs[BuiltinFunctionIndex::imported_memory_copy().index() as usize] =
wasmtime_imported_memory_copy as usize;
ptrs[BuiltinFunctionIndex::get_memory_fill_index().index() as usize] =
wasmtime_memory_fill as usize;
ptrs[BuiltinFunctionIndex::get_imported_memory_fill_index().index() as usize] =
ptrs[BuiltinFunctionIndex::memory_fill().index() as usize] = wasmtime_memory_fill as usize;
ptrs[BuiltinFunctionIndex::imported_memory_fill().index() as usize] =
wasmtime_imported_memory_fill as usize;
ptrs[BuiltinFunctionIndex::get_memory_init_index().index() as usize] =
wasmtime_memory_init as usize;
ptrs[BuiltinFunctionIndex::get_data_drop_index().index() as usize] =
wasmtime_data_drop as usize;
debug_assert!(ptrs.iter().cloned().all(|p| p != 0));
ptrs[BuiltinFunctionIndex::memory_init().index() as usize] = wasmtime_memory_init as usize;
ptrs[BuiltinFunctionIndex::data_drop().index() as usize] = wasmtime_data_drop as usize;
if cfg!(debug_assertions) {
for i in 0..ptrs.len() {
debug_assert!(ptrs[i] != 0, "index {} is not initialized", i);
}
}
Self { ptrs }
}
}