Skip to content
Open
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
17 changes: 16 additions & 1 deletion cutile-rs/cutile-ir/src/bytecode/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ impl<'a> Reader<'a> {
}

fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
if self.pos + n > self.data.len() {
if n > self.remaining() {
return Err(err("unexpected end of data"));
}
let slice = &self.data[self.pos..self.pos + n];
Expand All @@ -191,6 +191,10 @@ impl<'a> Reader<'a> {
let mut shift: u32 = 0;
loop {
let b = self.read_byte()?;
// The tenth byte can contribute only bit 63 and cannot continue.
if shift == 63 && b > 1 {
return Err(err("varint overflow"));
}
result |= ((b & 0x7F) as u64) << shift;
if b & 0x80 == 0 {
break;
Expand Down Expand Up @@ -884,6 +888,17 @@ mod tests {
assert!(decode_bytecode(&data).is_err());
}

#[test]
fn varint_u64_boundaries() {
for value in [0, 127, 128, 1 << 63, u64::MAX] {
let mut writer = EncodingWriter::new();
writer.write_varint(value);
let mut reader = Reader::new(writer.as_bytes());
assert_eq!(reader.read_varint().unwrap(), value);
assert_eq!(reader.remaining(), 0);
}
}

#[test]
fn roundtrip_string_section() {
// Build a bytecode with just a string section.
Expand Down
39 changes: 39 additions & 0 deletions cutile-rs/cutile-ir/tests/decode_rejection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//! on valid minimal modules.

use cutile_ir::builder::{append_op, build_single_block_region, OpBuilder};
use cutile_ir::bytecode::encoding::EncodingWriter;
use cutile_ir::bytecode::{BytecodeVersion, Opcode, Section, MAGIC};
use cutile_ir::ir::*;
use cutile_ir::{decode_bytecode, write_bytecode};
Expand Down Expand Up @@ -124,6 +125,44 @@ fn reject_invalid_section_marker() {
);
}

#[test]
fn reject_section_length_overflow() {
let mut buf = minimal_bytecode();
buf.pop();
buf.push(Section::String as u8);
let mut length = EncodingWriter::new();
length.write_varint(usize::MAX as u64);
buf.extend_from_slice(length.as_bytes());
buf.push(Section::EndOfBytecode as u8);

assert!(
decode_bytecode(&buf).is_err(),
"an oversized section length should return an error"
);
}

#[test]
fn reject_section_length_varint_overflow() {
// The tenth byte has only one payload bit available in a u64 varint.
// Without an overflow check, each of these lengths wraps to 1 and the
// empty string table below appears to be a valid section.
for last_byte in [0x02, 0x04, 0x7e] {
let mut buf = minimal_bytecode();
buf.pop();
buf.push(Section::String as u8);
buf.push(0x81);
buf.extend_from_slice(&[0x80; 8]);
buf.push(last_byte);
buf.push(0); // Empty string table.
buf.push(Section::EndOfBytecode as u8);

assert!(
decode_bytecode(&buf).is_err(),
"an overflowing varint ending in {last_byte:#04x} should be rejected"
);
}
}

#[test]
fn reject_truncated_type_section() {
// Build a valid module so we get correct bytecode, then find and truncate
Expand Down