1use std::{io, marker::PhantomData, ops::Range};
2
3use serde::{Deserialize, Serialize};
4
5use super::{
6 Arg, Certificate, Error, Phase, Result, limit,
7 verify::{Context, Verifier},
8};
9
10use dolang_util::verified::Verified;
11
12const MAGIC: [u8; 8] = *b"\xffdobytec";
13const VERSION: [u8; 3] = [0, 0, 1];
14
15#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
16struct Header {
17 magic: [u8; 8],
18 version: [u8; 3],
19}
20
21#[derive(Serialize, Deserialize, Debug, PartialEq)]
22pub struct Content<'a> {
23 #[serde(borrow)]
24 pub bintab: BinTable<'a>,
25 pub symtab: SymTable,
26 pub consttab: ConstTable,
27 pub packtab: PackTable,
28 pub unpacktab: UnpackTable,
29 #[serde(borrow)]
30 pub functab: FuncTable<'a>,
31 #[serde(borrow)]
32 pub debugbintab: BinTable<'a>,
33 pub funcdebugtab: FuncDebugTable,
34 #[serde(default)]
35 pub module_name: Option<StrId>,
36}
37
38#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
39pub struct BinTable<'a> {
40 pub content: &'a [u8],
41}
42
43pub type StrId = Range<usize>;
44pub type BinId = Range<usize>;
45
46#[derive(Serialize, Deserialize, Debug, PartialEq)]
47pub enum Const {
48 Nil,
49 I64(i64),
50 VerbatimI64(i64, StrId),
51 F64(f64),
52 VerbatimF64(f64, StrId),
53 Bool(bool),
54 Str(StrId),
55 Sym(usize),
56 Bin(BinId),
57}
58
59#[derive(Serialize, Deserialize, Debug, PartialEq)]
60pub struct ConstTable {
61 pub content: Vec<Const>,
62}
63
64#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
65pub struct SymEntry {
66 pub name: StrId,
67 pub private: bool,
68}
69
70#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
71pub struct SymTable {
72 pub content: Vec<SymEntry>,
73}
74
75#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
76pub struct PackTable {
77 pub content: Vec<Vec<Arg>>,
78}
79
80#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
81pub enum UnpackKeyKind {
82 Sym(usize),
84 Const(usize),
86}
87
88#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
89pub struct UnpackKey {
90 pub kind: UnpackKeyKind,
92 pub default: Option<usize>,
94}
95
96#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
97pub struct UnpackSig {
98 pub required: usize,
100 pub optional: Vec<usize>,
102 pub keys: Vec<UnpackKey>,
104 pub variadic: super::Variadic,
106}
107
108#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
109pub struct UnpackTable {
110 pub content: Vec<UnpackSig>,
111}
112
113#[derive(Debug, PartialEq, Eq)]
114pub struct Serde<'a>(PhantomData<&'a ()>);
115
116impl<'a> Phase for Serde<'a> {
117 type Bytes = &'a [u8];
118}
119
120pub type Func<'a> = super::Func<Serde<'a>>;
121
122#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
123pub struct FuncEntry<'a> {
124 #[serde(borrow)]
125 pub func: Func<'a>,
126 pub cert: Certificate,
127}
128
129#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
130pub struct FuncTable<'a> {
131 #[serde(borrow)]
132 pub content: Vec<FuncEntry<'a>>,
133}
134
135#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
136pub struct SourceLine {
137 pub offset_delta: usize,
138 pub line_delta: i32,
139 pub file: StrId,
140}
141
142#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
143pub struct FuncDebug {
144 pub name: StrId,
145 pub sourcemap: Vec<SourceLine>,
146}
147
148#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
149pub struct FuncDebugTable {
150 pub content: Vec<FuncDebug>,
151}
152
153pub fn deserialize(buffer: &[u8]) -> Result<Verified<Content<'_>>> {
154 deserialize_raw(buffer).and_then(verify)
155}
156
157fn deserialize_raw(buffer: &[u8]) -> Result<Content<'_>> {
158 if buffer.len() > limit::BYTECODE_FILE_SIZE {
159 return Err(Error::FileSizeLimit);
160 }
161
162 const HEADER: Header = Header {
163 magic: MAGIC,
164 version: VERSION,
165 };
166 let (header, read): (Header, _) = postcard::take_from_bytes(buffer)?;
167 if header != HEADER {
168 return Err(Error::InvalidHeader);
169 }
170
171 let (content, read): (Content, _) = postcard::take_from_bytes(read)?;
174 if !read.is_empty() {
175 return Err(Error::TrailingJunk(
176 read.as_ptr().addr() - buffer.as_ptr().addr(),
177 ));
178 }
179
180 Ok(content)
181}
182
183pub fn serialize<W: io::Write>(content: &Content, w: &mut W) -> io::Result<()> {
184 const HEADER: Header = Header {
185 magic: MAGIC,
186 version: VERSION,
187 };
188 let w = postcard::to_io(&HEADER, w).map_err(io::Error::other)?;
189 postcard::to_io(&content, w).map_err(io::Error::other)?;
190 Ok(())
191}
192
193impl<'c> Context for Content<'c> {
194 type Phase = Serde<'c>;
195
196 fn slice<'a>(&'a self, bytes: &'a <Self::Phase as Phase>::Bytes) -> &'a [u8] {
197 bytes
198 }
199
200 fn function(&self, index: usize) -> Option<&Func<'c>> {
201 self.functab.content.get(index).map(|e| &e.func)
202 }
203
204 fn pack(&self, index: usize) -> Option<impl Iterator<Item = Arg>> {
205 self.packtab.content.get(index).map(|v| v.iter().cloned())
206 }
207
208 fn unpack_arity(&self, index: usize) -> Option<usize> {
209 self.unpacktab.content.get(index).map(|s| {
210 s.required
211 .strict_add(s.optional.len())
212 .strict_add(s.keys.len())
213 .strict_add(match s.variadic {
214 super::Variadic::None | super::Variadic::Discard => 0,
215 super::Variadic::Capture => 1,
216 })
217 })
218 }
219
220 fn symbol_valid(&self, index: usize) -> bool {
221 self.symtab.content.get(index).is_some()
222 }
223
224 fn constant_valid(&self, index: usize) -> bool {
225 self.consttab.content.get(index).is_some()
226 }
227}
228
229fn invalid_str(s: &StrId, tab: &[u8]) -> bool {
230 s.start > s.end
231 || s.end > tab.len()
232 || s.end - s.start >= limit::STRING_LENGTH
233 || std::str::from_utf8(&tab[s.start..s.end]).is_err()
234}
235
236fn invalid_bin(s: &BinId, tab: &[u8]) -> bool {
237 s.start > s.end || s.end > tab.len() || s.end - s.start >= limit::STRING_LENGTH
238}
239
240fn verify(content: Content) -> Result<Verified<Content>> {
241 verify_bintab(&content.bintab)?;
242 verify_debugbintab(&content.debugbintab)?;
243 verify_module_name(content.module_name.as_ref(), content.debugbintab.content)?;
244 let symtab_len = verify_symtab(&content.symtab, content.bintab.content)?;
245 verify_unpacktab(
246 &content.unpacktab,
247 symtab_len,
248 content.consttab.content.len(),
249 )?;
250 verify_consttab(&content.consttab, content.bintab.content, symtab_len)?;
251 verify_packtab(&content.packtab, symtab_len)?;
252 verify_functab(&content.functab, &content.unpacktab)?;
253 verify_funcdebugtab(
254 &content.funcdebugtab,
255 &content.functab,
256 content.debugbintab.content,
257 )?;
258
259 let verifier = Verifier::new(&content);
260 verifier.check(content.functab.content.iter().map(|e| (&e.func, &e.cert)))?;
261
262 Ok(unsafe { Verified::new(content) })
264}
265
266fn verify_debugbintab(debugbintab: &BinTable) -> Result<()> {
267 let debugbintab_len = debugbintab.content.len();
268 if debugbintab_len > limit::DEBUG_BIN_TAB_SIZE {
269 return Err(Error::DebugBinTabLimit);
270 }
271 std::str::from_utf8(debugbintab.content).map_err(|_| Error::InvalidUtf8InDebugBinTab)?;
272 Ok(())
273}
274
275fn verify_functab(functab: &FuncTable, unpacktab: &UnpackTable) -> Result<()> {
277 if functab.content.is_empty() {
278 return Err(Error::FuncTabEmpty);
279 }
280
281 if functab.content.len() > limit::FUNC_TAB_ENTRIES {
282 return Err(Error::FuncTabLimit);
283 }
284
285 for (i, func) in functab.content.iter().enumerate() {
286 if func.func.sig >= unpacktab.content.len() {
288 return Err(Error::InvalidUnpackInFuncTab(i));
289 }
290
291 let sig = &unpacktab.content[func.func.sig];
293 for (j, key) in sig.keys.iter().enumerate() {
294 if matches!(key.kind, UnpackKeyKind::Const(_)) {
295 return Err(Error::ConstKeyInFunctionParam(func.func.sig, j));
296 }
297 }
298 let bytecode_len = func.func.bytecode.len();
300 if bytecode_len == 0 {
301 return Err(Error::EmptyCode(i));
302 }
303 if bytecode_len > limit::FUNC_SIZE {
304 return Err(Error::CodeLimit(i));
305 }
306 let cert_len = func.cert.blocks.len();
307 if cert_len > limit::CERT_ENTRIES {
308 return Err(Error::CertLimit(i));
309 }
310 if func.cert.max_operand_depth.saturating_add(func.func.locals) > limit::FUNC_FRAME_SLOTS {
312 return Err(Error::StackSlotLimit(i));
313 }
314 }
317
318 Ok(())
319}
320
321fn verify_bintab(bintab: &BinTable) -> Result<()> {
322 let bintab_len = bintab.content.len();
323 if bintab_len > limit::BIN_TAB_SIZE {
324 return Err(Error::BinTabLimit);
325 }
326 Ok(())
327}
328
329fn verify_symtab(symtab: &SymTable, bintab: &[u8]) -> Result<usize> {
330 let symtab_len = symtab.content.len();
331 if symtab_len > limit::SYMBOL_TAB_ENTRIES {
332 return Err(Error::SymTabLimit);
333 }
334 for (i, s) in symtab.content.iter().enumerate() {
335 if invalid_str(&s.name, bintab) {
336 return Err(Error::InvalidStrInSymTab(i));
337 }
338 }
339 Ok(symtab_len)
340}
341
342fn verify_module_name(module_name: Option<&StrId>, bintab: &[u8]) -> Result<()> {
343 if let Some(name) = module_name
344 && invalid_str(name, bintab)
345 {
346 return Err(Error::InvalidModuleName);
347 }
348 Ok(())
349}
350
351fn verify_unpacktab(
352 unpacktab: &UnpackTable,
353 symtab_len: usize,
354 consttab_len: usize,
355) -> Result<usize> {
356 let unpacktab_len = unpacktab.content.len();
357
358 if unpacktab_len > limit::UNPACK_TAB_ENTRIES {
359 return Err(Error::UnpackTabLimit);
360 }
361
362 for (i, u) in unpacktab.content.iter().enumerate() {
363 if u.required
364 .saturating_add(u.optional.len())
365 .saturating_add(u.keys.len())
366 > limit::UNPACK_ENTRIES
367 {
368 return Err(Error::UnpackLimit(i));
369 }
370
371 for (j, UnpackKey { kind, default }) in u.keys.iter().enumerate() {
372 match kind {
373 UnpackKeyKind::Sym(sym) => {
374 if *sym >= symtab_len {
375 return Err(Error::InvalidSymInUnpackTab(i, j));
376 }
377 }
378 UnpackKeyKind::Const(c) => {
379 if *c >= consttab_len {
380 return Err(Error::InvalidConstInUnpackTab(i, j));
381 }
382 }
383 }
384 if let Some(default) = default
385 && *default >= consttab_len
386 {
387 return Err(Error::InvalidConstInUnpackTab(i, j));
388 }
389 }
390 }
391 Ok(unpacktab_len)
392}
393
394fn verify_packtab(packtab: &PackTable, symtab_len: usize) -> Result<()> {
395 if packtab.content.len() > limit::PACK_TAB_ENTRIES {
396 return Err(Error::PackTabLimit);
397 }
398
399 for (i, p) in packtab.content.iter().enumerate() {
400 if p.len() > limit::PACK_ENTRIES {
401 return Err(Error::PackLimit(i));
402 }
403
404 for (j, arg) in p.iter().enumerate() {
405 if let Arg::Key(idx) = arg
406 && *idx >= symtab_len
407 {
408 return Err(Error::InvalidSymInPackTab(i, j));
409 }
410 }
411 }
412 Ok(())
413}
414
415fn verify_funcdebugtab(
416 funcdebugtab: &FuncDebugTable,
417 functab: &FuncTable,
418 debugbintab: &[u8],
419) -> Result<()> {
420 if !funcdebugtab.content.is_empty() && funcdebugtab.content.len() != functab.content.len() {
421 return Err(Error::FuncDebugTabWrongSize);
422 }
423
424 for (i, c) in funcdebugtab.content.iter().enumerate() {
425 if invalid_str(&c.name, debugbintab) {
426 return Err(Error::InvalidStrInFuncDebugTab(i));
427 }
428 if c.sourcemap.is_empty() {
429 return Err(Error::SourceMapEmpty(i));
430 }
431 if c.sourcemap.len() > limit::SOURCE_MAP_ENTRIES {
432 return Err(Error::SourceMapLimit(i));
433 }
434 let bytecode_len = functab.content[i].func.bytecode.len();
435 let mut offset = 0usize;
436 let mut line = 0u32;
437 for (j, m) in c.sourcemap.iter().enumerate() {
438 if j != 0 {
439 offset = offset
440 .checked_add(m.offset_delta)
441 .and_then(|o| o.checked_add(1))
442 .ok_or_else(|| Error::SourceMapOffsetBounds(i, j))?;
443 if offset >= bytecode_len {
444 return Err(Error::SourceMapOffsetBounds(i, j));
445 }
446
447 if m.line_delta == 0 {
448 return Err(Error::SourceMapLineDeltaZero(i, j));
449 }
450 }
451 line = line
452 .checked_add_signed(m.line_delta)
453 .ok_or_else(|| Error::SourceMapLineBounds(i, j))?;
454 if invalid_str(&m.file, debugbintab) {
455 return Err(Error::InvalidStrInSourceMap(i, j));
456 }
457 }
458 }
459 Ok(())
460}
461
462fn verify_consttab(consttab: &ConstTable, bintab: &[u8], symtab_len: usize) -> Result<()> {
463 if consttab.content.len() > limit::CONST_TAB_ENTRIES {
464 return Err(Error::ConstTabLimit);
465 }
466
467 for (i, c) in consttab.content.iter().enumerate() {
468 if let Const::Str(s) = c
469 && invalid_str(s, bintab)
470 {
471 return Err(Error::InvalidStrInConstTab(i));
472 } else if let Const::Bin(b) = c
473 && invalid_bin(b, bintab)
474 {
475 return Err(Error::InvalidBinInConstTab(i));
476 } else if let Const::Sym(idx) = c
477 && *idx >= symtab_len
478 {
479 return Err(Error::InvalidSymInConstTab(i));
480 }
481 }
482
483 Ok(())
484}
485
486#[cfg(test)]
487mod test {
488 use super::*;
489 use crate::{Certificate, Encode, Inst, Variadic, verify::Verifier};
490
491 fn encode_raw(insts: &[Inst]) -> Vec<u8> {
492 let mut bytecode = Vec::new();
493 for inst in insts {
494 inst.encode(&mut bytecode).unwrap();
495 }
496 bytecode
497 }
498
499 fn empty_sig() -> UnpackSig {
500 UnpackSig {
501 required: 0,
502 optional: vec![],
503 keys: vec![],
504 variadic: Variadic::None,
505 }
506 }
507
508 fn valid_content_with<'a>(
509 bintab: &'a [u8],
510 bytecode: &'a [u8],
511 debugbintab: &'a [u8],
512 ) -> Content<'a> {
513 let mut content = Content {
514 bintab: BinTable { content: bintab },
515 symtab: SymTable {
516 content: vec![SymEntry {
517 name: 0..7,
518 private: false,
519 }],
520 },
521 consttab: ConstTable {
522 content: vec![Const::Str(7..12)],
523 },
524 packtab: PackTable {
525 content: vec![vec![]],
526 },
527 unpacktab: UnpackTable {
528 content: vec![empty_sig()],
529 },
530 functab: FuncTable {
531 content: vec![FuncEntry {
532 func: Func {
533 sig: 0,
534 locals: 0,
535 upvars: vec![],
536 bytecode,
537 },
538 cert: Certificate::default(),
539 }],
540 },
541 debugbintab: BinTable {
542 content: debugbintab,
543 },
544 funcdebugtab: FuncDebugTable { content: vec![] },
545 module_name: None,
546 };
547
548 let certs = Verifier::new(&content)
549 .compute(content.functab.content.iter().map(|e| &e.func))
550 .unwrap();
551 for (entry, cert) in content.functab.content.iter_mut().zip(certs.into_vec()) {
552 entry.cert = cert;
553 }
554 content
555 }
556
557 fn with_valid_content<T>(f: impl FnOnce(Content<'_>) -> T) -> T {
558 let bytecode = encode_raw(&[Inst::LoadConst(0), Inst::Ret]);
559 let content = valid_content_with(b"symnameconst", &bytecode, b"file\0func");
560 f(content)
561 }
562
563 fn serialize_bytes(content: &Content<'_>) -> Vec<u8> {
564 let mut out = Vec::new();
565 serialize(content, &mut out).unwrap();
566 out
567 }
568
569 fn expect_error<T>(res: Result<T>, pred: impl FnOnce(&Error) -> bool) {
570 match res {
571 Err(err) if pred(&err) => eprintln!("expected error: {err}"),
572 Err(err) => panic!("unexpected error: {err}"),
573 Ok(_) => panic!("unexpected success"),
574 }
575 }
576
577 #[test]
578 fn serialize_roundtrip_deserialize() {
579 with_valid_content(|content| {
580 let bytes = serialize_bytes(&content);
581 let verified = deserialize(&bytes).unwrap();
582 assert_eq!(
583 verified.functab.content.len(),
584 content.functab.content.len()
585 );
586 assert_eq!(verified.consttab, content.consttab);
587 assert_eq!(verified.symtab, content.symtab);
588 assert_eq!(verified.module_name, content.module_name);
589 });
590 }
591
592 #[test]
593 fn deserialize_rejects_invalid_header() {
594 with_valid_content(|content| {
595 let mut bytes = serialize_bytes(&content);
596 bytes[0] ^= 0xff;
597 expect_error(deserialize(&bytes), |err| {
598 matches!(err, Error::InvalidHeader)
599 });
600 });
601 }
602
603 #[test]
604 fn deserialize_rejects_trailing_junk() {
605 with_valid_content(|content| {
606 let mut bytes = serialize_bytes(&content);
607 bytes.push(0);
608 expect_error(deserialize(&bytes), |err| {
609 matches!(err, Error::TrailingJunk(_))
610 });
611 });
612 }
613
614 #[test]
615 fn verify_rejects_invalid_module_name() {
616 with_valid_content(|mut content| {
617 content.module_name = Some(99..100);
618 expect_error(verify(content), |err| {
619 matches!(err, Error::InvalidModuleName)
620 });
621 });
622 }
623
624 #[test]
625 fn verify_rejects_invalid_debugbintab_utf8() {
626 let invalid_debugbintab = [0xff];
627 let bytecode = encode_raw(&[Inst::LoadConst(0), Inst::Ret]);
628 let content = valid_content_with(b"symnameconst", &bytecode, &invalid_debugbintab);
629 expect_error(verify(content), |err| {
630 matches!(err, Error::InvalidUtf8InDebugBinTab)
631 });
632 }
633
634 #[test]
635 fn verify_rejects_invalid_symtab_string() {
636 with_valid_content(|mut content| {
637 content.symtab.content[0].name = 30..31;
638 expect_error(verify(content), |err| {
639 matches!(err, Error::InvalidStrInSymTab(0))
640 });
641 });
642 }
643
644 #[test]
645 fn verify_rejects_invalid_const_string_and_bin_and_sym() {
646 with_valid_content(|mut content| {
647 content.consttab.content[0] = Const::Str(30..31);
648 expect_error(verify(content), |err| {
649 matches!(err, Error::InvalidStrInConstTab(0))
650 });
651 });
652
653 with_valid_content(|mut content| {
654 content.consttab.content[0] = Const::Bin(30..31);
655 expect_error(verify(content), |err| {
656 matches!(err, Error::InvalidBinInConstTab(0))
657 });
658 });
659
660 with_valid_content(|mut content| {
661 content.consttab.content[0] = Const::Sym(4);
662 expect_error(verify(content), |err| {
663 matches!(err, Error::InvalidSymInConstTab(0))
664 });
665 });
666 }
667
668 #[test]
669 fn verify_rejects_invalid_pack_and_unpack_references() {
670 with_valid_content(|mut content| {
671 content.packtab.content[0] = vec![Arg::Key(9)];
672 expect_error(verify(content), |err| {
673 matches!(err, Error::InvalidSymInPackTab(0, 0))
674 });
675 });
676
677 with_valid_content(|mut content| {
678 content.unpacktab.content[0].keys = vec![UnpackKey {
679 kind: UnpackKeyKind::Sym(9),
680 default: None,
681 }];
682 expect_error(verify(content), |err| {
683 matches!(err, Error::InvalidSymInUnpackTab(0, 0))
684 });
685 });
686
687 with_valid_content(|mut content| {
688 content.unpacktab.content[0].keys = vec![UnpackKey {
689 kind: UnpackKeyKind::Const(9),
690 default: None,
691 }];
692 expect_error(verify(content), |err| {
693 matches!(err, Error::InvalidConstInUnpackTab(0, 0))
694 });
695 });
696
697 with_valid_content(|mut content| {
698 content.unpacktab.content[0].keys = vec![UnpackKey {
699 kind: UnpackKeyKind::Sym(0),
700 default: Some(9),
701 }];
702 expect_error(verify(content), |err| {
703 matches!(err, Error::InvalidConstInUnpackTab(0, 0))
704 });
705 });
706 }
707
708 #[test]
709 fn verify_rejects_const_key_in_function_param() {
710 with_valid_content(|mut content| {
711 content.unpacktab.content[0].keys = vec![UnpackKey {
712 kind: UnpackKeyKind::Const(0),
713 default: None,
714 }];
715 expect_error(verify(content), |err| {
716 matches!(err, Error::ConstKeyInFunctionParam(0, 0))
717 });
718 });
719 }
720
721 #[test]
722 fn verify_rejects_invalid_unpack_in_functab() {
723 with_valid_content(|mut content| {
724 content.functab.content[0].func.sig = 1;
725 expect_error(verify(content), |err| {
726 matches!(err, Error::InvalidUnpackInFuncTab(0))
727 });
728 });
729 }
730
731 #[test]
732 fn verify_rejects_wrong_funcdebugtab_size() {
733 with_valid_content(|mut content| {
734 content.funcdebugtab.content = vec![
735 FuncDebug {
736 name: 5..9,
737 sourcemap: vec![SourceLine {
738 offset_delta: 0,
739 line_delta: 1,
740 file: 0..4,
741 }],
742 },
743 FuncDebug {
744 name: 5..9,
745 sourcemap: vec![SourceLine {
746 offset_delta: 0,
747 line_delta: 1,
748 file: 0..4,
749 }],
750 },
751 ];
752 expect_error(verify(content), |err| {
753 matches!(err, Error::FuncDebugTabWrongSize)
754 });
755 });
756 }
757
758 #[test]
759 fn verify_rejects_funcdebugtab_and_sourcemap_errors() {
760 with_valid_content(|mut content| {
761 content.funcdebugtab.content = vec![FuncDebug {
762 name: 30..31,
763 sourcemap: vec![SourceLine {
764 offset_delta: 0,
765 line_delta: 1,
766 file: 0..4,
767 }],
768 }];
769 expect_error(verify(content), |err| {
770 matches!(err, Error::InvalidStrInFuncDebugTab(0))
771 });
772 });
773
774 with_valid_content(|mut content| {
775 content.funcdebugtab.content = vec![FuncDebug {
776 name: 5..9,
777 sourcemap: vec![],
778 }];
779 expect_error(verify(content), |err| {
780 matches!(err, Error::SourceMapEmpty(0))
781 });
782 });
783
784 with_valid_content(|mut content| {
785 content.funcdebugtab.content = vec![FuncDebug {
786 name: 5..9,
787 sourcemap: vec![
788 SourceLine {
789 offset_delta: 0,
790 line_delta: 1,
791 file: 0..4,
792 },
793 SourceLine {
794 offset_delta: 10,
795 line_delta: 1,
796 file: 0..4,
797 },
798 ],
799 }];
800 expect_error(verify(content), |err| {
801 matches!(err, Error::SourceMapOffsetBounds(0, 1))
802 });
803 });
804
805 with_valid_content(|mut content| {
806 content.funcdebugtab.content = vec![FuncDebug {
807 name: 5..9,
808 sourcemap: vec![
809 SourceLine {
810 offset_delta: 0,
811 line_delta: 1,
812 file: 0..4,
813 },
814 SourceLine {
815 offset_delta: 0,
816 line_delta: 0,
817 file: 0..4,
818 },
819 ],
820 }];
821 expect_error(verify(content), |err| {
822 matches!(err, Error::SourceMapLineDeltaZero(0, 1))
823 });
824 });
825
826 with_valid_content(|mut content| {
827 content.funcdebugtab.content = vec![FuncDebug {
828 name: 5..9,
829 sourcemap: vec![SourceLine {
830 offset_delta: 0,
831 line_delta: -1,
832 file: 0..4,
833 }],
834 }];
835 expect_error(verify(content), |err| {
836 matches!(err, Error::SourceMapLineBounds(0, 0))
837 });
838 });
839
840 with_valid_content(|mut content| {
841 content.funcdebugtab.content = vec![FuncDebug {
842 name: 5..9,
843 sourcemap: vec![SourceLine {
844 offset_delta: 0,
845 line_delta: 1,
846 file: 30..31,
847 }],
848 }];
849 expect_error(verify(content), |err| {
850 matches!(err, Error::InvalidStrInSourceMap(0, 0))
851 });
852 });
853 }
854
855 #[test]
856 fn deserialize_rejects_file_size_limit() {
857 let bytes = vec![0; limit::BYTECODE_FILE_SIZE + 1];
858 expect_error(deserialize(&bytes), |err| {
859 matches!(err, Error::FileSizeLimit)
860 });
861 }
862
863 #[test]
864 fn verify_rejects_bin_and_debug_bin_size_limits() {
865 let oversized_bintab = vec![0; limit::BIN_TAB_SIZE + 1];
866 let bytecode = encode_raw(&[Inst::LoadConst(0), Inst::Ret]);
867 let content = valid_content_with(&oversized_bintab, &bytecode, b"file\0func");
868 expect_error(verify(content), |err| matches!(err, Error::BinTabLimit));
869
870 let oversized_debugbintab = vec![b'a'; limit::DEBUG_BIN_TAB_SIZE + 1];
871 let bytecode = encode_raw(&[Inst::LoadConst(0), Inst::Ret]);
872 let content = valid_content_with(b"symnameconst", &bytecode, &oversized_debugbintab);
873 expect_error(verify(content), |err| {
874 matches!(err, Error::DebugBinTabLimit)
875 });
876 }
877
878 #[test]
879 #[cfg(not(miri))]
880 fn verify_rejects_symbol_and_constant_table_size_limits() {
881 with_valid_content(|mut content| {
882 content.symtab.content = (0..(limit::SYMBOL_TAB_ENTRIES + 1))
883 .map(|_| SymEntry {
884 name: 0..1,
885 private: false,
886 })
887 .collect();
888 expect_error(verify(content), |err| matches!(err, Error::SymTabLimit));
889 });
890
891 with_valid_content(|mut content| {
892 content.consttab.content = (0..(limit::CONST_TAB_ENTRIES + 1))
893 .map(|_| Const::Nil)
894 .collect();
895 expect_error(verify(content), |err| matches!(err, Error::ConstTabLimit));
896 });
897 }
898
899 #[test]
900 #[cfg(not(miri))]
901 fn verify_rejects_pack_and_unpack_table_size_limits() {
902 with_valid_content(|mut content| {
903 content.packtab.content = (0..(limit::PACK_TAB_ENTRIES + 1)).map(|_| vec![]).collect();
904 expect_error(verify(content), |err| matches!(err, Error::PackTabLimit));
905 });
906
907 with_valid_content(|mut content| {
908 content.unpacktab.content = (0..(limit::UNPACK_TAB_ENTRIES + 1))
909 .map(|_| empty_sig())
910 .collect();
911 expect_error(verify(content), |err| matches!(err, Error::UnpackTabLimit));
912 });
913 }
914
915 #[test]
916 fn verify_rejects_pack_and_unpack_entry_size_limits() {
917 with_valid_content(|mut content| {
918 content.packtab.content[0] =
919 (0..(limit::PACK_ENTRIES + 1)).map(|_| Arg::Value).collect();
920 expect_error(verify(content), |err| matches!(err, Error::PackLimit(0)));
921 });
922
923 with_valid_content(|mut content| {
924 content.unpacktab.content[0].keys = (0..(limit::UNPACK_ENTRIES + 1))
925 .map(|_| UnpackKey {
926 kind: UnpackKeyKind::Sym(0),
927 default: None,
928 })
929 .collect();
930 expect_error(verify(content), |err| matches!(err, Error::UnpackLimit(0)));
931 });
932 }
933
934 #[test]
935 #[cfg(not(miri))]
936 fn verify_rejects_function_table_and_function_limits() {
937 with_valid_content(|mut content| {
938 content.functab.content.clear();
939 expect_error(verify(content), |err| matches!(err, Error::FuncTabEmpty));
940 });
941
942 with_valid_content(|mut content| {
943 content.functab.content[0].func.bytecode = &[];
944 expect_error(verify(content), |err| matches!(err, Error::EmptyCode(0)));
945 });
946
947 let valid_bytecode = encode_raw(&[Inst::LoadConst(0), Inst::Ret]);
948 let mut content = valid_content_with(b"symnameconst", &valid_bytecode, b"file\0func");
949 let entry = FuncEntry {
950 func: Func {
951 sig: 0,
952 locals: 0,
953 upvars: vec![],
954 bytecode: &valid_bytecode,
955 },
956 cert: Certificate::default(),
957 };
958 content.functab.content = (0..(limit::FUNC_TAB_ENTRIES + 1))
959 .map(|_| FuncEntry {
960 func: Func {
961 sig: entry.func.sig,
962 locals: entry.func.locals,
963 upvars: entry.func.upvars.clone(),
964 bytecode: entry.func.bytecode,
965 },
966 cert: Certificate::default(),
967 })
968 .collect();
969 expect_error(verify(content), |err| matches!(err, Error::FuncTabLimit));
970
971 let valid_bytecode = encode_raw(&[Inst::LoadConst(0), Inst::Ret]);
972 let oversized_bytecode = vec![0; limit::FUNC_SIZE + 1];
973 let mut content = valid_content_with(b"symnameconst", &valid_bytecode, b"file\0func");
974 content.functab.content[0].func.bytecode = &oversized_bytecode;
975 expect_error(verify(content), |err| matches!(err, Error::CodeLimit(0)));
976
977 with_valid_content(|mut content| {
978 content.functab.content[0].cert.blocks = (0..(limit::CERT_ENTRIES + 1))
979 .map(|_| (0, Default::default()))
980 .collect();
981 expect_error(verify(content), |err| matches!(err, Error::CertLimit(0)));
982 });
983
984 with_valid_content(|mut content| {
985 content.functab.content[0].cert.max_operand_depth = limit::FUNC_FRAME_SLOTS + 1;
986 expect_error(verify(content), |err| {
987 matches!(err, Error::StackSlotLimit(0))
988 });
989 });
990 }
991
992 #[test]
993 fn verify_rejects_source_map_size_limit() {
994 with_valid_content(|mut content| {
995 content.funcdebugtab.content = vec![FuncDebug {
996 name: 5..9,
997 sourcemap: (0..(limit::SOURCE_MAP_ENTRIES + 1))
998 .map(|i| SourceLine {
999 offset_delta: usize::from(i != 0),
1000 line_delta: 1,
1001 file: 0..4,
1002 })
1003 .collect(),
1004 }];
1005 expect_error(verify(content), |err| {
1006 matches!(err, Error::SourceMapLimit(0))
1007 });
1008 });
1009 }
1010}