diff --git a/src/chunk_list.rs b/src/chunk_list.rs deleted file mode 100644 index 6db44503..00000000 --- a/src/chunk_list.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! The `ChunkList` type. - -use std::slice; - -/// The `ChunkList` type is used in validation contexts. It lets us prepend to a -/// list without copying the whole thing, which is something that wasm -/// validation does frequently. -#[derive(Debug)] -pub struct ChunkList<'a, T: 'a> { - head: Vec, - tail: Option<&'a ChunkList<'a, T>>, -} - -impl<'a, T: 'a> ChunkList<'a, T> { - /// Construct a new, empty chunk list. - pub fn new() -> ChunkList<'a, T> { - ChunkList { - head: vec![], - tail: None, - } - } - - /// Construct a new chunk list with the given elements. - pub fn with_head(head: Vec) -> ChunkList<'a, T> { - ChunkList { head, tail: None } - } - - /// Construct a new chunk list whose elements will be prepended in front of - /// the given tail. - pub fn with_tail(tail: &'a ChunkList<'a, T>) -> ChunkList<'a, T> { - ChunkList { - head: vec![], - tail: Some(tail), - } - } - - /// Construct a new chunk list with the given head elements and tail elements. - pub fn with_head_and_tail(head: Vec, tail: &'a ChunkList<'a, T>) -> ChunkList<'a, T> { - ChunkList { - head, - tail: Some(tail), - } - } - - /// Get a reference to the idx^th item in this chunk list. - pub fn get(&self, mut idx: usize) -> Option<&T> { - let mut chunk = self; - while chunk.head.len() <= idx { - idx = idx - chunk.head.len(); - chunk = chunk.tail?; - } - chunk.head.get(idx) - } - - /// Get the total length of this chunk list. - pub fn len(&self) -> usize { - let mut chunk = self; - let mut len = 0; - loop { - len += chunk.head.len(); - match chunk.tail { - None => return len, - Some(tail) => { - chunk = tail; - } - } - } - } - - /// Iterate over the items in this chunk list. - pub fn iter<'me>(&'me self) -> Iter<'me, 'a, T> { - Iter { - iter: self.head.iter(), - list: self, - } - } -} - -/// An iterator over a chunk list. -#[derive(Clone, Debug)] -pub struct Iter<'a, 'b, T> { - iter: slice::Iter<'a, T>, - list: &'a ChunkList<'b, T>, -} - -impl<'a, 'b, T> Iterator for Iter<'a, 'b, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - loop { - if let Some(x) = self.iter.next() { - return Some(x); - } - - self.list = match self.list.tail { - None => return None, - Some(t) => t, - }; - self.iter = self.list.head.iter(); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index dd9598f4..db457d53 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ #![deny(missing_docs)] mod arena_set; -mod chunk_list; pub mod const_value; pub mod dot; mod emit; @@ -14,4 +13,3 @@ pub mod module; mod parse; pub mod passes; pub mod ty; -pub mod validation_context; diff --git a/src/module/functions/local_function/context.rs b/src/module/functions/local_function/context.rs index a73e2a30..b9ea4139 100644 --- a/src/module/functions/local_function/context.rs +++ b/src/module/functions/local_function/context.rs @@ -6,7 +6,6 @@ use crate::module::functions::{FunctionId, LocalFunction}; use crate::module::Module; use crate::parse::IndicesToIds; use crate::ty::ValType; -use crate::validation_context::ValidationContext; use failure::Fail; #[derive(Debug)] @@ -44,7 +43,7 @@ pub type ControlStack = Vec; #[derive(Debug)] pub struct FunctionContext<'a> { /// The module that we're adding a function for. - pub module: &'a mut Module, + pub module: &'a Module, /// Mapping of indexes back to ids. pub indices: &'a IndicesToIds, @@ -55,9 +54,6 @@ pub struct FunctionContext<'a> { /// The function being validated/constructed. pub func: &'a mut LocalFunction, - /// The context under which the function is being validated/constructed. - pub validation: &'a ValidationContext<'a>, - /// The operands stack. pub operands: &'a mut OperandStack, @@ -68,11 +64,10 @@ pub struct FunctionContext<'a> { impl<'a> FunctionContext<'a> { /// Create a new function context. pub fn new( - module: &'a mut Module, + module: &'a Module, indices: &'a IndicesToIds, func_id: FunctionId, func: &'a mut LocalFunction, - validation: &'a ValidationContext<'a>, operands: &'a mut OperandStack, controls: &'a mut ControlStack, ) -> FunctionContext<'a> { @@ -81,24 +76,11 @@ impl<'a> FunctionContext<'a> { indices, func_id, func, - validation, operands, controls, } } - pub fn nested<'b>(&'b mut self, validation: &'b ValidationContext<'b>) -> FunctionContext<'b> { - FunctionContext { - module: self.module, - indices: self.indices, - func_id: self.func_id, - func: self.func, - validation, - operands: self.operands, - controls: self.controls, - } - } - pub fn push_operand(&mut self, op: Option, expr: E) where E: Copy + Into, @@ -165,9 +147,12 @@ impl<'a> FunctionContext<'a> { impl_unreachable(&mut self.operands, &mut self.controls, expr); } - pub fn control(&self, n: usize) -> &ControlFrame { + pub fn control(&self, n: usize) -> Result<&ControlFrame> { + if n >= self.controls.len() { + failure::bail!("jump to nonexistent control block"); + } let idx = self.controls.len() - n - 1; - &self.controls[idx] + Ok(&self.controls[idx]) } pub fn add_to_block(&mut self, block: BlockId, expr: E) @@ -182,7 +167,7 @@ impl<'a> FunctionContext<'a> { where E: Into, { - let ctrl = self.control(control_frame); + let ctrl = self.control(control_frame).unwrap(); if ctrl.unreachable.is_some() { return; } diff --git a/src/module/functions/local_function/mod.rs b/src/module/functions/local_function/mod.rs index 3be0a4e0..d207d425 100644 --- a/src/module/functions/local_function/mod.rs +++ b/src/module/functions/local_function/mod.rs @@ -15,7 +15,6 @@ use crate::module::locals::ModuleLocals; use crate::module::Module; use crate::parse::IndicesToIds; use crate::ty::{TypeId, ValType}; -use crate::validation_context::ValidationContext; use failure::{bail, Fail, ResultExt}; use id_arena::{Arena, Id}; use parity_wasm::elements; @@ -53,11 +52,8 @@ impl LocalFunction { indices: &mut IndicesToIds, id: FunctionId, ty: TypeId, - validation: &ValidationContext, body: wasmparser::FunctionBody, ) -> Result { - let validation = validation.for_function(&module.types.get(ty)); - // First up, implicitly add locals for all function arguments. We also // record these in the function itself for later processing. let mut args = Vec::new(); @@ -108,7 +104,6 @@ impl LocalFunction { indices, id, &mut func, - &validation, operands, controls, ); @@ -455,7 +450,7 @@ fn validate_instruction( .get_func(function_index) .context("invalid call")?; let ty_id = ctx.module.funcs.get(func).ty(); - let fun_ty = ctx.module.types.get(ty_id).clone(); + let fun_ty = ctx.module.types.get(ty_id); let mut args = ctx.pop_operands(fun_ty.params())?.into_boxed_slice(); args.reverse(); let expr = ctx.func.alloc(Call { func, args }); @@ -466,7 +461,7 @@ fn validate_instruction( .indices .get_type(index) .context("invalid call_indirect")?; - let ty = ctx.module.types.get(type_id).clone(); + let ty = ctx.module.types.get(type_id); let table = ctx .indices .get_table(table_index) @@ -707,13 +702,9 @@ fn validate_instruction( ctx.push_operand(t2, expr); } Operator::Return => { - let expected: Vec<_> = ctx - .validation - .return_ - .iter() - .flat_map(|b| b.iter().cloned()) - .collect(); - let values = ctx.pop_operands(&expected)?.into_boxed_slice(); + let fn_ty = ctx.module.funcs.get(ctx.func_id).ty(); + let expected = ctx.module.types.get(fn_ty).results(); + let values = ctx.pop_operands(expected)?.into_boxed_slice(); let expr = ctx.func.alloc(Return { values }); ctx.unreachable(expr); } @@ -723,31 +714,24 @@ fn validate_instruction( } Operator::Block { ty } => { let results = ValType::from_block_ty(ty)?; - let validation = ctx.validation.for_block(results.clone()); - let mut ctx = ctx.nested(&validation); ctx.push_control(BlockKind::Block, results.clone(), results); - validate_instruction_sequence_until_end(&mut ctx, ops)?; - validate_end(&mut ctx)?; + validate_instruction_sequence_until_end(ctx, ops)?; + validate_end(ctx)?; } Operator::Loop { ty } => { - let validation = ctx.validation.for_loop(); - let mut ctx = ctx.nested(&validation); let t = ValType::from_block_ty(ty)?; ctx.push_control(BlockKind::Loop, vec![].into_boxed_slice(), t); - validate_instruction_sequence_until_end(&mut ctx, ops)?; - validate_end(&mut ctx)?; + validate_instruction_sequence_until_end(ctx, ops)?; + validate_end(ctx)?; } Operator::If { ty } => { let ty = ValType::from_block_ty(ty)?; - let validation = ctx.validation.for_if_else(ty.clone()); - let mut ctx = ctx.nested(&validation); - let (_, condition) = ctx.pop_operand_expected(Some(ValType::I32))?; let consequent = ctx.push_control(BlockKind::IfElse, ty.clone(), ty.clone()); let mut else_found = false; - validate_instruction_sequence_until(&mut ctx, ops, |i| match i { + validate_instruction_sequence_until(ctx, ops, |i| match i { Operator::Else => { else_found = true; true @@ -760,7 +744,7 @@ fn validate_instruction( let alternative = ctx.push_control(BlockKind::IfElse, results.clone(), results); if else_found { - validate_instruction_sequence_until_end(&mut ctx, ops)?; + validate_instruction_sequence_until_end(ctx, ops)?; } let (results, _block) = ctx.pop_control()?; @@ -782,21 +766,11 @@ fn validate_instruction( .into()); } Operator::Br { relative_depth } => { - ctx.validation - .label(relative_depth) - .context("`br` to out-of-bounds block")?; - let n = relative_depth as usize; - if ctx.controls.len() <= n { - return Err(ErrorKind::InvalidWasm - .context("attempt to branch to out-of-bounds block") - .into()); - } - - let expected = ctx.control(n).label_types.clone(); + let expected = ctx.control(n)?.label_types.clone(); let args = ctx.pop_operands(&expected)?.into_boxed_slice(); - let to_block = ctx.control(n).block; + let to_block = ctx.control(n)?.block; let expr = ctx.func.alloc(Br { block: to_block, args, @@ -804,23 +778,13 @@ fn validate_instruction( ctx.unreachable(expr); } Operator::BrIf { relative_depth } => { - ctx.validation - .label(relative_depth) - .context("`br_if` to out-of-bounds block")?; - let n = relative_depth as usize; - if ctx.controls.len() <= n { - return Err(ErrorKind::InvalidWasm - .context("attempt to branch to out-of-bounds block") - .into()); - } - let (_, condition) = ctx.pop_operand_expected(Some(ValType::I32))?; - let expected = ctx.control(n).label_types.clone(); + let expected = ctx.control(n)?.label_types.clone(); let args = ctx.pop_operands(&expected)?.into_boxed_slice(); - let to_block = ctx.control(n).block; + let to_block = ctx.control(n)?.block; let expr = ctx.func.alloc(BrIf { condition, block: to_block, @@ -838,16 +802,7 @@ fn validate_instruction( Some(n) => n, None => bail!("malformed `br_table"), }; - ctx.validation - .label(n) - .context("`br_table` with out-of-bounds block")?; - let n = n as usize; - if ctx.controls.len() < n { - return Err(ErrorKind::InvalidWasm - .context("attempt to jump to an out-of-bounds block from a table entry") - .into()); - } - let control = ctx.control(n); + let control = ctx.control(n as usize)?; match label_types { None => label_types = Some(&control.label_types), Some(n) => { diff --git a/src/module/functions/mod.rs b/src/module/functions/mod.rs index dd52b5d2..b8131deb 100644 --- a/src/module/functions/mod.rs +++ b/src/module/functions/mod.rs @@ -9,7 +9,6 @@ use crate::module::imports::ImportId; use crate::module::Module; use crate::parse::IndicesToIds; use crate::ty::TypeId; -use crate::validation_context::ValidationContext; use failure::bail; use id_arena::{Arena, Id}; use parity_wasm::elements; @@ -212,7 +211,6 @@ impl Module { bail!("code and function sections must have same number of entries") } let num_imports = self.funcs.arena.len() - (amt as usize); - let validation = ValidationContext::new(); for (i, body) in section.into_iter().enumerate() { let body = body?; @@ -223,7 +221,7 @@ impl Module { _ => unreachable!(), }; - let local = LocalFunction::parse(self, indices, id, ty, &validation, body)?; + let local = LocalFunction::parse(self, indices, id, ty, body)?; self.funcs.arena[id] = Function { id, kind: FunctionKind::Local(local), diff --git a/src/validation_context.rs b/src/validation_context.rs deleted file mode 100644 index 377919bc..00000000 --- a/src/validation_context.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Wasm validation context. - -use super::chunk_list::ChunkList; -use super::error::{ErrorKind, Result}; -use crate::ty::{Type, ValType}; -use failure::Fail; -use std::u32; - -/// Wasm validation context. -/// -/// https://webassembly.github.io/spec/core/valid/conventions.html#contexts -#[derive(Debug)] -pub struct ValidationContext<'a> { - pub(crate) labels: ChunkList<'a, Box<[ValType]>>, - pub(crate) return_: ChunkList<'a, Box<[ValType]>>, -} - -impl<'a> ValidationContext<'a> { - /// Construct a `ValidationContext` from the given module. - pub fn new() -> ValidationContext<'a> { - ValidationContext { - labels: ChunkList::new(), - return_: ChunkList::new(), - } - } - - /// Get a nested validation context. - pub fn nested<'b>(&'b self) -> ValidationContext<'b> { - ValidationContext { - labels: ChunkList::with_tail(&self.labels), - return_: ChunkList::with_tail(&self.return_), - } - } - - /// Get a nested validation context for the given function. - pub fn for_function<'b>(&'b self, ty: &Type) -> ValidationContext<'b> { - let labels = vec![ty - .results() - .iter() - .cloned() - .collect::>() - .into_boxed_slice()]; - let return_ = labels.clone(); - - ValidationContext { - labels: ChunkList::with_head(labels), - return_: ChunkList::with_head(return_), - } - } - - /// Get a nested validation context for a block with the given label. - pub fn for_block(&self, label: Box<[ValType]>) -> ValidationContext { - ValidationContext { - labels: ChunkList::with_head_and_tail(vec![label], &self.labels), - ..self.nested() - } - } - - /// Get a nested validation context for a loop. - pub fn for_loop(&self) -> ValidationContext { - ValidationContext { - labels: ChunkList::with_head_and_tail(vec![vec![].into_boxed_slice()], &self.labels), - ..self.nested() - } - } - - /// Get a nested validation context for an if/else. - pub fn for_if_else(&self, label: Box<[ValType]>) -> ValidationContext { - ValidationContext { - labels: ChunkList::with_head_and_tail(vec![label], &self.labels), - ..self.nested() - } - } - - /// Get the type of the n^th local. - pub fn label(&self, n: u32) -> Result<&[ValType]> { - self.labels.get(n as usize).map(|t| &t[..]).ok_or_else(|| { - ErrorKind::InvalidWasm - .context(format!( - "local {} is out of bounds ({} labels)", - n, - self.labels.len() - )) - .into() - }) - } -}