wasm: Create ParseError type

This commit is contained in:
Brian Carroll 2022-05-22 10:42:30 +01:00
parent 66c78ceed5
commit c16a5ad8ae
No known key found for this signature in database
GPG key ID: 9CF4E3BF9C4722C7
7 changed files with 72 additions and 40 deletions

View file

@ -4,18 +4,24 @@ use bumpalo::Bump;
/// Parse serialized bytes into a data structure
/// Specific parsers may need contextual data from other parts of the .wasm file
pub trait Parse<ParseContext>: Sized {
fn parse(ctx: ParseContext, bytes: &[u8], cursor: &mut usize) -> Result<Self, String>;
fn parse(ctx: ParseContext, bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError>;
}
#[derive(Debug)]
pub struct ParseError {
pub offset: usize,
pub message: String,
}
/// Skip over serialized bytes for a type
/// This may, or may not, require looking at the byte values
pub trait SkipBytes: Sized {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), String>;
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), ParseError>;
}
/// Decode an unsigned 32-bit integer from the provided buffer in LEB-128 format
/// Return the integer itself and the offset after it ends
fn decode_u32(bytes: &[u8]) -> Result<(u32, usize), String> {
fn decode_u32(bytes: &[u8]) -> Result<(u32, usize), ()> {
let mut value = 0;
let mut shift = 0;
for (i, byte) in bytes.iter().take(MAX_SIZE_ENCODED_U32).enumerate() {
@ -25,23 +31,30 @@ fn decode_u32(bytes: &[u8]) -> Result<(u32, usize), String> {
}
shift += 7;
}
Err(format!(
"Failed to decode u32 as LEB-128 from bytes: {:2x?}",
std::vec::Vec::from_iter(bytes.iter().take(MAX_SIZE_ENCODED_U32))
))
Err(())
}
impl Parse<()> for u32 {
fn parse(_ctx: (), bytes: &[u8], cursor: &mut usize) -> Result<Self, String> {
let (value, len) = decode_u32(&bytes[*cursor..])?;
*cursor += len;
Ok(value)
fn parse(_ctx: (), bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
match decode_u32(&bytes[*cursor..]) {
Ok((value, len)) => {
*cursor += len;
Ok(value)
}
Err(()) => Err(ParseError {
offset: *cursor,
message: format!(
"Failed to decode u32 as LEB-128 from bytes: {:2x?}",
&bytes[*cursor..][..MAX_SIZE_ENCODED_U32]
),
}),
}
}
}
// Parse string bytes without utf8 validation
impl<'a> Parse<&'a Bump> for &'a [u8] {
fn parse(arena: &'a Bump, bytes: &[u8], cursor: &mut usize) -> Result<Self, String> {
fn parse(arena: &'a Bump, bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
let len = u32::parse((), bytes, cursor)?;
let end = *cursor + len as usize;
let bytes: &[u8] = &bytes[*cursor..end];
@ -52,7 +65,7 @@ impl<'a> Parse<&'a Bump> for &'a [u8] {
}
impl SkipBytes for u32 {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), String> {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), ParseError> {
const MAX_LEN: usize = 5;
for (i, byte) in bytes.iter().enumerate().skip(*cursor).take(MAX_LEN) {
if byte & 0x80 == 0 {
@ -60,12 +73,15 @@ impl SkipBytes for u32 {
return Ok(());
}
}
Err("Invalid LEB encoding".into())
Err(ParseError {
offset: *cursor,
message: "Invalid LEB encoding".into(),
})
}
}
impl SkipBytes for u64 {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), String> {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), ParseError> {
const MAX_LEN: usize = 10;
for (i, byte) in bytes.iter().enumerate().skip(*cursor).take(MAX_LEN) {
if byte & 0x80 == 0 {
@ -73,12 +89,15 @@ impl SkipBytes for u64 {
return Ok(());
}
}
Err("Invalid LEB encoding".into())
Err(ParseError {
offset: *cursor,
message: "Invalid LEB encoding".into(),
})
}
}
impl SkipBytes for u8 {
fn skip_bytes(_bytes: &[u8], cursor: &mut usize) -> Result<(), String> {
fn skip_bytes(_bytes: &[u8], cursor: &mut usize) -> Result<(), ParseError> {
*cursor += 1;
Ok(())
}
@ -86,7 +105,7 @@ impl SkipBytes for u8 {
/// Note: This is just for skipping over Wasm bytes. We don't actually care about String vs str!
impl SkipBytes for String {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), String> {
fn skip_bytes(bytes: &[u8], cursor: &mut usize) -> Result<(), ParseError> {
let len = u32::parse((), bytes, cursor)?;
if false {