Skip to main content

sbpf_disassembler/
errors.rs

1use {
2    sbpf_common::errors::SBPFError,
3    std::{ops::Range, string::FromUtf8Error},
4    thiserror::Error,
5};
6
7/// Render the acceptable values for an ELF header field, e.g. `0xf7 or 0x107`.
8fn hex_list(values: &[u64]) -> String {
9    values
10        .iter()
11        .map(|v| format!("{v:#x}"))
12        .collect::<Vec<_>>()
13        .join(" or ")
14}
15
16#[derive(Debug, Error, Clone)]
17pub enum DisassemblerError {
18    #[error("Failed to parse ELF file: {source}; first bytes: {first_bytes:02x?}")]
19    InvalidElfFile {
20        first_bytes: Vec<u8>,
21        #[source]
22        source: object::read::Error,
23    },
24    #[error("Non-standard ELF header: {field} is {found:#x}, expected {}", hex_list(.expected))]
25    NonStandardElfHeader {
26        field: &'static str,
27        expected: Vec<u64>,
28        found: u64,
29    },
30    #[error("Invalid Program Type: {0:#x}")]
31    InvalidProgramType(u32),
32    #[error("Invalid Section Header Type: {0:#x}")]
33    InvalidSectionHeaderType(u32),
34    #[error("Invalid Relocation Type: {0:#x}")]
35    InvalidRelocationType(u32),
36    #[error("Failed to read {section} section data: {source}")]
37    SectionDataError {
38        section: &'static str,
39        #[source]
40        source: object::read::Error,
41    },
42    #[error("Invalid data length: {0}")]
43    InvalidDataLength(usize),
44    #[error("Bytecode error at bytes {span:?}: {error}")]
45    BytecodeError { error: String, span: Range<usize> },
46    #[error("Missing text section; sections present: {sections:?}")]
47    MissingTextSection { sections: Vec<String> },
48    #[error("Invalid offset in .dynstr section: offset {offset}, data length {data_len}")]
49    InvalidDynstrOffset { offset: usize, data_len: usize },
50    #[error("Non-UTF8 data in .dynstr section: {0}")]
51    InvalidUtf8InDynstr(FromUtf8Error),
52    #[error("Invalid section header string table index: {shstrndx}, section header count: {shnum}")]
53    InvalidShstrndx { shstrndx: u16, shnum: usize },
54    #[error(
55        "Invalid section name offset: sh_name {sh_name:#x} exceeds string table length \
56         {strtab_len:#x}"
57    )]
58    InvalidSectionName { sh_name: u32, strtab_len: usize },
59    #[error(
60        "Section {section} data out of bounds: offset {offset:#x} + size {size:#x} exceeds file \
61         length {file_len:#x}"
62    )]
63    SectionDataOutOfBounds {
64        section: String,
65        offset: u64,
66        size: u64,
67        file_len: usize,
68    },
69}
70
71impl From<SBPFError> for DisassemblerError {
72    fn from(err: SBPFError) -> Self {
73        match err {
74            SBPFError::BytecodeError { error, span, .. } => {
75                DisassemblerError::BytecodeError { error, span }
76            }
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use {super::*, object::read::elf::ElfFile64};
84
85    #[test]
86    fn test_from_sbpf_error() {
87        let sbpf_error = SBPFError::BytecodeError {
88            error: "test error".to_string(),
89            span: 8..16,
90            custom_label: None,
91        };
92        let disasm_error: DisassemblerError = sbpf_error.into();
93        assert!(matches!(
94            disasm_error,
95            DisassemblerError::BytecodeError { error, span }
96                if error == "test error" && span == (8..16)
97        ));
98    }
99
100    #[test]
101    fn test_error_display() {
102        let empty: &[u8] = &[];
103        let parse_error = ElfFile64::<object::Endianness>::parse(empty).unwrap_err();
104        let msg = DisassemblerError::InvalidElfFile {
105            first_bytes: vec![0x7f, 0x45],
106            source: parse_error,
107        }
108        .to_string();
109        assert!(msg.starts_with("Failed to parse ELF file: "));
110        assert!(msg.ends_with("; first bytes: [7f, 45]"));
111
112        assert_eq!(
113            DisassemblerError::NonStandardElfHeader {
114                field: "machine",
115                expected: vec![0xf7, 0x107],
116                found: 0x108,
117            }
118            .to_string(),
119            "Non-standard ELF header: machine is 0x108, expected 0xf7 or 0x107"
120        );
121        assert_eq!(
122            DisassemblerError::InvalidProgramType(0x6474e550).to_string(),
123            "Invalid Program Type: 0x6474e550"
124        );
125        assert_eq!(
126            DisassemblerError::InvalidSectionHeaderType(0x6ffffff6).to_string(),
127            "Invalid Section Header Type: 0x6ffffff6"
128        );
129        assert_eq!(
130            DisassemblerError::InvalidRelocationType(0x2a).to_string(),
131            "Invalid Relocation Type: 0x2a"
132        );
133        let section_error = ElfFile64::<object::Endianness>::parse(empty).unwrap_err();
134        assert!(
135            DisassemblerError::SectionDataError {
136                section: ".rel.dyn",
137                source: section_error,
138            }
139            .to_string()
140            .starts_with("Failed to read .rel.dyn section data: ")
141        );
142        assert_eq!(
143            DisassemblerError::InvalidDataLength(13).to_string(),
144            "Invalid data length: 13"
145        );
146        assert_eq!(
147            DisassemblerError::BytecodeError {
148                error: "custom".to_string(),
149                span: 8..16,
150            }
151            .to_string(),
152            "Bytecode error at bytes 8..16: custom"
153        );
154        assert_eq!(
155            DisassemblerError::MissingTextSection {
156                sections: vec![".rodata".to_string(), ".shstrtab".to_string()],
157            }
158            .to_string(),
159            "Missing text section; sections present: [\".rodata\", \".shstrtab\"]"
160        );
161        assert_eq!(
162            DisassemblerError::InvalidDynstrOffset {
163                offset: 128,
164                data_len: 64,
165            }
166            .to_string(),
167            "Invalid offset in .dynstr section: offset 128, data length 64"
168        );
169
170        let utf8_error = String::from_utf8(vec![0x66, 0xff]).unwrap_err();
171        assert!(
172            DisassemblerError::InvalidUtf8InDynstr(utf8_error)
173                .to_string()
174                .starts_with("Non-UTF8 data in .dynstr section: invalid utf-8")
175        );
176        assert_eq!(
177            DisassemblerError::InvalidShstrndx {
178                shstrndx: 9,
179                shnum: 6,
180            }
181            .to_string(),
182            "Invalid section header string table index: 9, section header count: 6"
183        );
184        assert_eq!(
185            DisassemblerError::InvalidSectionName {
186                sh_name: 0x40,
187                strtab_len: 0x2a,
188            }
189            .to_string(),
190            "Invalid section name offset: sh_name 0x40 exceeds string table length 0x2a"
191        );
192        assert_eq!(
193            DisassemblerError::SectionDataOutOfBounds {
194                section: ".text".to_string(),
195                offset: 0x1000,
196                size: 0x20,
197                file_len: 0x800,
198            }
199            .to_string(),
200            "Section .text data out of bounds: offset 0x1000 + size 0x20 exceeds file length 0x800"
201        );
202    }
203}