Skip to main content

dolang_bytecode/
file.rs

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    /// Symbol table index
83    Sym(usize),
84    /// Constant table index
85    Const(usize),
86}
87
88#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
89pub struct UnpackKey {
90    /// Key kind (symbol or constant)
91    pub kind: UnpackKeyKind,
92    /// Constant table index of default value, if key is optional
93    pub default: Option<usize>,
94}
95
96#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
97pub struct UnpackSig {
98    /// Required positional arguments
99    pub required: usize,
100    /// Optional positional arguments (constant table index of default value)
101    pub optional: Vec<usize>,
102    /// Key arguments (required and optional)
103    pub keys: Vec<UnpackKey>,
104    /// Variadic mode (None, Discard, or Capture)
105    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    // FIXME: the overall file size limit mitigates this, but the inability to (easily) impose limits
172    // during deserialization is a problem
173    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    // 🫡 Don't crash
263    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
275// Perfunctory, instruction-level analysis done by dedicated verifier
276fn 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        // Function signature must be in bounds
287        if func.func.sig >= unpacktab.content.len() {
288            return Err(Error::InvalidUnpackInFuncTab(i));
289        }
290
291        // Validate that function signature doesn't contain constant keys
292        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        // Bytecode must be non-empty and within limit
299        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        // Stack frame size must be within limit
311        if func.cert.max_operand_depth.saturating_add(func.func.locals) > limit::FUNC_FRAME_SLOTS {
312            return Err(Error::StackSlotLimit(i));
313        }
314        // We could check upvar limits now, but they could still be exceeded by individual
315        // instructions pushing the upvar stack, so this is deferred to instruction-level analysis
316    }
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}