Implement call_indirect signature checking.

The call_indirect.wast spec test now passes.
This commit is contained in:
Dan Gohman
2018-12-05 08:46:17 -05:00
parent 57635eb62b
commit 96941a59af
11 changed files with 263 additions and 69 deletions

View File

@@ -1,3 +1,4 @@
use cast;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir;
use cranelift_codegen::ir::condcodes::*;
@@ -109,6 +110,9 @@ pub struct FuncEnvironment<'module_environment> {
/// The Cranelift global holding the base address of the globals vector.
globals_base: Option<ir::GlobalValue>,
/// The Cranelift global holding the base address of the signature IDs vector.
signature_ids_base: Option<ir::GlobalValue>,
/// The external function declaration for implementing wasm's `memory.size`.
memory_size_extfunc: Option<FuncRef>,
@@ -131,9 +135,10 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
memories_base: None,
tables_base: None,
globals_base: None,
signature_ids_base: None,
memory_size_extfunc: None,
memory_grow_extfunc: None,
offsets: VMOffsets::new(isa.frontend_config().pointer_bytes()),
offsets: VMOffsets::new(isa.pointer_bytes()),
}
}
@@ -145,10 +150,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
real_call_args
}
fn pointer_bytes(&self) -> u8 {
self.isa.pointer_bytes()
}
fn vmctx(&mut self, func: &mut Function) -> ir::GlobalValue {
self.vmctx.unwrap_or_else(|| {
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
@@ -309,12 +310,14 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
}
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable {
let pointer_type = self.pointer_type();
let vmctx = self.vmctx(func);
let globals_base = self.globals_base.unwrap_or_else(|| {
let new_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(i32::from(self.offsets.vmctx_globals())),
global_type: self.pointer_type(),
global_type: pointer_type,
readonly: true,
});
self.globals_base = Some(new_base);
@@ -323,7 +326,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
let gv = func.create_global_value(ir::GlobalValueData::IAddImm {
base: globals_base,
offset: Imm64::new(i64::from(self.offsets.index_vmglobal(index.as_u32()))),
global_type: self.pointer_type(),
global_type: pointer_type,
});
GlobalVariable::Memory {
gv,
@@ -332,12 +335,14 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
}
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
let pointer_type = self.pointer_type();
let vmctx = self.vmctx(func);
let memories_base = self.memories_base.unwrap_or_else(|| {
let new_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(i32::from(self.offsets.vmctx_memories())),
global_type: self.pointer_type(),
global_type: pointer_type,
readonly: true,
});
self.memories_base = Some(new_base);
@@ -383,7 +388,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
base: memories_base,
offset: Offset32::new(self.offsets.index_vmmemory_base(index.as_u32())),
global_type: self.pointer_type(),
global_type: pointer_type,
readonly: readonly_base,
});
func.create_heap(ir::HeapData {
@@ -396,12 +401,14 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
}
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table {
let pointer_type = self.pointer_type();
let vmctx = self.vmctx(func);
let tables_base = self.tables_base.unwrap_or_else(|| {
let new_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(i32::from(self.offsets.vmctx_tables())),
global_type: self.pointer_type(),
global_type: pointer_type,
readonly: true,
});
self.tables_base = Some(new_base);
@@ -410,7 +417,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
base: tables_base,
offset: Offset32::new(self.offsets.index_vmtable_base(index.as_u32())),
global_type: self.pointer_type(),
global_type: pointer_type,
readonly: false,
});
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
@@ -421,7 +428,9 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
});
let element_size = match self.module.table_plans[index].style {
TableStyle::CallerChecksSignature => 2 * u64::from(self.pointer_bytes()),
TableStyle::CallerChecksSignature => {
u64::from(self.offsets.size_of_vmcaller_checked_anyfunc())
}
};
func.create_table(ir::TableData {
@@ -461,40 +470,70 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
callee: ir::Value,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
// FIXME: Cranelift's call_indirect doesn't implement signature checking,
// so we need to implement it ourselves.
debug_assert_eq!(
table_index.index(),
0,
"non-default tables not supported yet"
);
let pointer_type = self.pointer_type();
let table_entry_addr = pos.ins().table_addr(self.pointer_type(), table, callee, 0);
let table_entry_addr = pos.ins().table_addr(pointer_type, table, callee, 0);
// Dereference table_entry_addr to get the function address.
let mut mem_flags = ir::MemFlags::new();
mem_flags.set_notrap();
mem_flags.set_aligned();
let func_addr = pos
.ins()
.load(self.pointer_type(), mem_flags, table_entry_addr, 0);
let func_addr = pos.ins().load(
pointer_type,
mem_flags,
table_entry_addr,
i32::from(self.offsets.vmcaller_checked_anyfunc_func_ptr()),
);
// If necessary, check the signature.
match self.module.table_plans[table_index].style {
TableStyle::CallerChecksSignature => {
// Dereference table_type_addr to get the function signature id.
let sig_id_size = self.offsets.size_of_vmsignature_id();
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
let vmctx = self.vmctx(pos.func);
let signature_ids_base = self.globals_base.unwrap_or_else(|| {
let new_base = pos.func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(i32::from(self.offsets.vmctx_signature_ids())),
global_type: pointer_type,
readonly: true,
});
self.signature_ids_base = Some(new_base);
new_base
});
let sig_ids = pos.ins().global_value(pointer_type, signature_ids_base);
// Load the caller ID.
// TODO: Factor this out into a MemFlags constructor, as it's used a lot.
let mut mem_flags = ir::MemFlags::new();
mem_flags.set_notrap();
mem_flags.set_aligned();
let callee_sig = pos.ins().load(
self.pointer_type(),
let caller_sig_id = pos.ins().load(
sig_id_type,
mem_flags,
sig_ids,
cast::i32(
sig_index
.as_u32()
.checked_mul(u32::from(sig_id_size))
.unwrap(),
).unwrap(),
);
// Load the callee ID.
let mut mem_flags = ir::MemFlags::new();
mem_flags.set_notrap();
mem_flags.set_aligned();
let callee_sig_id = pos.ins().load(
sig_id_type,
mem_flags,
table_entry_addr,
i32::from(self.pointer_bytes()),
i32::from(self.offsets.vmcaller_checked_anyfunc_type_id()),
);
let cmp =
pos.ins()
.icmp_imm(IntCC::Equal, callee_sig, i64::from(sig_index.as_u32()));
// Check that they match.
let cmp = pos.ins().icmp(IntCC::Equal, callee_sig_id, caller_sig_id);
pos.ins().trapz(cmp, ir::TrapCode::BadSignature);
}
}

View File

@@ -36,6 +36,7 @@ extern crate cranelift_wasm;
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc;
extern crate cast;
mod compilation;
mod environ;

View File

@@ -55,6 +55,32 @@ impl VMOffsets {
}
}
/// Offsets for `wasmtime_execute::VMSignatureId`.
impl VMOffsets {
/// Return the size of `VMSignatureId`.
pub fn size_of_vmsignature_id(&self) -> u8 {
4
}
}
/// Offsets for `wasmtime_execute::VMCallerCheckedAnyfunc`.
impl VMOffsets {
/// The offset of the `func_ptr` field.
pub fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
0 * self.pointer_size
}
/// The offset of the `type_id` field.
pub fn vmcaller_checked_anyfunc_type_id(&self) -> u8 {
1 * self.pointer_size
}
/// Return the size of `VMTable`.
pub fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
2 * self.pointer_size
}
}
/// Offsets for `wasmtime_execute::VMContext`.
impl VMOffsets {
/// The offset of the `memories` field.
@@ -72,10 +98,15 @@ impl VMOffsets {
2 * self.pointer_size
}
/// The offset of the `signature_ids` field.
pub fn vmctx_signature_ids(&self) -> u8 {
3 * self.pointer_size
}
/// Return the size of `VMContext`.
#[allow(dead_code)]
pub fn size_of_vmctx(&self) -> u8 {
3 * self.pointer_size
4 * self.pointer_size
}
/// Return the offset from the `memories` pointer to `VMMemory` index `index`.