Allow wasm embedders to reject wasm modules with unsupported features. (#345)
Define `WasmError` (and an accompanying `WasmResult`) to represent errors translating WebAssembly functions. Make `translate_call` and related functions return `WasmResult`s so that embedders have the flexibility to reject features they don't support. Move `InvalidInput` out of `CtonError` and into `WasmError`, where it's now named `InvalidWebAssembly`, as it's a WebAssembly-specific error condition. Also extend it to preserve the original error message and bytecode offset.
This commit is contained in:
@@ -4,7 +4,7 @@ use cretonne_codegen::cursor::FuncCursor;
|
||||
use cretonne_codegen::ir::types::*;
|
||||
use cretonne_codegen::ir::{self, InstBuilder};
|
||||
use cretonne_codegen::settings;
|
||||
use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment};
|
||||
use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment, WasmResult};
|
||||
use func_translator::FuncTranslator;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
@@ -196,7 +196,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
sig_ref: ir::SigRef,
|
||||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
) -> ir::Inst {
|
||||
) -> WasmResult<ir::Inst> {
|
||||
// Pass the current function's vmctx parameter on to the callee.
|
||||
let vmctx = pos.func
|
||||
.special_param(ir::ArgumentPurpose::VMContext)
|
||||
@@ -224,9 +224,11 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
|
||||
args.push(vmctx, &mut pos.func.dfg.value_lists);
|
||||
|
||||
pos.ins()
|
||||
.CallIndirect(ir::Opcode::CallIndirect, VOID, sig_ref, args)
|
||||
.0
|
||||
Ok(
|
||||
pos.ins()
|
||||
.CallIndirect(ir::Opcode::CallIndirect, VOID, sig_ref, args)
|
||||
.0,
|
||||
)
|
||||
}
|
||||
|
||||
fn translate_call(
|
||||
@@ -235,7 +237,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
_callee_index: FunctionIndex,
|
||||
callee: ir::FuncRef,
|
||||
call_args: &[ir::Value],
|
||||
) -> ir::Inst {
|
||||
) -> WasmResult<ir::Inst> {
|
||||
// Pass the current function's vmctx parameter on to the callee.
|
||||
let vmctx = pos.func
|
||||
.special_param(ir::ArgumentPurpose::VMContext)
|
||||
@@ -247,7 +249,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
|
||||
args.push(vmctx, &mut pos.func.dfg.value_lists);
|
||||
|
||||
pos.ins().Call(ir::Opcode::Call, VOID, callee, args).0
|
||||
Ok(pos.ins().Call(ir::Opcode::Call, VOID, callee, args).0)
|
||||
}
|
||||
|
||||
fn translate_grow_memory(
|
||||
@@ -256,8 +258,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
_index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
_val: ir::Value,
|
||||
) -> ir::Value {
|
||||
pos.ins().iconst(I32, -1)
|
||||
) -> WasmResult<ir::Value> {
|
||||
Ok(pos.ins().iconst(I32, -1))
|
||||
}
|
||||
|
||||
fn translate_current_memory(
|
||||
@@ -265,8 +267,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
mut pos: FuncCursor,
|
||||
_index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
) -> ir::Value {
|
||||
pos.ins().iconst(I32, -1)
|
||||
) -> WasmResult<ir::Value> {
|
||||
Ok(pos.ins().iconst(I32, -1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +387,7 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
self.info.start_func = Some(func_index);
|
||||
}
|
||||
|
||||
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> Result<(), String> {
|
||||
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> {
|
||||
let func = {
|
||||
let mut func_environ = DummyFuncEnvironment::new(&self.info);
|
||||
let function_index = self.get_num_func_imports() + self.info.function_bodies.len();
|
||||
@@ -393,9 +395,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
|
||||
let sig = func_environ.vmctx_sig(self.get_func_type(function_index));
|
||||
let mut func = ir::Function::with_name_signature(name, sig);
|
||||
let reader = wasmparser::BinaryReader::new(body_bytes);
|
||||
self.trans
|
||||
.translate_from_reader(reader, &mut func, &mut func_environ)
|
||||
.map_err(|e| format!("{}", e))?;
|
||||
self.trans.translate_from_reader(
|
||||
reader,
|
||||
&mut func,
|
||||
&mut func_environ,
|
||||
)?;
|
||||
func
|
||||
};
|
||||
self.func_bytecode_sizes.push(body_bytes.len());
|
||||
|
||||
@@ -4,4 +4,4 @@ mod dummy;
|
||||
mod spec;
|
||||
|
||||
pub use environ::dummy::DummyEnvironment;
|
||||
pub use environ::spec::{FuncEnvironment, GlobalValue, ModuleEnvironment};
|
||||
pub use environ::spec::{FuncEnvironment, GlobalValue, ModuleEnvironment, WasmError, WasmResult};
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
use cretonne_codegen::cursor::FuncCursor;
|
||||
use cretonne_codegen::ir::{self, InstBuilder};
|
||||
use cretonne_codegen::settings::Flags;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex};
|
||||
use wasmparser::BinaryReaderError;
|
||||
|
||||
/// The value of a WebAssembly global variable.
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -23,6 +23,49 @@ pub enum GlobalValue {
|
||||
},
|
||||
}
|
||||
|
||||
/// A WebAssembly translation error.
|
||||
///
|
||||
/// When a WebAssembly function can't be translated, one of these error codes will be returned
|
||||
/// to describe the failure.
|
||||
#[derive(Fail, Debug, PartialEq, Eq)]
|
||||
pub enum WasmError {
|
||||
/// The input WebAssembly code is invalid.
|
||||
///
|
||||
/// This error code is used by a WebAssembly translator when it encounters invalid WebAssembly
|
||||
/// code. This should never happen for validated WebAssembly code.
|
||||
#[fail(display = "Invalid input WebAssembly code at offset {}: {}", _1, _0)]
|
||||
InvalidWebAssembly {
|
||||
message: &'static str,
|
||||
offset: usize,
|
||||
},
|
||||
|
||||
/// A feature used by the WebAssembly code is not supported by the embedding environment.
|
||||
///
|
||||
/// Embedding environments may have their own limitations and feature restrictions.
|
||||
#[fail(display = "Unsupported feature: {}", _0)]
|
||||
Unsupported(&'static str),
|
||||
|
||||
/// An implementation limit was exceeded.
|
||||
///
|
||||
/// Cretonne can compile very large and complicated functions, but the [implementation has
|
||||
/// limits][limits] that cause compilation to fail when they are exceeded.
|
||||
///
|
||||
/// [limits]: https://cretonne.readthedocs.io/en/latest/langref.html#implementation-limits
|
||||
#[fail(display = "Implementation limit exceeded")]
|
||||
ImplLimitExceeded,
|
||||
}
|
||||
|
||||
impl WasmError {
|
||||
/// Convert from a `BinaryReaderError` to a `WasmError`.
|
||||
pub fn from_binary_reader_error(e: BinaryReaderError) -> Self {
|
||||
let BinaryReaderError { message, offset } = e;
|
||||
WasmError::InvalidWebAssembly { message, offset }
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenient alias for a `Result` that uses `WasmError` as the error type.
|
||||
pub type WasmResult<T> = Result<T, WasmError>;
|
||||
|
||||
/// Environment affecting the translation of a single WebAssembly function.
|
||||
///
|
||||
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cretonne
|
||||
@@ -99,7 +142,7 @@ pub trait FuncEnvironment {
|
||||
sig_ref: ir::SigRef,
|
||||
callee: ir::Value,
|
||||
call_args: &[ir::Value],
|
||||
) -> ir::Inst;
|
||||
) -> WasmResult<ir::Inst>;
|
||||
|
||||
/// Translate a `call` WebAssembly instruction at `pos`.
|
||||
///
|
||||
@@ -114,8 +157,8 @@ pub trait FuncEnvironment {
|
||||
_callee_index: FunctionIndex,
|
||||
callee: ir::FuncRef,
|
||||
call_args: &[ir::Value],
|
||||
) -> ir::Inst {
|
||||
pos.ins().call(callee, call_args)
|
||||
) -> WasmResult<ir::Inst> {
|
||||
Ok(pos.ins().call(callee, call_args))
|
||||
}
|
||||
|
||||
/// Translate a `grow_memory` WebAssembly instruction.
|
||||
@@ -132,7 +175,7 @@ pub trait FuncEnvironment {
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
val: ir::Value,
|
||||
) -> ir::Value;
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Translates a `current_memory` WebAssembly instruction.
|
||||
///
|
||||
@@ -145,7 +188,7 @@ pub trait FuncEnvironment {
|
||||
pos: FuncCursor,
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
) -> ir::Value;
|
||||
) -> WasmResult<ir::Value>;
|
||||
|
||||
/// Emit code at the beginning of every wasm loop.
|
||||
///
|
||||
@@ -229,5 +272,5 @@ pub trait ModuleEnvironment<'data> {
|
||||
fn declare_start_func(&mut self, index: FunctionIndex);
|
||||
|
||||
/// Provides the contents of a function body.
|
||||
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> Result<(), String>;
|
||||
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user