diff --git a/src/binary_reader.rs b/src/binary_reader.rs index 96377fb3..0df1e6a2 100644 --- a/src/binary_reader.rs +++ b/src/binary_reader.rs @@ -13,7 +13,6 @@ * limitations under the License. */ -use std::boxed::Box; use std::convert::TryInto; use std::str; use std::vec::Vec; @@ -361,23 +360,8 @@ impl<'a> BinaryReader<'a> { } } - fn read_br_table(&mut self) -> Result> { - let targets_len = self.read_var_u32()? as usize; - if targets_len > MAX_WASM_BR_TABLE_SIZE { - return Err(BinaryReaderError::new( - "br_table size is out of bound", - self.original_position() - 1, - )); - } - let start = self.position; - for _ in 0..targets_len { - self.skip_var_32()?; - } - self.skip_var_32()?; - Ok(BrTable { - buffer: &self.buffer[start..self.position], - cnt: targets_len as usize, - }) + fn read_br_table(&mut self) -> Result { + BrTable::read_table::(self) } /// Returns whether the `BinaryReader` has reached the end of the file. @@ -712,7 +696,7 @@ impl<'a> BinaryReader<'a> { Ok(imm) } - fn read_0xfe_operator(&mut self) -> Result> { + fn read_0xfe_operator(&mut self) -> Result { let code = self.read_u8()? as u8; Ok(match code { 0x00 => Operator::AtomicNotify { @@ -944,7 +928,7 @@ impl<'a> BinaryReader<'a> { /// # Errors /// If `BinaryReader` has less bytes remaining than required to parse /// the `Operator`. - pub fn read_operator(&mut self) -> Result> { + pub fn read_operator(&mut self) -> Result { let code = self.read_u8()? as u8; Ok(match code { 0x00 => Operator::Unreachable, @@ -1252,7 +1236,7 @@ impl<'a> BinaryReader<'a> { }) } - fn read_0xfc_operator(&mut self) -> Result> { + fn read_0xfc_operator(&mut self) -> Result { let code = self.read_u8()? as u8; Ok(match code { 0x00 => Operator::I32TruncSatF32S, @@ -1364,7 +1348,7 @@ impl<'a> BinaryReader<'a> { Ok(V128(bytes)) } - fn read_0xfd_operator(&mut self) -> Result> { + fn read_0xfd_operator(&mut self) -> Result { let code = self.read_var_u32()?; Ok(match code { 0x00 => Operator::V128Load { @@ -1696,85 +1680,56 @@ impl<'a> BinaryReader<'a> { } } -impl<'a> BrTable<'a> { - /// Returns the number of `br_table` entries, not including the default - /// label - pub fn len(&self) -> usize { - self.cnt - } - - /// Returns whether `BrTable` doesn't have any labels apart from the default one. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } +use crate::primitives::WasmBrTableBuilder; - /// Reads br_table entries. +impl BrTable { + /// Reads branch table (`br_table`) entries from the given buffer. /// /// # Examples + /// /// ```rust - /// let buf = vec![0x0e, 0x02, 0x01, 0x02, 0x00]; - /// let mut reader = wasmparser::BinaryReader::new(&buf); - /// let op = reader.read_operator().unwrap(); - /// if let wasmparser::Operator::BrTable { ref table } = op { - /// let br_table_depths = table.read_table().unwrap(); - /// assert!(br_table_depths.0 == vec![1,2].into_boxed_slice() && - /// br_table_depths.1 == 0); - /// } else { - /// unreachable!(); - /// } + /// # use wasmparser::{BinaryReader, BrTable, BrTableBuilder, WasmBrTable, WasmBrTableBuilder}; + /// // `0x0e` (`br_table` ID) and count already parsed at this point: + /// let buffer = vec![0x02, 0x01, 0x02, 0x00]; + /// let mut reader = BinaryReader::new(&buffer); + /// let br_table = BrTable::read_table::(&mut reader).unwrap(); + /// let expected = { + /// let mut builder = BrTableBuilder::new(2); + /// builder.push_target(1); + /// builder.push_target(2); + /// builder.default_target(0) + /// }; + /// assert_eq!(br_table, expected); /// ``` - pub fn read_table(&self) -> Result<(Box<[u32]>, u32)> { - let mut reader = BinaryReader::new(self.buffer); - let mut table = Vec::new(); - while !reader.eof() { - table.push(reader.read_var_u32()?); + pub fn read_table(reader: &mut BinaryReader) -> Result + where + B: WasmBrTableBuilder, + { + let targets_len = reader.read_var_u32().map_err(|_| { + BinaryReaderError::new("br_table: missing target count", reader.original_position()) + })? as usize; + if targets_len > MAX_WASM_BR_TABLE_SIZE { + return Err(BinaryReaderError::new( + "br_table: size is out of bound", + reader.original_position() - 1, + )); + } + let mut builder = ::new(targets_len); + for _ in 0..targets_len { + let target = reader.read_var_u32().map_err(|_| { + BinaryReaderError::new( + "br_table: encountered invalid or missing branch target", + reader.original_position(), + ) + })?; + builder.push_target(target); } - let default_target = table.pop().ok_or_else(|| { + let default_target = reader.read_var_u32().map_err(|_| { BinaryReaderError::new( - "br_table missing default target", + "br_table: missing default target", reader.original_position(), ) })?; - Ok((table.into_boxed_slice(), default_target)) - } -} - -/// Iterator for `BrTable`. -/// -/// #Examples -/// ```rust -/// let buf = vec![0x0e, 0x02, 0x01, 0x02, 0x00]; -/// let mut reader = wasmparser::BinaryReader::new(&buf); -/// let op = reader.read_operator().unwrap(); -/// if let wasmparser::Operator::BrTable { ref table } = op { -/// for depth in table { -/// println!("BrTable depth: {}", depth); -/// } -/// } -/// ``` -#[derive(Clone, Debug)] -pub struct BrTableIterator<'a> { - reader: BinaryReader<'a>, -} - -impl<'a> IntoIterator for &'a BrTable<'a> { - type Item = u32; - type IntoIter = BrTableIterator<'a>; - - fn into_iter(self) -> Self::IntoIter { - BrTableIterator { - reader: BinaryReader::new(self.buffer), - } - } -} - -impl<'a> Iterator for BrTableIterator<'a> { - type Item = u32; - - fn next(&mut self) -> Option { - if self.reader.eof() { - return None; - } - self.reader.read_var_u32().ok() + Ok(builder.default_target(default_target)) } } diff --git a/src/lib.rs b/src/lib.rs index e581b932..8f8028bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,7 @@ pub use crate::parser::WasmDecoder; pub use crate::primitives::BinaryReaderError; pub use crate::primitives::BrTable; +pub use crate::primitives::BrTableBuilder; pub use crate::primitives::CustomSectionKind; pub use crate::primitives::ExternalKind; pub use crate::primitives::FuncType; @@ -58,6 +59,8 @@ pub use crate::primitives::SectionCode; pub use crate::primitives::TableType; pub use crate::primitives::Type; pub use crate::primitives::TypeOrFuncType; +pub use crate::primitives::WasmBrTable; +pub use crate::primitives::WasmBrTableBuilder; pub use crate::primitives::V128; pub use crate::validator::validate; diff --git a/src/operators_validator.rs b/src/operators_validator.rs index f9ca198d..f7662d49 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -947,14 +947,31 @@ impl OperatorValidator { Operator::BrTable { ref table } => { self.check_operands_1(Type::I32)?; let mut depth0: Option = None; - for relative_depth in table { + use crate::WasmBrTable as _; + let len_targets = table.len(); + fn check_target( + validator: &OperatorValidator, + depth0: &mut Option, + relative_depth: u32, + ) -> OperatorValidatorResult<()> { if depth0.is_none() { - self.check_jump_from_block(relative_depth, 1)?; - depth0 = Some(relative_depth); - continue; + validator.check_jump_from_block(relative_depth, 1)?; + depth0.replace(relative_depth); + } else { + validator.match_block_return(relative_depth, depth0.unwrap())?; } - self.match_block_return(relative_depth, depth0.unwrap())?; + validator.match_block_return(relative_depth, depth0.unwrap())?; + Ok(()) + } + // Check normal branch targets: + for i in 0..len_targets { + let relative_depth = + table.target_offset(i).expect("encountered missing target"); + check_target(self, &mut depth0, relative_depth)?; } + // Check default branch target: + let default_depth = table.default_offset(); + check_target(self, &mut depth0, default_depth)?; self.func_state.start_dead_code() } Operator::Return => { diff --git a/src/parser.rs b/src/parser.rs index f8ef88d9..5f9b8646 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -104,7 +104,7 @@ pub enum ParserState<'a> { DataCountSectionEntry(u32), BeginInitExpressionBody, - InitExpressionOperator(Operator<'a>), + InitExpressionOperator(Operator), EndInitExpressionBody, BeginFunctionBody { @@ -113,7 +113,7 @@ pub enum ParserState<'a> { FunctionBodyLocals { locals: Box<[(u32, Type)]>, }, - CodeOperator(Operator<'a>), + CodeOperator(Operator), EndFunctionBody, SkippingFunctionBody, diff --git a/src/primitives.rs b/src/primitives.rs index d55dd350..5e546b88 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -212,11 +212,101 @@ pub enum RelocType { GlobalIndexLEB, } -/// A br_table entries representation. -#[derive(Debug, Clone)] -pub struct BrTable<'a> { - pub(crate) buffer: &'a [u8], - pub(crate) cnt: usize, +/// Trait implemented by Wasm branching table (`br_table`) operators. +pub trait WasmBrTable { + /// Returns the number of branching targets, not including the default label. + fn len(&self) -> usize; + + /// Returns `true` if the branch table doesn't have any labels apart from the default one. + fn is_empty(&self) -> bool; + + /// Returns the branch offset for the target at the given index. + fn target_offset(&self, at: usize) -> Option; + + /// Returns the default branch offset. + fn default_offset(&self) -> u32; +} + +/// Trait implemented by Wasm branching table builders. +/// +/// A branching table builder can build up a Wasm branch table incrementally +/// while upholding its invariants throughout the building process. This way +/// the internals of the Wasm branching table are less constraint by the build +/// procedure. +pub trait WasmBrTableBuilder { + /// The branch table that is going to be build. + type BrTable; + + /// Creates a new branch table builder. + /// + /// The `targets_hint` tells the builder how many branch targets it can + /// expect. This can be used by the builder in order to speed-up + /// the branch table construction. + fn new(targets_hint: usize) -> Self; + + /// Pushes another branching target offset to the built branch table. + fn push_target(&mut self, offset: u32); + + /// Finalizes the branching table with the given default offset. + fn default_target(self, default_offset: u32) -> Self::BrTable; +} + +/// A branch table (`br_table`). +/// +/// Stores its target and default branch offsets. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BrTable { + /// Non-empty vector storing the target offsets followed by the default offset. + targets: Box<[u32]>, + /// The default target offset. + default_target: u32, +} + +impl WasmBrTable for BrTable { + fn len(&self) -> usize { + self.targets.len().saturating_sub(1) + } + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn target_offset(&self, at: usize) -> Option { + self.targets.get(at).copied() + } + + fn default_offset(&self) -> u32 { + self.default_target + } +} + +/// A builder for a branch table (`br_table`). +#[derive(Debug)] +pub struct BrTableBuilder { + /// The building branch table. + targets: Vec, +} + +impl WasmBrTableBuilder for BrTableBuilder { + type BrTable = BrTable; + + fn new(targets_hint: usize) -> Self { + Self { + targets: Vec::with_capacity(targets_hint), + } + } + + fn push_target(&mut self, target: u32) { + self.targets.push(target); + } + + fn default_target(mut self, default_target: u32) -> BrTable { + self.targets.push(default_target); + BrTable { + targets: self.targets.into_boxed_slice(), + default_target, + } + } } /// An IEEE binary32 immediate floating point value, represented as a u32 @@ -260,7 +350,7 @@ pub type SIMDLaneIndex = u8; /// /// [here]: https://webassembly.github.io/spec/core/binary/instructions.html #[derive(Debug, Clone)] -pub enum Operator<'a> { +pub enum Operator { Unreachable, Nop, Block { ty: TypeOrFuncType }, @@ -270,7 +360,7 @@ pub enum Operator<'a> { End, Br { relative_depth: u32 }, BrIf { relative_depth: u32 }, - BrTable { table: BrTable<'a> }, + BrTable { table: BrTable }, Return, Call { function_index: u32 }, CallIndirect { index: u32, table_index: u32 }, diff --git a/src/readers/operators.rs b/src/readers/operators.rs index 6791ab80..c0f1b27e 100644 --- a/src/readers/operators.rs +++ b/src/readers/operators.rs @@ -48,7 +48,7 @@ impl<'a> OperatorsReader<'a> { )) } - pub fn read<'b>(&mut self) -> Result> + pub fn read<'b>(&mut self) -> Result where 'a: 'b, { @@ -65,7 +65,7 @@ impl<'a> OperatorsReader<'a> { } } - pub fn read_with_offset<'b>(&mut self) -> Result<(Operator<'b>, usize)> + pub fn read_with_offset<'b>(&mut self) -> Result<(Operator, usize)> where 'a: 'b, { @@ -75,7 +75,7 @@ impl<'a> OperatorsReader<'a> { } impl<'a> IntoIterator for OperatorsReader<'a> { - type Item = Result>; + type Item = Result; type IntoIter = OperatorsIterator<'a>; /// Reads content of the code section. @@ -116,7 +116,7 @@ pub struct OperatorsIterator<'a> { } impl<'a> Iterator for OperatorsIterator<'a> { - type Item = Result>; + type Item = Result; fn next(&mut self) -> Option { if self.err || self.reader.eof() { @@ -134,7 +134,7 @@ pub struct OperatorsIteratorWithOffsets<'a> { } impl<'a> Iterator for OperatorsIteratorWithOffsets<'a> { - type Item = Result<(Operator<'a>, usize)>; + type Item = Result<(Operator, usize)>; /// Reads content of the code section with offsets. /// diff --git a/src/validator.rs b/src/validator.rs index 6b2ec95b..e232d806 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -853,7 +853,7 @@ impl<'b> ValidatingOperatorParser<'b> { MemoryType = M, GlobalType = G, >, - ) -> Result> + ) -> Result where 'b: 'c, {