elf/
parse.rs

1//! Utilities to drive safe and lazy parsing of ELF structures.
2use core::{marker::PhantomData, ops::Range};
3
4use crate::endian::EndianParse;
5use crate::file::Class;
6
7#[derive(Debug)]
8pub enum ParseError {
9    /// Returned when the ELF File Header's magic bytes weren't ELF's defined
10    /// magic bytes
11    BadMagic([u8; 4]),
12    /// Returned when the ELF File Header's `e_ident[EI_CLASS]` wasn't one of the
13    /// defined `ELFCLASS*` constants
14    UnsupportedElfClass(u8),
15    /// Returned when the ELF File Header's `e_ident[EI_DATA]` wasn't one of the
16    /// defined `ELFDATA*` constants
17    UnsupportedElfEndianness(u8),
18    /// Returned when parsing an ELF struct with a version field whose value wasn't
19    /// something we support and know how to parse.
20    UnsupportedVersion((u64, u64)),
21    /// Returned when parsing an ELF structure resulted in an offset which fell
22    /// out of bounds of the requested structure
23    BadOffset(u64),
24    /// Returned when parsing a string out of a StringTable failed to find the
25    /// terminating NUL byte
26    StringTableMissingNul(u64),
27    /// Returned when parsing a table of ELF structures and the file specified
28    /// an entry size for that table that was different than what we had
29    /// expected
30    BadEntsize((u64, u64)),
31    /// Returned when trying to interpret a section's data as the wrong type.
32    /// For example, trying to treat an SHT_PROGBIGS section as a SHT_STRTAB.
33    UnexpectedSectionType((u32, u32)),
34    /// Returned when trying to interpret a segment's data as the wrong type.
35    /// For example, trying to treat an PT_LOAD section as a PT_NOTE.
36    UnexpectedSegmentType((u32, u32)),
37    /// Returned when a section has a sh_addralign value that was different
38    /// than we expected.
39    UnexpectedAlignment(usize),
40    /// Returned when parsing an ELF structure out of an in-memory `&[u8]`
41    /// resulted in a request for a section of file bytes outside the range of
42    /// the slice. Commonly caused by truncated file contents.
43    SliceReadError((usize, usize)),
44    /// Returned when doing math with parsed elf fields that resulted in integer overflow.
45    IntegerOverflow,
46    /// Returned when parsing a string out of a StringTable that contained
47    /// invalid Utf8
48    Utf8Error(core::str::Utf8Error),
49    /// Returned when parsing an ELF structure and the underlying structure data
50    /// was truncated and thus the full structure contents could not be parsed.
51    TryFromSliceError(core::array::TryFromSliceError),
52    /// Returned when parsing an ELF structure whose on-disk fields were too big
53    /// to represent in the native machine's usize type for in-memory processing.
54    /// This could be the case when processessing large 64-bit files on a 32-bit machine.
55    TryFromIntError(core::num::TryFromIntError),
56    #[cfg(feature = "std")]
57    /// Returned when parsing an ELF structure out of an io stream encountered
58    /// an io error.
59    IOError(std::io::Error),
60}
61
62#[cfg(feature = "std")]
63impl std::error::Error for ParseError {
64    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
65        match *self {
66            ParseError::BadMagic(_) => None,
67            ParseError::UnsupportedElfClass(_) => None,
68            ParseError::UnsupportedElfEndianness(_) => None,
69            ParseError::UnsupportedVersion(_) => None,
70            ParseError::BadOffset(_) => None,
71            ParseError::StringTableMissingNul(_) => None,
72            ParseError::BadEntsize(_) => None,
73            ParseError::UnexpectedSectionType(_) => None,
74            ParseError::UnexpectedSegmentType(_) => None,
75            ParseError::UnexpectedAlignment(_) => None,
76            ParseError::SliceReadError(_) => None,
77            ParseError::IntegerOverflow => None,
78            ParseError::Utf8Error(ref err) => Some(err),
79            ParseError::TryFromSliceError(ref err) => Some(err),
80            ParseError::TryFromIntError(ref err) => Some(err),
81            ParseError::IOError(ref err) => Some(err),
82        }
83    }
84}
85
86#[cfg(all(feature = "nightly", not(feature = "std")))]
87impl core::error::Error for ParseError {
88    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
89        match *self {
90            ParseError::BadMagic(_) => None,
91            ParseError::UnsupportedElfClass(_) => None,
92            ParseError::UnsupportedElfEndianness(_) => None,
93            ParseError::UnsupportedVersion(_) => None,
94            ParseError::BadOffset(_) => None,
95            ParseError::StringTableMissingNul(_) => None,
96            ParseError::BadEntsize(_) => None,
97            ParseError::UnexpectedSectionType(_) => None,
98            ParseError::UnexpectedSegmentType(_) => None,
99            ParseError::UnexpectedAlignment(_) => None,
100            ParseError::SliceReadError(_) => None,
101            ParseError::IntegerOverflow => None,
102            ParseError::Utf8Error(ref err) => Some(err),
103            ParseError::TryFromSliceError(ref err) => Some(err),
104            ParseError::TryFromIntError(ref err) => Some(err),
105        }
106    }
107}
108
109impl core::fmt::Display for ParseError {
110    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111        match *self {
112            ParseError::BadMagic(ref magic) => {
113                write!(f, "Invalid Magic Bytes: {magic:X?}")
114            }
115            ParseError::UnsupportedElfClass(class) => {
116                write!(f, "Unsupported ELF Class: {class}")
117            }
118            ParseError::UnsupportedElfEndianness(endianness) => {
119                write!(f, "Unsupported ELF Endianness: {endianness}")
120            }
121            ParseError::UnsupportedVersion((found, expected)) => {
122                write!(
123                    f,
124                    "Unsupported ELF Version field found: {found} expected: {expected}"
125                )
126            }
127            ParseError::BadOffset(offset) => {
128                write!(f, "Bad offset: {offset:#X}")
129            }
130            ParseError::StringTableMissingNul(offset) => {
131                write!(
132                    f,
133                    "Could not find terminating NUL byte starting at offset: {offset:#X}"
134                )
135            }
136            ParseError::BadEntsize((found, expected)) => {
137                write!(
138                    f,
139                    "Invalid entsize. Expected: {expected:#X}, Found: {found:#X}"
140                )
141            }
142            ParseError::UnexpectedSectionType((found, expected)) => {
143                write!(
144                    f,
145                    "Could not interpret section of type {found} as type {expected}"
146                )
147            }
148            ParseError::UnexpectedSegmentType((found, expected)) => {
149                write!(
150                    f,
151                    "Could not interpret section of type {found} as type {expected}"
152                )
153            }
154            ParseError::UnexpectedAlignment(align) => {
155                write!(
156                    f,
157                    "Could not interpret section with unexpected alignment of {align}"
158                )
159            }
160            ParseError::SliceReadError((start, end)) => {
161                write!(f, "Could not read bytes in range [{start:#X}, {end:#X})")
162            }
163            ParseError::IntegerOverflow => {
164                write!(f, "Integer overflow detected")
165            }
166            ParseError::Utf8Error(ref err) => err.fmt(f),
167            ParseError::TryFromSliceError(ref err) => err.fmt(f),
168            ParseError::TryFromIntError(ref err) => err.fmt(f),
169            #[cfg(feature = "std")]
170            ParseError::IOError(ref err) => err.fmt(f),
171        }
172    }
173}
174
175impl From<core::str::Utf8Error> for ParseError {
176    fn from(err: core::str::Utf8Error) -> Self {
177        ParseError::Utf8Error(err)
178    }
179}
180
181impl From<core::array::TryFromSliceError> for ParseError {
182    fn from(err: core::array::TryFromSliceError) -> Self {
183        ParseError::TryFromSliceError(err)
184    }
185}
186
187impl From<core::num::TryFromIntError> for ParseError {
188    fn from(err: core::num::TryFromIntError) -> Self {
189        ParseError::TryFromIntError(err)
190    }
191}
192
193#[cfg(feature = "std")]
194impl From<std::io::Error> for ParseError {
195    fn from(err: std::io::Error) -> ParseError {
196        ParseError::IOError(err)
197    }
198}
199
200/// Trait for safely parsing an ELF structure of a given class (32/64 bit) with
201/// an given endian-awareness at the given offset into the data buffer.
202///
203/// This is the trait that drives our elf parser, where the various ELF
204/// structures implement ParseAt in order to parse their Rust-native representation
205/// from a buffer, all using safe code.
206pub trait ParseAt: Sized {
207    /// Parse this type by using the given endian-awareness and ELF class layout.
208    /// This is generic on EndianParse in order to allow users to optimize for
209    /// their expectations of data layout. See EndianParse for more details.
210    fn parse_at<E: EndianParse>(
211        endian: E,
212        class: Class,
213        offset: &mut usize,
214        data: &[u8],
215    ) -> Result<Self, ParseError>;
216
217    /// Returns the expected size of the type being parsed for the given ELF class
218    fn size_for(class: Class) -> usize;
219
220    /// Checks whether the given entsize matches what we need to parse this type
221    ///
222    /// Returns a ParseError for bad/unexpected entsizes that don't match what this type parses.
223    fn validate_entsize(class: Class, entsize: usize) -> Result<usize, ParseError> {
224        let expected = Self::size_for(class);
225        match entsize == expected {
226            true => Ok(entsize),
227            false => Err(ParseError::BadEntsize((entsize as u64, expected as u64))),
228        }
229    }
230}
231
232/// Lazy-parsing iterator which wraps bytes and parses out a `P: ParseAt` on each `next()`
233#[derive(Debug)]
234pub struct ParsingIterator<'data, E: EndianParse, P: ParseAt> {
235    endian: E,
236    class: Class,
237    data: &'data [u8],
238    offset: usize,
239    // This struct doesn't technically own a P, but it yields them
240    // as it iterates
241    pd: PhantomData<&'data P>,
242}
243
244impl<'data, E: EndianParse, P: ParseAt> ParsingIterator<'data, E, P> {
245    pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self {
246        ParsingIterator {
247            endian,
248            class,
249            data,
250            offset: 0,
251            pd: PhantomData,
252        }
253    }
254}
255
256impl<'data, E: EndianParse, P: ParseAt> Iterator for ParsingIterator<'data, E, P> {
257    type Item = P;
258    fn next(&mut self) -> Option<Self::Item> {
259        if self.data.is_empty() {
260            return None;
261        }
262
263        Self::Item::parse_at(self.endian, self.class, &mut self.offset, self.data).ok()
264    }
265}
266
267/// Lazy-parsing table which wraps bytes and parses out a `P: ParseAt` at a given index into
268/// the table on each `get()`.
269#[derive(Debug, Clone, Copy)]
270pub struct ParsingTable<'data, E: EndianParse, P: ParseAt> {
271    endian: E,
272    class: Class,
273    data: &'data [u8],
274    // This struct doesn't technically own a P, but it yields them
275    pd: PhantomData<&'data P>,
276}
277
278impl<'data, E: EndianParse, P: ParseAt> ParsingTable<'data, E, P> {
279    pub fn new(endian: E, class: Class, data: &'data [u8]) -> Self {
280        ParsingTable {
281            endian,
282            class,
283            data,
284            pd: PhantomData,
285        }
286    }
287
288    /// Get a lazy-parsing iterator for the table's bytes
289    pub fn iter(&self) -> ParsingIterator<'data, E, P> {
290        ParsingIterator::new(self.endian, self.class, self.data)
291    }
292
293    /// Returns the number of elements of type P in the table.
294    pub fn len(&self) -> usize {
295        self.data.len() / P::size_for(self.class)
296    }
297
298    /// Returns whether the table is empty (contains zero elements).
299    pub fn is_empty(&self) -> bool {
300        self.len() == 0
301    }
302
303    /// Parse the element at `index` in the table.
304    pub fn get(&self, index: usize) -> Result<P, ParseError> {
305        if self.data.is_empty() {
306            return Err(ParseError::BadOffset(index as u64));
307        }
308
309        let entsize = P::size_for(self.class);
310        let mut start = index
311            .checked_mul(entsize)
312            .ok_or(ParseError::IntegerOverflow)?;
313        if start > self.data.len() {
314            return Err(ParseError::BadOffset(index as u64));
315        }
316
317        P::parse_at(self.endian, self.class, &mut start, self.data)
318    }
319}
320
321impl<'data, E: EndianParse, P: ParseAt> IntoIterator for ParsingTable<'data, E, P> {
322    type IntoIter = ParsingIterator<'data, E, P>;
323    type Item = P;
324
325    fn into_iter(self) -> Self::IntoIter {
326        ParsingIterator::new(self.endian, self.class, self.data)
327    }
328}
329
330// Simple convenience extension trait to wrap get() with .ok_or(SliceReadError)
331pub(crate) trait ReadBytesExt<'data> {
332    fn get_bytes(self, range: Range<usize>) -> Result<&'data [u8], ParseError>;
333}
334
335impl<'data> ReadBytesExt<'data> for &'data [u8] {
336    fn get_bytes(self, range: Range<usize>) -> Result<&'data [u8], ParseError> {
337        let start = range.start;
338        let end = range.end;
339        self.get(range)
340            .ok_or(ParseError::SliceReadError((start, end)))
341    }
342}
343
344#[cfg(test)]
345pub(crate) fn test_parse_for<E: EndianParse, P: ParseAt + core::fmt::Debug + PartialEq>(
346    endian: E,
347    class: Class,
348    expected: P,
349) {
350    let size = P::size_for(class);
351    let mut data = vec![0u8; size];
352    for (n, elem) in data.iter_mut().enumerate().take(size) {
353        *elem = n as u8;
354    }
355
356    let mut offset = 0;
357    let entry = P::parse_at(endian, class, &mut offset, data.as_ref()).expect("Failed to parse");
358
359    assert_eq!(entry, expected);
360    assert_eq!(offset, size);
361}
362
363#[cfg(test)]
364pub(crate) fn test_parse_fuzz_too_short<E: EndianParse, P: ParseAt + core::fmt::Debug>(
365    endian: E,
366    class: Class,
367) {
368    let size = P::size_for(class);
369    let data = vec![0u8; size];
370    for n in 0..size {
371        let buf = data.split_at(n).0;
372        let mut offset: usize = 0;
373        let error = P::parse_at(endian, class, &mut offset, buf).expect_err("Expected an error");
374        assert!(
375            matches!(error, ParseError::SliceReadError(_)),
376            "Unexpected Error type found: {error}"
377        );
378    }
379}
380
381#[cfg(test)]
382mod read_bytes_tests {
383    use super::ParseError;
384    use super::ReadBytesExt;
385
386    #[test]
387    fn get_bytes_works() {
388        let data = &[0u8, 1, 2, 3];
389        let subslice = data.get_bytes(1..3).expect("should be within range");
390        assert_eq!(subslice, [1, 2]);
391    }
392
393    #[test]
394    fn get_bytes_out_of_range_errors() {
395        let data = &[0u8, 1, 2, 3];
396        let err = data.get_bytes(3..9).expect_err("should be out of range");
397        assert!(
398            matches!(err, ParseError::SliceReadError((3, 9))),
399            "Unexpected Error type found: {err}"
400        );
401    }
402}
403
404#[cfg(test)]
405mod parsing_table_tests {
406    use crate::endian::{AnyEndian, BigEndian, LittleEndian};
407
408    use super::*;
409
410    type U32Table<'data, E> = ParsingTable<'data, E, u32>;
411
412    #[test]
413    fn test_u32_validate_entsize() {
414        assert!(matches!(u32::validate_entsize(Class::ELF32, 4), Ok(4)));
415        assert!(matches!(
416            u32::validate_entsize(Class::ELF32, 8),
417            Err(ParseError::BadEntsize((8, 4)))
418        ));
419    }
420
421    #[test]
422    fn test_u32_parse_at() {
423        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
424        let mut offset = 2;
425        let result = u32::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref())
426            .expect("Expected to parse but:");
427        assert_eq!(result, 0x05040302);
428    }
429
430    #[test]
431    fn test_u32_table_len() {
432        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
433        let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
434        assert_eq!(table.len(), 2);
435    }
436
437    #[test]
438    fn test_u32_table_is_empty() {
439        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
440        let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
441        assert!(!table.is_empty());
442
443        let table = U32Table::new(LittleEndian, Class::ELF32, &[]);
444        assert!(table.is_empty());
445
446        let table = U32Table::new(LittleEndian, Class::ELF32, data.get(0..1).unwrap());
447        assert!(table.is_empty());
448    }
449
450    #[test]
451    fn test_u32_table_get_parse_failure() {
452        let data = vec![0u8, 1];
453        let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
454        assert!(matches!(
455            table.get(0),
456            Err(ParseError::SliceReadError((0, 4)))
457        ));
458    }
459
460    #[test]
461    fn test_lsb_u32_table_get() {
462        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
463        let table = U32Table::new(LittleEndian, Class::ELF32, data.as_ref());
464        assert!(matches!(table.get(0), Ok(0x03020100)));
465        assert!(matches!(table.get(1), Ok(0x07060504)));
466        assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
467    }
468
469    #[test]
470    fn test_any_lsb_u32_table_get() {
471        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
472        let table = U32Table::new(AnyEndian::Little, Class::ELF32, data.as_ref());
473        assert!(matches!(table.get(0), Ok(0x03020100)));
474        assert!(matches!(table.get(1), Ok(0x07060504)));
475        assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
476    }
477
478    #[test]
479    fn test_msb_u32_table_get() {
480        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
481        let table = U32Table::new(BigEndian, Class::ELF32, data.as_ref());
482        assert!(matches!(table.get(0), Ok(0x00010203)));
483        assert!(matches!(table.get(1), Ok(0x04050607)));
484        assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
485    }
486
487    #[test]
488    fn test_any_msb_u32_table_get() {
489        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
490        let table = U32Table::new(AnyEndian::Big, Class::ELF32, data.as_ref());
491        assert!(matches!(table.get(0), Ok(0x00010203)));
492        assert!(matches!(table.get(1), Ok(0x04050607)));
493        assert!(matches!(table.get(7), Err(ParseError::BadOffset(7))));
494    }
495
496    #[test]
497    fn test_u32_table_get_unaligned() {
498        let data = [0u8, 1, 2, 3, 4, 5, 6, 7];
499        let table = U32Table::new(LittleEndian, Class::ELF32, data.get(1..).unwrap());
500        assert!(matches!(table.get(0), Ok(0x04030201)));
501    }
502}