Skip to content
This repository was archived by the owner on Jun 16, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 47 additions & 92 deletions src/binary_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* limitations under the License.
*/

use std::boxed::Box;
use std::convert::TryInto;
use std::str;
use std::vec::Vec;
Expand Down Expand Up @@ -361,23 +360,8 @@ impl<'a> BinaryReader<'a> {
}
}

fn read_br_table(&mut self) -> Result<BrTable<'a>> {
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> {
BrTable::read_table::<crate::BrTableBuilder>(self)
}

/// Returns whether the `BinaryReader` has reached the end of the file.
Expand Down Expand Up @@ -712,7 +696,7 @@ impl<'a> BinaryReader<'a> {
Ok(imm)
}

fn read_0xfe_operator(&mut self) -> Result<Operator<'a>> {
fn read_0xfe_operator(&mut self) -> Result<Operator> {
let code = self.read_u8()? as u8;
Ok(match code {
0x00 => Operator::AtomicNotify {
Expand Down Expand Up @@ -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<Operator<'a>> {
pub fn read_operator(&mut self) -> Result<Operator> {
let code = self.read_u8()? as u8;
Ok(match code {
0x00 => Operator::Unreachable,
Expand Down Expand Up @@ -1252,7 +1236,7 @@ impl<'a> BinaryReader<'a> {
})
}

fn read_0xfc_operator(&mut self) -> Result<Operator<'a>> {
fn read_0xfc_operator(&mut self) -> Result<Operator> {
let code = self.read_u8()? as u8;
Ok(match code {
0x00 => Operator::I32TruncSatF32S,
Expand Down Expand Up @@ -1364,7 +1348,7 @@ impl<'a> BinaryReader<'a> {
Ok(V128(bytes))
}

fn read_0xfd_operator(&mut self) -> Result<Operator<'a>> {
fn read_0xfd_operator(&mut self) -> Result<Operator> {
let code = self.read_var_u32()?;
Ok(match code {
0x00 => Operator::V128Load {
Expand Down Expand Up @@ -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::<BrTableBuilder>(&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<B>(reader: &mut BinaryReader) -> Result<Self>
where
B: WasmBrTableBuilder<BrTable = Self>,
{
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 = <B as WasmBrTableBuilder>::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<u32> {
if self.reader.eof() {
return None;
}
self.reader.read_var_u32().ok()
Ok(builder.default_target(default_target))
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
27 changes: 22 additions & 5 deletions src/operators_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -947,14 +947,31 @@ impl OperatorValidator {
Operator::BrTable { ref table } => {
self.check_operands_1(Type::I32)?;
let mut depth0: Option<u32> = None;
for relative_depth in table {
use crate::WasmBrTable as _;
let len_targets = table.len();
fn check_target(
validator: &OperatorValidator,
depth0: &mut Option<u32>,
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 => {
Expand Down
4 changes: 2 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub enum ParserState<'a> {
DataCountSectionEntry(u32),

BeginInitExpressionBody,
InitExpressionOperator(Operator<'a>),
InitExpressionOperator(Operator),
EndInitExpressionBody,

BeginFunctionBody {
Expand All @@ -113,7 +113,7 @@ pub enum ParserState<'a> {
FunctionBodyLocals {
locals: Box<[(u32, Type)]>,
},
CodeOperator(Operator<'a>),
CodeOperator(Operator),
EndFunctionBody,
SkippingFunctionBody,

Expand Down
104 changes: 97 additions & 7 deletions src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>;

/// 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<u32> {
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<u32>,
}

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
Expand Down Expand Up @@ -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 },
Expand All @@ -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 },
Expand Down
Loading