1use super::chunk::{Chunk, FunctionDef, ABI_VERSION, MAGIC};
2use super::instruction::Instruction;
3use super::opcode::Opcode;
4use super::value::Value;
5use super::verify::TrustLevel;
6
7const MAX_NAME: u32 = 64 * 1024;
8const MAX_ITEMS: u32 = 1_000_000;
9const MAX_BLOB: u32 = 1_048_576;
11const MAX_VALUE_DEPTH: u32 = 16;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum FormatError {
17 BadMagic { found: [u8; 4] },
18 UnsupportedAbi(u32),
19 Truncated,
20 LimitExceeded { what: &'static str, got: u32 },
21 BadUtf8,
22 UnknownValueTag(u8),
23 UnknownOpcode { at: usize, byte: u8 },
24 ForbiddenConstant(super::verify::ConstantKind),
25 ValueTooNested,
26}
27
28impl std::fmt::Display for FormatError {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 match self {
31 FormatError::BadMagic { found } => {
32 write!(f, "bad magic {found:?}, expected {:?}", MAGIC)
33 }
34 FormatError::UnsupportedAbi(v) => write!(f, "unsupported ABI version {v}"),
35 FormatError::Truncated => write!(f, "truncated .bf module"),
36 FormatError::LimitExceeded { what, got } => {
37 write!(f, "{what} count {got} exceeds decoder limit")
38 }
39 FormatError::BadUtf8 => write!(f, "name is not valid UTF-8"),
40 FormatError::UnknownValueTag(t) => write!(f, "unknown value tag 0x{t:02X}"),
41 FormatError::UnknownOpcode { at, byte } => {
42 write!(f, "unknown opcode 0x{byte:02X} at instruction {at}")
43 }
44 FormatError::ForbiddenConstant(kind) => {
45 write!(f, "untrusted module must not embed {kind} in the constant pool")
46 }
47 FormatError::ValueTooNested => write!(f, "value nesting exceeds decoder limit"),
48 }
49 }
50}
51
52impl std::error::Error for FormatError {}
53
54pub fn encode(chunk: &Chunk) -> Vec<u8> {
56 let mut out = Vec::new();
57 out.extend_from_slice(&MAGIC);
58 out.extend_from_slice(&ABI_VERSION.to_le_bytes());
59 write_string(&mut out, &chunk.name);
60 out.extend_from_slice(&(chunk.constants.len() as u32).to_le_bytes());
61 for value in &chunk.constants {
62 write_value(&mut out, value);
63 }
64 out.extend_from_slice(&(chunk.functions.len() as u32).to_le_bytes());
65 for def in &chunk.functions {
66 write_string(&mut out, &def.name);
67 out.extend_from_slice(&def.entry.to_le_bytes());
68 out.push(def.arity);
69 out.push(def.num_registers);
70 }
71 out.extend_from_slice(&(chunk.code.len() as u32).to_le_bytes());
72 for instr in &chunk.code {
73 out.push(instr.op.as_u8());
74 out.push(instr.a);
75 out.push(instr.b);
76 out.push(instr.c);
77 out.extend_from_slice(&instr.imm.to_le_bytes());
78 }
79 out
80}
81
82pub fn decode(bytes: &[u8]) -> Result<Chunk, FormatError> {
85 decode_with(bytes, TrustLevel::Untrusted)
86}
87
88pub fn decode_with(bytes: &[u8], trust: TrustLevel) -> Result<Chunk, FormatError> {
91 let mut r = Reader { data: bytes, pos: 0 };
92 let magic = r.read_array::<4>()?;
93 if magic != MAGIC {
94 return Err(FormatError::BadMagic { found: magic });
95 }
96 let abi = r.read_u32()?;
97 if abi != ABI_VERSION {
98 return Err(FormatError::UnsupportedAbi(abi));
99 }
100 let name = r.read_string()?;
101 let n_const = r.read_count("constants")?;
102 let mut constants = Vec::with_capacity(n_const as usize);
103 for _ in 0..n_const {
104 constants.push(r.read_value(trust, 0)?);
105 }
106 let n_fn = r.read_count("functions")?;
107 let mut functions = Vec::with_capacity(n_fn as usize);
108 for _ in 0..n_fn {
109 functions.push(FunctionDef {
110 name: r.read_string()?,
111 entry: r.read_u32()?,
112 arity: r.read_u8()?,
113 num_registers: r.read_u8()?,
114 });
115 }
116 let n_code = r.read_count("code")?;
117 let mut code = Vec::with_capacity(n_code as usize);
118 for at in 0..n_code as usize {
119 let byte = r.read_u8()?;
120 let op = Opcode::from_u8(byte).ok_or(FormatError::UnknownOpcode { at, byte })?;
121 code.push(Instruction {
122 op,
123 a: r.read_u8()?,
124 b: r.read_u8()?,
125 c: r.read_u8()?,
126 imm: r.read_i32()?,
127 });
128 }
129 Ok(Chunk {
130 name,
131 constants,
132 code,
133 functions,
134 })
135}
136
137fn write_string(out: &mut Vec<u8>, s: &str) {
138 let bytes = s.as_bytes();
139 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
140 out.extend_from_slice(bytes);
141}
142
143fn write_value(out: &mut Vec<u8>, value: &Value) {
144 match value {
145 Value::Unit => out.push(0),
146 Value::Bool(b) => {
147 out.push(1);
148 out.push(u8::from(*b));
149 }
150 Value::Int(i) => {
151 out.push(2);
152 out.extend_from_slice(&i.to_le_bytes());
153 }
154 Value::Float(x) => {
155 out.push(3);
156 out.extend_from_slice(&x.to_le_bytes());
157 }
158 Value::Pid(p) => {
159 out.push(4);
160 out.extend_from_slice(&p.to_le_bytes());
161 }
162 Value::Message(m) => {
163 out.push(5);
165 out.extend_from_slice(&m.sender.to_le_bytes());
166 out.extend_from_slice(&m.reply_cap.as_u128().to_le_bytes());
167 out.extend_from_slice(&m.request_id.to_le_bytes());
168 out.extend_from_slice(&m.tag.to_le_bytes());
169 write_value(out, m.payload.as_ref());
170 }
171 Value::Cap(c) => {
172 out.push(6);
173 out.extend_from_slice(&c.as_u128().to_le_bytes());
174 }
175 Value::Str(s) => {
176 out.push(7);
177 write_blob(out, s.as_bytes());
178 }
179 Value::Bytes(b) => {
180 out.push(8);
181 write_blob(out, b);
182 }
183 }
184}
185
186fn write_blob(out: &mut Vec<u8>, bytes: &[u8]) {
187 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
188 out.extend_from_slice(bytes);
189}
190
191struct Reader<'a> {
192 data: &'a [u8],
193 pos: usize,
194}
195
196impl<'a> Reader<'a> {
197 fn rest(&self) -> usize {
198 self.data.len().saturating_sub(self.pos)
199 }
200
201 fn take(&mut self, n: usize) -> Result<&'a [u8], FormatError> {
202 if self.rest() < n {
203 return Err(FormatError::Truncated);
204 }
205 let slice = &self.data[self.pos..self.pos + n];
206 self.pos += n;
207 Ok(slice)
208 }
209
210 fn read_u8(&mut self) -> Result<u8, FormatError> {
211 Ok(self.take(1)?[0])
212 }
213
214 fn read_array<const N: usize>(&mut self) -> Result<[u8; N], FormatError> {
215 let slice = self.take(N)?;
216 let mut arr = [0u8; N];
217 arr.copy_from_slice(slice);
218 Ok(arr)
219 }
220
221 fn read_u32(&mut self) -> Result<u32, FormatError> {
222 Ok(u32::from_le_bytes(self.read_array()?))
223 }
224
225 fn read_u16(&mut self) -> Result<u16, FormatError> {
226 Ok(u16::from_le_bytes(self.read_array()?))
227 }
228
229 fn read_i32(&mut self) -> Result<i32, FormatError> {
230 Ok(i32::from_le_bytes(self.read_array()?))
231 }
232
233 fn read_i64(&mut self) -> Result<i64, FormatError> {
234 Ok(i64::from_le_bytes(self.read_array()?))
235 }
236
237 fn read_u64(&mut self) -> Result<u64, FormatError> {
238 Ok(u64::from_le_bytes(self.read_array()?))
239 }
240
241 fn read_u128(&mut self) -> Result<u128, FormatError> {
242 Ok(u128::from_le_bytes(self.read_array()?))
243 }
244
245 fn read_f64(&mut self) -> Result<f64, FormatError> {
246 Ok(f64::from_le_bytes(self.read_array()?))
247 }
248
249 fn read_count(&mut self, what: &'static str) -> Result<u32, FormatError> {
250 let n = self.read_u32()?;
251 if n > MAX_ITEMS {
252 return Err(FormatError::LimitExceeded { what, got: n });
253 }
254 Ok(n)
255 }
256
257 fn read_string(&mut self) -> Result<String, FormatError> {
258 let len = self.read_u32()?;
259 if len > MAX_NAME {
260 return Err(FormatError::LimitExceeded {
261 what: "name",
262 got: len,
263 });
264 }
265 let bytes = self.take(len as usize)?;
266 String::from_utf8(bytes.to_vec()).map_err(|_| FormatError::BadUtf8)
267 }
268
269 fn read_value(&mut self, trust: TrustLevel, depth: u32) -> Result<Value, FormatError> {
270 if depth > MAX_VALUE_DEPTH {
271 return Err(FormatError::ValueTooNested);
272 }
273 match self.read_u8()? {
274 0 => Ok(Value::Unit),
275 1 => Ok(Value::Bool(self.read_u8()? != 0)),
276 2 => Ok(Value::Int(self.read_i64()?)),
277 3 => Ok(Value::Float(self.read_f64()?)),
278 4 => {
279 if trust == TrustLevel::Untrusted {
280 return Err(FormatError::ForbiddenConstant(
281 super::verify::ConstantKind::ProcessId,
282 ));
283 }
284 Ok(Value::Pid(self.read_u64()?))
285 }
286 5 => {
287 if trust == TrustLevel::Untrusted {
288 return Err(FormatError::ForbiddenConstant(
289 super::verify::ConstantKind::Message,
290 ));
291 }
292 let sender = self.read_u64()?;
293 let reply_cap = super::cap::CapId::from_raw(self.read_u128()?);
294 let request_id = self.read_u64()?;
295 let tag = self.read_u16()?;
296 let payload = self.read_value(trust, depth + 1)?;
297 Ok(Value::Message(
298 super::value::Message::new(sender, request_id, tag, payload)
299 .authenticate(sender, reply_cap),
300 ))
301 }
302 6 => {
303 if trust == TrustLevel::Untrusted {
304 return Err(FormatError::ForbiddenConstant(
305 super::verify::ConstantKind::Capability,
306 ));
307 }
308 Ok(Value::Cap(super::cap::CapId::from_raw(self.read_u128()?)))
309 }
310 7 => {
311 let bytes = self.read_blob("str")?;
312 let s = std::str::from_utf8(bytes).map_err(|_| FormatError::BadUtf8)?;
313 Ok(Value::str(s))
314 }
315 8 => Ok(Value::bytes(self.read_blob("bytes")?)),
316 tag => Err(FormatError::UnknownValueTag(tag)),
317 }
318 }
319
320 fn read_blob(&mut self, what: &'static str) -> Result<&'a [u8], FormatError> {
321 let len = self.read_u32()?;
322 if len > MAX_BLOB {
323 return Err(FormatError::LimitExceeded { what, got: len });
324 }
325 self.take(len as usize)
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use crate::bytecode::builder::ChunkBuilder;
333 use crate::bytecode::opcode::Opcode;
334 use crate::bytecode::verify::{verify, TrustLevel};
335
336
337 #[test]
338 fn roundtrip_preserves_chunk() -> Result<(), FormatError> {
339 let mut b = ChunkBuilder::new("roundtrip");
340 b.begin_function("main", 0, 2);
341 let k = b.const_(Value::Int(9));
342 b.emit_load_const(0, k);
343 b.emit_load_imm(1, 1);
344 b.emit_binop(Opcode::Add, 0, 0, 1);
345 b.emit_return(0);
346 let original = b.finish();
347 let bytes = encode(&original);
348 assert_eq!(&bytes[..4], &MAGIC);
349 let decoded = decode(&bytes)?;
350 assert!(verify(&decoded).is_ok());
351 assert_eq!(decoded.name, original.name);
352 assert_eq!(decoded.constants, original.constants);
353 assert_eq!(decoded.code, original.code);
354 assert_eq!(decoded.functions, original.functions);
355 Ok(())
356 }
357
358 #[test]
359 fn roundtrip_preserves_message_constant() -> Result<(), FormatError> {
360 use super::super::value::Message;
361 let mut b = ChunkBuilder::new("msg-const");
362 b.begin_function("main", 0, 1);
363 let k = b.const_(Value::Message(Message::new(1, 2, 3, 4u64)));
364 b.emit_load_const(0, k);
365 b.emit_return(0);
366 let original = b.finish();
367 let decoded = decode_with(&encode(&original), TrustLevel::Trusted)?;
368 assert_eq!(decoded.constants, original.constants);
369 let got = decoded.constants[0]
370 .as_message()
371 .ok_or(FormatError::Truncated)?;
372 assert_eq!(got.sender, 1);
373 assert_eq!(got.request_id, 2);
374 assert_eq!(got.tag, 3);
375 assert_eq!(got.payload.as_ref(), &Value::Int(4));
376 Ok(())
377 }
378
379 #[test]
380 fn untrusted_decode_rejects_cap_constant() {
381 use crate::bytecode::cap::CapId;
382 let mut b = ChunkBuilder::new("cap-const");
383 b.begin_function("main", 0, 1);
384 let k = b.const_(Value::Cap(CapId::from_raw(1)));
385 b.emit_load_const(0, k);
386 b.emit_return(0);
387 let bytes = encode(&b.finish());
388 assert!(matches!(
389 decode(&bytes),
390 Err(FormatError::ForbiddenConstant(_))
391 ));
392 assert!(decode_with(&bytes, TrustLevel::Trusted).is_ok());
393 }
394
395 #[test]
396 fn roundtrip_preserves_str_and_bytes_constants() -> Result<(), FormatError> {
397 let mut b = ChunkBuilder::new("blob-const");
398 b.begin_function("main", 0, 2);
399 let ks = b.const_(Value::str("olá"));
400 let kb = b.const_(Value::bytes([0u8, 255, 7]));
401 b.emit_load_const(0, ks);
402 b.emit_load_const(1, kb);
403 b.emit_return(0);
404 let original = b.finish();
405 let decoded = decode(&encode(&original))?;
406 assert_eq!(decoded.constants, original.constants);
407 assert_eq!(decoded.constants[0].as_str(), Some("olá"));
408 assert_eq!(decoded.constants[1].as_bytes(), Some(&[0, 255, 7][..]));
409 Ok(())
410 }
411
412 #[test]
413 fn rejects_bad_magic() {
414 assert!(matches!(
415 decode(b"XXXX"),
416 Err(FormatError::BadMagic { .. })
417 ));
418 }
419}