Implement call_indirect signature checking.
The call_indirect.wast spec test now passes.
This commit is contained in:
@@ -5,9 +5,10 @@ use cranelift_entity::EntityRef;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||
use memory::LinearMemory;
|
||||
use sig_registry::SignatureRegistry;
|
||||
use std::string::String;
|
||||
use table::{AnyFunc, Table};
|
||||
use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
|
||||
use table::Table;
|
||||
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
||||
use wasmtime_environ::{Compilation, DataInitializer, Module};
|
||||
|
||||
/// An Instance of a WebAssemby module.
|
||||
@@ -19,6 +20,10 @@ pub struct Instance {
|
||||
/// WebAssembly table data.
|
||||
tables: PrimaryMap<TableIndex, Table>,
|
||||
|
||||
/// Function Signature IDs.
|
||||
/// FIXME: This should be shared across instances rather than per-Instance.
|
||||
sig_registry: SignatureRegistry,
|
||||
|
||||
/// Memory base address vector pointed to by vmctx.
|
||||
vmctx_memories: PrimaryMap<MemoryIndex, VMMemory>,
|
||||
|
||||
@@ -39,8 +44,9 @@ impl Instance {
|
||||
compilation: &Compilation,
|
||||
data_initializers: &[DataInitializer],
|
||||
) -> Result<Self, String> {
|
||||
let mut sig_registry = SignatureRegistry::new();
|
||||
let mut memories = instantiate_memories(module, data_initializers)?;
|
||||
let mut tables = instantiate_tables(module, compilation);
|
||||
let mut tables = instantiate_tables(module, compilation, &mut sig_registry);
|
||||
|
||||
let mut vmctx_memories = memories
|
||||
.values_mut()
|
||||
@@ -57,14 +63,21 @@ impl Instance {
|
||||
let vmctx_memories_ptr = vmctx_memories.values_mut().into_slice().as_mut_ptr();
|
||||
let vmctx_globals_ptr = vmctx_globals.values_mut().into_slice().as_mut_ptr();
|
||||
let vmctx_tables_ptr = vmctx_tables.values_mut().into_slice().as_mut_ptr();
|
||||
let signature_ids_ptr = sig_registry.vmsignature_ids();
|
||||
|
||||
Ok(Self {
|
||||
memories,
|
||||
tables,
|
||||
sig_registry,
|
||||
vmctx_memories,
|
||||
vmctx_globals,
|
||||
vmctx_tables,
|
||||
vmctx: VMContext::new(vmctx_memories_ptr, vmctx_globals_ptr, vmctx_tables_ptr),
|
||||
vmctx: VMContext::new(
|
||||
vmctx_memories_ptr,
|
||||
vmctx_globals_ptr,
|
||||
vmctx_tables_ptr,
|
||||
signature_ids_ptr,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -139,7 +152,11 @@ fn instantiate_memories(
|
||||
}
|
||||
|
||||
/// Allocate memory for just the tables of the current module.
|
||||
fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap<TableIndex, Table> {
|
||||
fn instantiate_tables(
|
||||
module: &Module,
|
||||
compilation: &Compilation,
|
||||
sig_registry: &mut SignatureRegistry,
|
||||
) -> PrimaryMap<TableIndex, Table> {
|
||||
let mut tables = PrimaryMap::with_capacity(module.table_plans.len());
|
||||
for table in module.table_plans.values() {
|
||||
tables.push(Table::new(table));
|
||||
@@ -150,14 +167,14 @@ fn instantiate_tables(module: &Module, compilation: &Compilation) -> PrimaryMap<
|
||||
let slice = &mut tables[init.table_index].as_mut();
|
||||
let subslice = &mut slice[init.offset..init.offset + init.elements.len()];
|
||||
for (i, func_idx) in init.elements.iter().enumerate() {
|
||||
// FIXME: Implement cross-module signature checking.
|
||||
let type_id = module.functions[*func_idx];
|
||||
let callee_sig = module.functions[*func_idx];
|
||||
let code_buf = &compilation.functions[module.defined_func_index(*func_idx).expect(
|
||||
"table element initializer with imported function not supported yet",
|
||||
)];
|
||||
subslice[i] = AnyFunc {
|
||||
let type_id = sig_registry.register(callee_sig, &module.signatures[callee_sig]);
|
||||
subslice[i] = VMCallerCheckedAnyfunc {
|
||||
func_ptr: code_buf.as_ptr(),
|
||||
type_id: type_id.index(),
|
||||
type_id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ extern crate lazy_static;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate memoffset;
|
||||
extern crate cast;
|
||||
|
||||
mod code;
|
||||
mod execute;
|
||||
@@ -50,6 +51,7 @@ mod invoke;
|
||||
mod libcalls;
|
||||
mod memory;
|
||||
mod mmap;
|
||||
mod sig_registry;
|
||||
mod signalhandlers;
|
||||
mod table;
|
||||
mod traphandlers;
|
||||
|
||||
43
lib/execute/src/sig_registry.rs
Normal file
43
lib/execute/src/sig_registry.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Implement a registry of function signatures, for fast indirect call
|
||||
//! signature checking.
|
||||
|
||||
use cast;
|
||||
use cranelift_codegen::ir;
|
||||
use cranelift_entity::SecondaryMap;
|
||||
use cranelift_wasm::SignatureIndex;
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use vmcontext::VMSignatureId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SignatureRegistry {
|
||||
signature_hash: HashMap<ir::Signature, VMSignatureId>,
|
||||
signature_ids: SecondaryMap<SignatureIndex, VMSignatureId>,
|
||||
}
|
||||
|
||||
impl SignatureRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
signature_hash: HashMap::new(),
|
||||
signature_ids: SecondaryMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vmsignature_ids(&mut self) -> *mut VMSignatureId {
|
||||
self.signature_ids.values_mut().into_slice().as_mut_ptr()
|
||||
}
|
||||
|
||||
/// Register the given signature.
|
||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) -> VMSignatureId {
|
||||
let len = self.signature_hash.len();
|
||||
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
let sig_id = cast::u32(len).unwrap();
|
||||
entry.insert(sig_id);
|
||||
sig_id
|
||||
}
|
||||
};
|
||||
self.signature_ids[sig_index] = sig_id;
|
||||
sig_id
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
//! Implement a registry of function signatures, for fast indirect call
|
||||
//! signature checking.
|
||||
@@ -3,30 +3,13 @@
|
||||
//! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories.
|
||||
|
||||
use cranelift_wasm::TableElementType;
|
||||
use std::ptr;
|
||||
use vmcontext::VMTable;
|
||||
use vmcontext::{VMCallerCheckedAnyfunc, VMTable};
|
||||
use wasmtime_environ::{TablePlan, TableStyle};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct AnyFunc {
|
||||
pub func_ptr: *const u8,
|
||||
pub type_id: usize,
|
||||
}
|
||||
|
||||
impl Default for AnyFunc {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
func_ptr: ptr::null(),
|
||||
type_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A table instance.
|
||||
#[derive(Debug)]
|
||||
pub struct Table {
|
||||
vec: Vec<AnyFunc>,
|
||||
vec: Vec<VMCallerCheckedAnyfunc>,
|
||||
maximum: Option<u32>,
|
||||
}
|
||||
|
||||
@@ -43,7 +26,10 @@ impl Table {
|
||||
match plan.style {
|
||||
TableStyle::CallerChecksSignature => {
|
||||
let mut vec = Vec::new();
|
||||
vec.resize(plan.table.minimum as usize, AnyFunc::default());
|
||||
vec.resize(
|
||||
plan.table.minimum as usize,
|
||||
VMCallerCheckedAnyfunc::default(),
|
||||
);
|
||||
|
||||
Self {
|
||||
vec,
|
||||
@@ -58,14 +44,14 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[AnyFunc]> for Table {
|
||||
fn as_ref(&self) -> &[AnyFunc] {
|
||||
impl AsRef<[VMCallerCheckedAnyfunc]> for Table {
|
||||
fn as_ref(&self) -> &[VMCallerCheckedAnyfunc] {
|
||||
self.vec.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[AnyFunc]> for Table {
|
||||
fn as_mut(&mut self) -> &mut [AnyFunc] {
|
||||
impl AsMut<[VMCallerCheckedAnyfunc]> for Table {
|
||||
fn as_mut(&mut self) -> &mut [VMCallerCheckedAnyfunc] {
|
||||
self.vec.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use cranelift_entity::EntityRef;
|
||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||
use instance::Instance;
|
||||
use std::mem::size_of;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
/// The main fields a JIT needs to access to utilize a WebAssembly linear,
|
||||
@@ -171,6 +172,69 @@ impl VMTable {
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of the `type_id` field in `VMCallerCheckedAnyfunc`.
|
||||
pub type VMSignatureId = u32;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmsignature_id {
|
||||
use super::VMSignatureId;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmcaller_checked_anyfunc_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMSignatureId>(),
|
||||
usize::from(offsets.size_of_vmsignature_id())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The VM caller-checked "anyfunc" record, for caller-side signature checking.
|
||||
/// It consists of the actual function pointer and a signature id to be checked
|
||||
/// by the caller.
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct VMCallerCheckedAnyfunc {
|
||||
pub func_ptr: *const u8,
|
||||
pub type_id: VMSignatureId,
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_vmcaller_checked_anyfunc {
|
||||
use super::VMCallerCheckedAnyfunc;
|
||||
use std::mem::size_of;
|
||||
use wasmtime_environ::VMOffsets;
|
||||
|
||||
#[test]
|
||||
fn check_vmcaller_checked_anyfunc_offsets() {
|
||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||
assert_eq!(
|
||||
size_of::<VMCallerCheckedAnyfunc>(),
|
||||
usize::from(offsets.size_of_vmcaller_checked_anyfunc())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMCallerCheckedAnyfunc, func_ptr),
|
||||
usize::from(offsets.vmcaller_checked_anyfunc_func_ptr())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMCallerCheckedAnyfunc, type_id),
|
||||
usize::from(offsets.vmcaller_checked_anyfunc_type_id())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VMCallerCheckedAnyfunc {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
func_ptr: ptr::null_mut(),
|
||||
type_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
|
||||
/// This has pointers to the globals, memories, tables, and other runtime
|
||||
/// state associated with the current instance.
|
||||
@@ -185,6 +249,8 @@ pub struct VMContext {
|
||||
/// A pointer to an array of `VMTable` instances, indexed by
|
||||
/// WebAssembly table index.
|
||||
tables: *mut VMTable,
|
||||
/// Signature identifiers for signature-checking indirect calls.
|
||||
signature_ids: *mut u32,
|
||||
// If more elements are added here, remember to add offset_of tests below!
|
||||
}
|
||||
|
||||
@@ -210,16 +276,26 @@ mod test {
|
||||
offset_of!(VMContext, tables),
|
||||
usize::from(offsets.vmctx_tables())
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(VMContext, signature_ids),
|
||||
usize::from(offsets.vmctx_signature_ids())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl VMContext {
|
||||
/// Create a new `VMContext` instance.
|
||||
pub fn new(memories: *mut VMMemory, globals: *mut VMGlobal, tables: *mut VMTable) -> Self {
|
||||
pub fn new(
|
||||
memories: *mut VMMemory,
|
||||
globals: *mut VMGlobal,
|
||||
tables: *mut VMTable,
|
||||
signature_ids: *mut u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
memories,
|
||||
globals,
|
||||
tables,
|
||||
signature_ids,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user