use alloc::vec::Vec;
use core::fmt;
use crate::encode::{SECTIONS, section};
use crate::ids::{ConstId, FuncId, GlobalId, ImportId, Reg, StrId, Target, TypeId};
use crate::inst::{Inst, InstError};
use crate::module::{
Callee, Const, Export, ExportItem, Function, Global, Handler, Hook, HookBinding, Import,
JumpTable, LineRow, LocalVar, Module, Strings,
};
use crate::types::{Field, FuncType, Method, StructDef, TypeDef, ValType};
use crate::{FORMAT_VERSION, MAGIC};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct Limits {
pub max_bytes: usize,
pub max_strings: usize,
pub max_types: usize,
pub max_consts: usize,
pub max_const_depth: u32,
pub max_functions: usize,
pub max_insts: usize,
pub max_total_insts: usize,
pub max_items: usize,
}
impl Default for Limits {
fn default() -> Self {
Limits {
max_bytes: 256 << 20,
max_strings: 1 << 22,
max_types: 1 << 20,
max_consts: 1 << 22,
max_const_depth: 64,
max_functions: 1 << 20,
max_insts: 1 << 24,
max_total_insts: 1 << 25,
max_items: 1 << 24,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[non_exhaustive]
pub enum Limit {
Bytes,
Strings,
Types,
Consts,
ConstDepth,
Functions,
Insts,
TotalInsts,
Items,
PerFunction,
}
impl fmt::Display for Limit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Limit::Bytes => "input size",
Limit::Strings => "string count",
Limit::Types => "type count",
Limit::Consts => "constant count",
Limit::ConstDepth => "constant nesting depth",
Limit::Functions => "function count",
Limit::Insts => "instructions per function",
Limit::TotalInsts => "total instructions",
Limit::Items => "list length",
Limit::PerFunction => "16-bit table size (65,536 entries)",
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum DecodeErrorKind {
UnexpectedEnd,
BadMagic,
UnsupportedVersion(u32),
ReservedFlags(u32),
SectionOutOfOrder {
expected: u32,
found: u32,
},
SectionLength(u32),
TrailingBytes,
LimitExceeded(Limit),
InvalidUtf8,
InvalidTag {
what: &'static str,
tag: u8,
},
InvalidChar(u32),
ConstForwardRef {
index: u32,
child: u32,
},
HookOrder,
DebugCount {
expected: u32,
found: u32,
},
Inst(InstError),
}
impl fmt::Display for DecodeErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DecodeErrorKind::UnexpectedEnd => f.write_str("unexpected end of input"),
DecodeErrorKind::BadMagic => f.write_str("not an LSB module (bad magic)"),
DecodeErrorKind::UnsupportedVersion(v) => write!(
f,
"unsupported format version {v} (this decoder reads version {FORMAT_VERSION})"
),
DecodeErrorKind::ReservedFlags(v) => write!(f, "reserved header flags set ({v:#x})"),
DecodeErrorKind::SectionOutOfOrder { expected, found } => {
write!(f, "expected section {expected}, found {found}")
}
DecodeErrorKind::SectionLength(id) => {
write!(f, "section {id} does not match its declared length")
}
DecodeErrorKind::TrailingBytes => f.write_str("bytes after the last section"),
DecodeErrorKind::LimitExceeded(limit) => write!(f, "{limit} limit exceeded"),
DecodeErrorKind::InvalidUtf8 => f.write_str("string is not valid UTF-8"),
DecodeErrorKind::InvalidTag { what, tag } => write!(f, "invalid {what} tag {tag}"),
DecodeErrorKind::InvalidChar(v) => {
write!(f, "{v:#x} is not a Unicode scalar value")
}
DecodeErrorKind::ConstForwardRef { index, child } => write!(
f,
"constant k{index} refers to k{child}, which is not an earlier constant"
),
DecodeErrorKind::HookOrder => {
f.write_str("hook bindings are not in strictly increasing order")
}
DecodeErrorKind::DebugCount { expected, found } => write!(
f,
"debug section describes {found} functions, the module has {expected}"
),
DecodeErrorKind::Inst(e) => write!(f, "{e}"),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct DecodeError {
offset: usize,
kind: DecodeErrorKind,
}
impl DecodeError {
#[must_use]
pub fn offset(&self) -> usize {
self.offset
}
#[must_use]
pub fn kind(&self) -> &DecodeErrorKind {
&self.kind
}
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "at byte {}: {}", self.offset, self.kind)
}
}
impl core::error::Error for DecodeError {}
type Result<T> = core::result::Result<T, DecodeError>;
struct Reader<'a> {
bytes: &'a [u8],
pos: usize,
end: usize,
}
impl<'a> Reader<'a> {
fn fail<T>(&self, at: usize, kind: DecodeErrorKind) -> Result<T> {
Err(DecodeError { offset: at, kind })
}
fn remaining(&self) -> usize {
self.end.saturating_sub(self.pos)
}
fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let slice = self
.pos
.checked_add(n)
.filter(|&stop| stop <= self.end)
.and_then(|stop| self.bytes.get(self.pos..stop));
match slice {
Some(s) => {
self.pos += n;
Ok(s)
}
None => self.fail(self.pos, DecodeErrorKind::UnexpectedEnd),
}
}
fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
let at = self.pos;
match <[u8; N]>::try_from(self.take(N)?) {
Ok(a) => Ok(a),
Err(_) => self.fail(at, DecodeErrorKind::UnexpectedEnd),
}
}
fn u8(&mut self) -> Result<u8> {
Ok(self.array::<1>()?[0])
}
fn u16(&mut self) -> Result<u16> {
Ok(u16::from_le_bytes(self.array()?))
}
fn u32(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(self.array()?))
}
fn u64(&mut self) -> Result<u64> {
Ok(u64::from_le_bytes(self.array()?))
}
fn count(&mut self, limit: usize, which: Limit, min_size: usize) -> Result<usize> {
let at = self.pos;
let n = self.u32()? as usize;
if n > limit {
return self.fail(at, DecodeErrorKind::LimitExceeded(which));
}
if n.saturating_mul(min_size) > self.remaining() {
return self.fail(at, DecodeErrorKind::UnexpectedEnd);
}
Ok(n)
}
fn bool(&mut self, what: &'static str) -> Result<bool> {
let at = self.pos;
match self.u8()? {
0 => Ok(false),
1 => Ok(true),
tag => self.fail(at, DecodeErrorKind::InvalidTag { what, tag }),
}
}
fn opt_u32(&mut self) -> Result<Option<u32>> {
if self.bool("option")? {
Ok(Some(self.u32()?))
} else {
Ok(None)
}
}
fn val_type(&mut self) -> Result<ValType> {
let at = self.pos;
let tag = self.u8()?;
if let Some(ty) = ValType::from_simple_tag(tag) {
return Ok(ty);
}
if tag == ValType::Ref(TypeId(0)).tag() {
return Ok(ValType::Ref(TypeId(self.u32()?)));
}
self.fail(
at,
DecodeErrorKind::InvalidTag {
what: "value type",
tag,
},
)
}
fn val_types(&mut self, limit: usize, which: Limit) -> Result<Vec<ValType>> {
let n = self.count(limit, which, 1)?;
let mut out = Vec::with_capacity(n);
for _ in 0..n {
out.push(self.val_type()?);
}
Ok(out)
}
}
const PER_FUNCTION: usize = 1 << 16;
struct Decoder<'a, 'l> {
r: Reader<'a>,
limits: &'l Limits,
total_insts: usize,
}
impl Decoder<'_, '_> {
fn items(&mut self, min_size: usize) -> Result<usize> {
self.r.count(self.limits.max_items, Limit::Items, min_size)
}
fn per_function(&mut self, min_size: usize) -> Result<usize> {
let (limit, which) = if self.limits.max_items < PER_FUNCTION {
(self.limits.max_items, Limit::Items)
} else {
(PER_FUNCTION, Limit::PerFunction)
};
self.r.count(limit, which, min_size)
}
fn strings(&mut self) -> Result<Strings> {
let n = self.r.count(self.limits.max_strings, Limit::Strings, 4)?;
let mut strings = Strings::with_capacity(n, self.r.remaining());
for _ in 0..n {
let len = self.r.u32()? as usize;
let at = self.r.pos;
let bytes = self.r.take(len)?;
let Ok(s) = core::str::from_utf8(bytes) else {
return self.r.fail(at, DecodeErrorKind::InvalidUtf8);
};
if !strings.push(s) {
return self
.r
.fail(at, DecodeErrorKind::LimitExceeded(Limit::Bytes));
}
}
Ok(strings)
}
fn type_def(&mut self) -> Result<TypeDef> {
let at = self.r.pos;
Ok(match self.r.u8()? {
0 => {
let params = self.r.val_types(self.limits.max_items, Limit::Items)?;
let results = self.r.val_types(self.limits.max_items, Limit::Items)?;
TypeDef::Func(FuncType { params, results })
}
1 => {
let name = StrId(self.r.u32()?);
let parent = self.r.opt_u32()?.map(TypeId);
let n = self.per_function(5)?;
let mut fields = Vec::with_capacity(n);
for _ in 0..n {
let name = StrId(self.r.u32()?);
let ty = self.r.val_type()?;
fields.push(Field { name, ty });
}
let n = self.items(8)?;
let mut methods = Vec::with_capacity(n);
for _ in 0..n {
let name = StrId(self.r.u32()?);
let func = FuncId(self.r.u32()?);
methods.push(Method { name, func });
}
TypeDef::Struct(StructDef {
name,
parent,
fields,
methods,
})
}
2 => TypeDef::Array(self.r.val_type()?),
3 => {
let key = self.r.val_type()?;
let value = self.r.val_type()?;
TypeDef::Map { key, value }
}
4 => TypeDef::Cell(self.r.val_type()?),
5 => {
let key = self.r.val_type()?;
let value = self.r.val_type()?;
TypeDef::Iter { key, value }
}
6 => TypeDef::Coroutine,
tag => {
return self
.r
.fail(at, DecodeErrorKind::InvalidTag { what: "type", tag });
}
})
}
fn types(&mut self) -> Result<Vec<TypeDef>> {
let n = self.r.count(self.limits.max_types, Limit::Types, 1)?;
let mut types = Vec::with_capacity(n);
for _ in 0..n {
types.push(self.type_def()?);
}
Ok(types)
}
fn consts(&mut self) -> Result<Vec<Const>> {
let n = self.r.count(self.limits.max_consts, Limit::Consts, 2)?;
let mut consts = Vec::with_capacity(n);
let mut depth: Vec<u32> = Vec::with_capacity(n);
for index in 0..n {
let at = self.r.pos;
let c = match self.r.u8()? {
0 => Const::Bool(self.r.bool("boolean")?),
1 => Const::Int(i64::from_le_bytes(self.r.array()?)),
2 => Const::UInt(self.r.u64()?),
3 => Const::F32(self.r.u32()?),
4 => Const::F64(self.r.u64()?),
5 => {
let char_at = self.r.pos;
let v = self.r.u32()?;
match char::from_u32(v) {
Some(c) => Const::Char(c),
None => return self.r.fail(char_at, DecodeErrorKind::InvalidChar(v)),
}
}
6 => Const::Str(StrId(self.r.u32()?)),
7 => {
let len = self.items(1)?;
Const::Bytes(self.r.take(len)?.to_vec())
}
8 => {
let len = self.items(4)?;
let mut items = Vec::with_capacity(len);
for _ in 0..len {
items.push(ConstId(self.r.u32()?));
}
Const::Array(items)
}
9 => {
let len = self.items(8)?;
let mut entries = Vec::with_capacity(len);
for _ in 0..len {
let k = ConstId(self.r.u32()?);
let v = ConstId(self.r.u32()?);
entries.push((k, v));
}
Const::Map(entries)
}
tag => {
return self.r.fail(
at,
DecodeErrorKind::InvalidTag {
what: "constant",
tag,
},
);
}
};
let mut d = 1u32;
for child in c.children() {
match depth.get(child.index()) {
Some(&cd) => d = d.max(cd.saturating_add(1)),
None => {
let index = u32::try_from(index).unwrap_or(u32::MAX);
let kind = DecodeErrorKind::ConstForwardRef {
index,
child: child.0,
};
return self.r.fail(at, kind);
}
}
}
if d > self.limits.max_const_depth {
return self
.r
.fail(at, DecodeErrorKind::LimitExceeded(Limit::ConstDepth));
}
depth.push(d);
consts.push(c);
}
Ok(consts)
}
fn imports(&mut self) -> Result<Vec<Import>> {
let n = self.items(12)?;
let mut imports = Vec::with_capacity(n);
for _ in 0..n {
let module = StrId(self.r.u32()?);
let name = StrId(self.r.u32()?);
let sig = TypeId(self.r.u32()?);
imports.push(Import { module, name, sig });
}
Ok(imports)
}
fn globals(&mut self) -> Result<Vec<Global>> {
let n = self.items(7)?;
let mut globals = Vec::with_capacity(n);
for _ in 0..n {
let name = StrId(self.r.u32()?);
let ty = self.r.val_type()?;
let mutable = self.r.bool("boolean")?;
let init = self.r.opt_u32()?.map(ConstId);
globals.push(Global {
name,
ty,
mutable,
init,
});
}
Ok(globals)
}
fn function(&mut self) -> Result<Function> {
let name = StrId(self.r.u32()?);
let sig = TypeId(self.r.u32()?);
let regs = {
let n = self.per_function(1)?;
let mut v = Vec::with_capacity(n);
for _ in 0..n {
v.push(self.r.val_type()?);
}
v
};
let captures = {
let n = self.per_function(1)?;
let mut v = Vec::with_capacity(n);
for _ in 0..n {
v.push(self.r.val_type()?);
}
v
};
let names = {
let n = self.per_function(4)?;
let mut v = Vec::with_capacity(n);
for _ in 0..n {
v.push(StrId(self.r.u32()?));
}
v
};
let type_refs = {
let n = self.per_function(4)?;
let mut v = Vec::with_capacity(n);
for _ in 0..n {
v.push(TypeId(self.r.u32()?));
}
v
};
let tables = {
let n = self.items(8)?;
let mut v = Vec::with_capacity(n);
for _ in 0..n {
let default = Target(self.r.u32()?);
let len = self.items(4)?;
let mut targets = Vec::with_capacity(len);
for _ in 0..len {
targets.push(Target(self.r.u32()?));
}
v.push(JumpTable { targets, default });
}
v
};
let handlers = {
let n = self.items(14)?;
let mut v = Vec::with_capacity(n);
for _ in 0..n {
let start = self.r.u32()?;
let end = self.r.u32()?;
let target = Target(self.r.u32()?);
let catch = Reg(self.r.u16()?);
v.push(Handler {
start,
end,
target,
catch,
});
}
v
};
let code = {
let at = self.r.pos;
let n = self.r.count(self.limits.max_insts, Limit::Insts, 8)?;
self.total_insts = self.total_insts.saturating_add(n);
if self.total_insts > self.limits.max_total_insts {
return self
.r
.fail(at, DecodeErrorKind::LimitExceeded(Limit::TotalInsts));
}
let start = self.r.pos;
let words = self.r.take(n.saturating_mul(8))?;
let mut code = Vec::with_capacity(n);
for (i, word) in words.chunks_exact(8).enumerate() {
let mut bytes = [0u8; 8];
bytes.copy_from_slice(word);
match Inst::from_bytes(bytes) {
Ok(inst) => code.push(inst),
Err(e) => return self.r.fail(start + i * 8, DecodeErrorKind::Inst(e)),
}
}
code
};
Ok(Function {
name,
sig,
regs,
captures,
names,
type_refs,
tables,
handlers,
code,
lines: Vec::new(),
locals: Vec::new(),
})
}
fn functions(&mut self) -> Result<Vec<Function>> {
let n = self
.r
.count(self.limits.max_functions, Limit::Functions, 36)?;
let mut functions = Vec::with_capacity(n);
for _ in 0..n {
functions.push(self.function()?);
}
Ok(functions)
}
fn exports(&mut self) -> Result<Vec<Export>> {
let n = self.items(9)?;
let mut exports = Vec::with_capacity(n);
for _ in 0..n {
let name = StrId(self.r.u32()?);
let at = self.r.pos;
let tag = self.r.u8()?;
let index = self.r.u32()?;
let item = match tag {
0 => ExportItem::Func(FuncId(index)),
1 => ExportItem::Global(GlobalId(index)),
2 => ExportItem::Type(TypeId(index)),
tag => {
return self.r.fail(
at,
DecodeErrorKind::InvalidTag {
what: "export",
tag,
},
);
}
};
exports.push(Export { name, item });
}
Ok(exports)
}
fn hooks(&mut self) -> Result<Vec<HookBinding>> {
let n = self.r.count(Hook::ALL.len(), Limit::Items, 6)?;
let mut hooks: Vec<HookBinding> = Vec::with_capacity(n);
for _ in 0..n {
let at = self.r.pos;
let code = self.r.u8()?;
let Some(hook) = Hook::from_code(code) else {
return self.r.fail(
at,
DecodeErrorKind::InvalidTag {
what: "hook",
tag: code,
},
);
};
if hooks.last().is_some_and(|prev| prev.hook >= hook) {
return self.r.fail(at, DecodeErrorKind::HookOrder);
}
let callee_at = self.r.pos;
let tag = self.r.u8()?;
let index = self.r.u32()?;
let callee = match tag {
0 => Callee::Func(FuncId(index)),
1 => Callee::Import(ImportId(index)),
tag => {
return self.r.fail(
callee_at,
DecodeErrorKind::InvalidTag {
what: "callee",
tag,
},
);
}
};
hooks.push(HookBinding { hook, callee });
}
Ok(hooks)
}
fn debug(&mut self, functions: &mut [Function]) -> Result<()> {
let at = self.r.pos;
let n = self.r.u32()?;
if n as usize != functions.len() {
let expected = u32::try_from(functions.len()).unwrap_or(u32::MAX);
return self
.r
.fail(at, DecodeErrorKind::DebugCount { expected, found: n });
}
for f in functions {
let rows = self.items(16)?;
f.lines.reserve_exact(rows);
for _ in 0..rows {
let pc = self.r.u32()?;
let file = StrId(self.r.u32()?);
let line = self.r.u32()?;
let column = self.r.u32()?;
f.lines.push(LineRow {
pc,
file,
line,
column,
});
}
let locals = self.items(14)?;
f.locals.reserve_exact(locals);
for _ in 0..locals {
let reg = Reg(self.r.u16()?);
let name = StrId(self.r.u32()?);
let start = self.r.u32()?;
let end = self.r.u32()?;
f.locals.push(LocalVar {
reg,
name,
start,
end,
});
}
}
Ok(())
}
fn open(&mut self, id: u32) -> Result<usize> {
let at = self.r.pos;
let found = self.r.u32()?;
if found != id {
return self.r.fail(
at,
DecodeErrorKind::SectionOutOfOrder {
expected: id,
found,
},
);
}
let len = self.r.u32()? as usize;
let start = self.r.pos;
match start.checked_add(len).filter(|&end| end <= self.r.end) {
Some(end) => {
let outer = self.r.end;
self.r.end = end;
Ok(outer)
}
None => self.r.fail(start, DecodeErrorKind::UnexpectedEnd),
}
}
fn close(&mut self, id: u32, outer: usize) -> Result<()> {
if self.r.pos != self.r.end {
return self.r.fail(self.r.pos, DecodeErrorKind::SectionLength(id));
}
self.r.end = outer;
Ok(())
}
}
pub(crate) fn decode(bytes: &[u8], limits: &Limits) -> Result<Module> {
if bytes.len() > limits.max_bytes {
return Err(DecodeError {
offset: 0,
kind: DecodeErrorKind::LimitExceeded(Limit::Bytes),
});
}
let mut d = Decoder {
r: Reader {
bytes,
pos: 0,
end: bytes.len(),
},
limits,
total_insts: 0,
};
let magic: [u8; 4] = d.r.array()?;
if magic != MAGIC {
return d.r.fail(0, DecodeErrorKind::BadMagic);
}
let version = d.r.u32()?;
if version != FORMAT_VERSION {
return d.r.fail(4, DecodeErrorKind::UnsupportedVersion(version));
}
let flags = d.r.u32()?;
if flags != 0 {
return d.r.fail(8, DecodeErrorKind::ReservedFlags(flags));
}
let mut module = Module::default();
for id in (1u32..).take(SECTIONS) {
let outer = d.open(id)?;
match id {
section::STRINGS => module.strings = d.strings()?,
section::TYPES => module.types = d.types()?,
section::CONSTS => module.consts = d.consts()?,
section::IMPORTS => module.imports = d.imports()?,
section::GLOBALS => module.globals = d.globals()?,
section::FUNCTIONS => module.functions = d.functions()?,
section::EXPORTS => module.exports = d.exports()?,
section::HOOKS => module.hooks = d.hooks()?,
section::META => {
module.name = d.r.opt_u32()?.map(StrId);
module.start = d.r.opt_u32()?.map(FuncId);
}
_ => d.debug(&mut module.functions)?,
}
d.close(id, outer)?;
}
if d.r.pos != bytes.len() {
return d.r.fail(d.r.pos, DecodeErrorKind::TrailingBytes);
}
Ok(module)
}