From 7dab8331de0ce5e40fb0369e22e94122e17ce3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 18 Oct 2024 20:20:36 +0100 Subject: [PATCH 1/7] Factors out Elf64::parse_file_header(). --- src/elf_parser/mod.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index 12e13766..c2aed27b 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -138,19 +138,7 @@ pub struct Elf64<'a> { impl<'a> Elf64<'a> { /// Parse from the given byte slice pub fn parse(elf_bytes: &'a [u8]) -> Result { - let file_header_range = 0..mem::size_of::(); - let file_header_bytes = elf_bytes - .get(file_header_range.clone()) - .ok_or(ElfParserError::OutOfBounds)?; - let ptr = file_header_bytes.as_ptr(); - if (ptr as usize) - .checked_rem(mem::align_of::()) - .map(|remaining| remaining != 0) - .unwrap_or(true) - { - return Err(ElfParserError::InvalidAlignment); - } - let file_header = unsafe { &*ptr.cast::() }; + let (file_header_range, file_header) = Self::parse_file_header(elf_bytes)?; if file_header.e_ident.ei_mag != ELFMAG || file_header.e_ident.ei_class != ELFCLASS64 @@ -283,6 +271,26 @@ impl<'a> Elf64<'a> { self.dynamic_relocations_table } + /// Parses the file header. + pub fn parse_file_header( + elf_bytes: &'a [u8], + ) -> Result<(std::ops::Range, &'a Elf64Ehdr), ElfParserError> { + let file_header_range = 0..mem::size_of::(); + let file_header_bytes = elf_bytes + .get(file_header_range.clone()) + .ok_or(ElfParserError::OutOfBounds)?; + let ptr = file_header_bytes.as_ptr(); + if (ptr as usize) + .checked_rem(mem::align_of::()) + .map(|remaining| remaining != 0) + .unwrap_or(true) + { + return Err(ElfParserError::InvalidAlignment); + } + let file_header = unsafe { &*ptr.cast::() }; + Ok((file_header_range, file_header)) + } + fn parse_sections(&mut self) -> Result<(), ElfParserError> { macro_rules! section_header_by_name { ($self:expr, $section_header:expr, $section_name:expr, From 64567d2b7a0e208f41243dd8214b88c8a534124c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 21 Oct 2024 11:50:09 +0100 Subject: [PATCH 2/7] Factors out Elf64::parse_program_header_table(). --- src/elf_parser/mod.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index c2aed27b..6c215cc7 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -153,13 +153,8 @@ impl<'a> Elf64<'a> { return Err(ElfParserError::InvalidFileHeader); } - let program_header_table_range = file_header.e_phoff as usize - ..mem::size_of::() - .err_checked_mul(file_header.e_phnum as usize)? - .err_checked_add(file_header.e_phoff as usize)?; - check_that_there_is_no_overlap(&file_header_range, &program_header_table_range)?; - let program_header_table = - slice_from_bytes::(elf_bytes, program_header_table_range.clone())?; + let (program_header_table_range, program_header_table) = + Self::parse_program_header_table(elf_bytes, file_header_range.clone(), file_header)?; let section_header_table_range = file_header.e_shoff as usize ..mem::size_of::() @@ -291,6 +286,22 @@ impl<'a> Elf64<'a> { Ok((file_header_range, file_header)) } + /// Parses the program header table. + pub fn parse_program_header_table( + elf_bytes: &'a [u8], + file_header_range: std::ops::Range, + file_header: &Elf64Ehdr, + ) -> Result<(std::ops::Range, &'a [Elf64Phdr]), ElfParserError> { + let program_header_table_range = file_header.e_phoff as usize + ..mem::size_of::() + .err_checked_mul(file_header.e_phnum as usize)? + .err_checked_add(file_header.e_phoff as usize)?; + check_that_there_is_no_overlap(&file_header_range, &program_header_table_range)?; + let program_header_table = + slice_from_bytes::(elf_bytes, program_header_table_range.clone())?; + Ok((program_header_table_range, program_header_table)) + } + fn parse_sections(&mut self) -> Result<(), ElfParserError> { macro_rules! section_header_by_name { ($self:expr, $section_header:expr, $section_name:expr, From 5d1e46251246c019139a8134ba44263148353116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 21 Oct 2024 11:50:36 +0100 Subject: [PATCH 3/7] Factors out Elf64::parse_section_header_table(). --- src/elf_parser/mod.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index 6c215cc7..ea1ccd40 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -156,14 +156,13 @@ impl<'a> Elf64<'a> { let (program_header_table_range, program_header_table) = Self::parse_program_header_table(elf_bytes, file_header_range.clone(), file_header)?; - let section_header_table_range = file_header.e_shoff as usize - ..mem::size_of::() - .err_checked_mul(file_header.e_shnum as usize)? - .err_checked_add(file_header.e_shoff as usize)?; - check_that_there_is_no_overlap(&file_header_range, §ion_header_table_range)?; - check_that_there_is_no_overlap(&program_header_table_range, §ion_header_table_range)?; - let section_header_table = - slice_from_bytes::(elf_bytes, section_header_table_range.clone())?; + let (section_header_table_range, section_header_table) = Self::parse_section_header_table( + elf_bytes, + file_header_range.clone(), + file_header, + program_header_table_range.clone(), + )?; + section_header_table .first() .filter(|section_header| section_header.sh_type == SHT_NULL) @@ -302,6 +301,24 @@ impl<'a> Elf64<'a> { Ok((program_header_table_range, program_header_table)) } + /// Parses the section header table. + pub fn parse_section_header_table( + elf_bytes: &'a [u8], + file_header_range: std::ops::Range, + file_header: &Elf64Ehdr, + program_header_table_range: std::ops::Range, + ) -> Result<(std::ops::Range, &'a [Elf64Shdr]), ElfParserError> { + let section_header_table_range = file_header.e_shoff as usize + ..mem::size_of::() + .err_checked_mul(file_header.e_shnum as usize)? + .err_checked_add(file_header.e_shoff as usize)?; + check_that_there_is_no_overlap(&file_header_range, §ion_header_table_range)?; + check_that_there_is_no_overlap(&program_header_table_range, §ion_header_table_range)?; + let section_header_table = + slice_from_bytes::(elf_bytes, section_header_table_range.clone())?; + Ok((section_header_table_range, section_header_table)) + } + fn parse_sections(&mut self) -> Result<(), ElfParserError> { macro_rules! section_header_by_name { ($self:expr, $section_header:expr, $section_name:expr, From 26ccd7295e31cfe69c802d85cddc97b881094ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 18 Oct 2024 16:33:23 +0100 Subject: [PATCH 4/7] Makes program and section header scan more similar. --- src/elf_parser/mod.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index ea1ccd40..6758f40e 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -168,28 +168,20 @@ impl<'a> Elf64<'a> { .filter(|section_header| section_header.sh_type == SHT_NULL) .ok_or(ElfParserError::InvalidSectionHeader)?; - let mut prev_program_header: Option<&Elf64Phdr> = None; + let mut vaddr = 0usize; for program_header in program_header_table { if program_header.p_type != PT_LOAD { continue; } - - if let Some(prev_program_header) = prev_program_header { - // program headers must be ascending - if program_header.p_vaddr < prev_program_header.p_vaddr { - return Err(ElfParserError::InvalidProgramHeader); - } + if (program_header.p_vaddr as usize) < vaddr { + return Err(ElfParserError::InvalidProgramHeader); } - - if program_header + vaddr = program_header .p_offset - .err_checked_add(program_header.p_filesz)? as usize - > elf_bytes.len() - { + .err_checked_add(program_header.p_filesz)? as usize; + if vaddr > elf_bytes.len() { return Err(ElfParserError::OutOfBounds); } - - prev_program_header = Some(program_header) } let mut offset = 0usize; @@ -206,10 +198,10 @@ impl<'a> Elf64<'a> { if section_range.start < offset { return Err(ElfParserError::SectionNotInOrder); } - if section_range.end > elf_bytes.len() { + offset = section_range.end; + if offset > elf_bytes.len() { return Err(ElfParserError::OutOfBounds); } - offset = section_range.end; } let section_names_section_header = (file_header.e_shstrndx != SHN_UNDEF) From 8cc7b678f8a8cc7439f1dd3891a469f4c7d241cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 21 Oct 2024 11:51:43 +0100 Subject: [PATCH 5/7] Moves slice_from_bytes() back in Elf64. --- src/elf_parser/mod.rs | 90 ++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index 6758f40e..ae334d05 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -289,7 +289,7 @@ impl<'a> Elf64<'a> { .err_checked_add(file_header.e_phoff as usize)?; check_that_there_is_no_overlap(&file_header_range, &program_header_table_range)?; let program_header_table = - slice_from_bytes::(elf_bytes, program_header_table_range.clone())?; + Self::slice_from_bytes::(elf_bytes, program_header_table_range.clone())?; Ok((program_header_table_range, program_header_table)) } @@ -307,7 +307,7 @@ impl<'a> Elf64<'a> { check_that_there_is_no_overlap(&file_header_range, §ion_header_table_range)?; check_that_there_is_no_overlap(&program_header_table_range, §ion_header_table_range)?; let section_header_table = - slice_from_bytes::(elf_bytes, section_header_table_range.clone())?; + Self::slice_from_bytes::(elf_bytes, section_header_table_range.clone())?; Ok((section_header_table_range, section_header_table)) } @@ -355,7 +355,8 @@ impl<'a> Elf64<'a> { .iter() .find(|program_header| program_header.p_type == PT_DYNAMIC) { - dynamic_table = self.slice_from_program_header(dynamic_program_header).ok(); + dynamic_table = + Self::slice_from_program_header(self.elf_bytes, dynamic_program_header).ok(); } // if PT_DYNAMIC does not exist or is invalid (some of our tests have this), @@ -367,7 +368,7 @@ impl<'a> Elf64<'a> { .find(|section_header| section_header.sh_type == SHT_DYNAMIC) { dynamic_table = Some( - self.slice_from_section_header(dynamic_section_header) + Self::slice_from_section_header(self.elf_bytes, dynamic_section_header) .map_err(|_| ElfParserError::InvalidDynamicSectionTable)?, ); } @@ -430,7 +431,7 @@ impl<'a> Elf64<'a> { .sh_offset } as usize; - self.slice_from_bytes(offset..offset.err_checked_add(size)?) + Self::slice_from_bytes(self.elf_bytes, offset..offset.err_checked_add(size)?) .map(Some) .map_err(|_| ElfParserError::InvalidDynamicSectionTable) } @@ -529,18 +530,19 @@ impl<'a> Elf64<'a> { return Err(ElfParserError::InvalidSectionHeader); } - self.slice_from_section_header(section_header) + Self::slice_from_section_header(self.elf_bytes, section_header) } /// Returns the `&[T]` contained in the data described by the given program /// header pub fn slice_from_program_header( - &self, + bytes: &'a [u8], &Elf64Phdr { p_offset, p_filesz, .. }: &Elf64Phdr, ) -> Result<&'a [T], ElfParserError> { - self.slice_from_bytes( + Self::slice_from_bytes( + bytes, (p_offset as usize)..(p_offset as usize).err_checked_add(p_filesz as usize)?, ) } @@ -548,19 +550,50 @@ impl<'a> Elf64<'a> { /// Returns the `&[T]` contained in the section data described by the given /// section header pub fn slice_from_section_header( - &self, + bytes: &'a [u8], &Elf64Shdr { sh_offset, sh_size, .. }: &Elf64Shdr, ) -> Result<&'a [T], ElfParserError> { - self.slice_from_bytes( + Self::slice_from_bytes( + bytes, (sh_offset as usize)..(sh_offset as usize).err_checked_add(sh_size as usize)?, ) } - /// Returns the `&[T]` contained at `elf_bytes[offset..size]` - fn slice_from_bytes(&self, range: Range) -> Result<&'a [T], ElfParserError> { - slice_from_bytes(self.elf_bytes, range) + /// Returns the `&[T]` contained at `bytes[range]` + fn slice_from_bytes( + bytes: &[u8], + range: Range, + ) -> Result<&[T], ElfParserError> { + if range + .len() + .checked_rem(mem::size_of::()) + .map(|remainder| remainder != 0) + .unwrap_or(true) + { + return Err(ElfParserError::InvalidSize); + } + + let bytes = bytes + .get(range.clone()) + .ok_or(ElfParserError::OutOfBounds)?; + + let ptr = bytes.as_ptr(); + if (ptr as usize) + .checked_rem(mem::align_of::()) + .map(|remaining| remaining != 0) + .unwrap_or(true) + { + return Err(ElfParserError::InvalidAlignment); + } + + Ok(unsafe { + slice::from_raw_parts( + ptr.cast(), + range.len().checked_div(mem::size_of::()).unwrap_or(0), + ) + }) } fn program_header_for_vaddr( @@ -623,37 +656,6 @@ impl fmt::Debug for Elf64<'_> { } } -fn slice_from_bytes(bytes: &[u8], range: Range) -> Result<&[T], ElfParserError> { - if range - .len() - .checked_rem(mem::size_of::()) - .map(|remainder| remainder != 0) - .unwrap_or(true) - { - return Err(ElfParserError::InvalidSize); - } - - let bytes = bytes - .get(range.clone()) - .ok_or(ElfParserError::OutOfBounds)?; - - let ptr = bytes.as_ptr(); - if (ptr as usize) - .checked_rem(mem::align_of::()) - .map(|remaining| remaining != 0) - .unwrap_or(true) - { - return Err(ElfParserError::InvalidAlignment); - } - - Ok(unsafe { - slice::from_raw_parts( - ptr.cast(), - range.len().checked_div(mem::size_of::()).unwrap_or(0), - ) - }) -} - impl From for ElfParserError { fn from(_: ArithmeticOverflow) -> ElfParserError { ElfParserError::OutOfBounds From a8d03edcc7605b95961e583c32ef64db68b1fd66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Sat, 19 Oct 2024 20:11:08 +0100 Subject: [PATCH 6/7] Removes self parameter from ELf64::get_string_in_section(). --- src/elf_parser/mod.rs | 55 ++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index ae334d05..23fc272a 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -330,7 +330,8 @@ impl<'a> Elf64<'a> { .section_names_section_header .ok_or(ElfParserError::NoSectionNameStringTable)?; for section_header in self.section_header_table.iter() { - let section_name = self.get_string_in_section( + let section_name = Self::get_string_in_section( + self.elf_bytes, section_names_section_header, section_header.sh_name, SECTION_NAME_LENGTH_MAXIMUM, @@ -454,7 +455,7 @@ impl<'a> Elf64<'a> { /// Query a single string from a section which is marked as SHT_STRTAB pub fn get_string_in_section( - &self, + elf_bytes: &'a [u8], section_header: &Elf64Shdr, offset_in_section: Elf64Word, maximum_length: usize, @@ -468,8 +469,7 @@ impl<'a> Elf64<'a> { ..(section_header.sh_offset as usize) .err_checked_add(section_header.sh_size as usize)? .min(offset_in_file.err_checked_add(maximum_length)?); - let unterminated_string_bytes = self - .elf_bytes + let unterminated_string_bytes = elf_bytes .get(string_range) .ok_or(ElfParserError::OutOfBounds)?; unterminated_string_bytes @@ -486,7 +486,8 @@ impl<'a> Elf64<'a> { /// Returns the string corresponding to the given `sh_name` pub fn section_name(&self, sh_name: Elf64Word) -> Result<&'a [u8], ElfParserError> { - self.get_string_in_section( + Self::get_string_in_section( + self.elf_bytes, self.section_names_section_header .ok_or(ElfParserError::NoSectionNameStringTable)?, sh_name, @@ -496,7 +497,8 @@ impl<'a> Elf64<'a> { /// Returns the name of the `st_name` symbol pub fn symbol_name(&self, st_name: Elf64Word) -> Result<&'a [u8], ElfParserError> { - self.get_string_in_section( + Self::get_string_in_section( + self.elf_bytes, self.symbol_names_section_header .ok_or(ElfParserError::NoStringTable)?, st_name, @@ -513,7 +515,8 @@ impl<'a> Elf64<'a> { /// Returns the name of the `st_name` dynamic symbol pub fn dynamic_symbol_name(&self, st_name: Elf64Word) -> Result<&'a [u8], ElfParserError> { - self.get_string_in_section( + Self::get_string_in_section( + self.elf_bytes, self.dynamic_symbol_names_section_header .ok_or(ElfParserError::NoDynamicStringTable)?, st_name, @@ -620,16 +623,14 @@ impl fmt::Debug for Elf64<'_> { writeln!(f, "{program_header:#X?}")?; } for section_header in self.section_header_table.iter() { - let section_name = self - .get_string_in_section( - self.section_names_section_header.unwrap(), - section_header.sh_name, - SECTION_NAME_LENGTH_MAXIMUM, - ) - .and_then(|name| { - std::str::from_utf8(name).map_err(|_| ElfParserError::InvalidString) - }) - .unwrap(); + let section_name = Self::get_string_in_section( + self.elf_bytes, + self.section_names_section_header.unwrap(), + section_header.sh_name, + SECTION_NAME_LENGTH_MAXIMUM, + ) + .and_then(|name| std::str::from_utf8(name).map_err(|_| ElfParserError::InvalidString)) + .unwrap(); writeln!(f, "{section_name}")?; writeln!(f, "{section_header:#X?}")?; } @@ -638,16 +639,16 @@ impl fmt::Debug for Elf64<'_> { writeln!(f, "{symbol_table:#X?}")?; for symbol in symbol_table.iter() { if symbol.st_name != 0 { - let symbol_name = self - .get_string_in_section( - self.symbol_names_section_header.unwrap(), - symbol.st_name, - SYMBOL_NAME_LENGTH_MAXIMUM, - ) - .and_then(|name| { - std::str::from_utf8(name).map_err(|_| ElfParserError::InvalidString) - }) - .unwrap(); + let symbol_name = Self::get_string_in_section( + self.elf_bytes, + self.symbol_names_section_header.unwrap(), + symbol.st_name, + SYMBOL_NAME_LENGTH_MAXIMUM, + ) + .and_then(|name| { + std::str::from_utf8(name).map_err(|_| ElfParserError::InvalidString) + }) + .unwrap(); writeln!(f, "{symbol_name}")?; } } From b1b2eda8b34c35c040b64027aa34b8ef73b543a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Fri, 18 Oct 2024 20:56:19 +0100 Subject: [PATCH 7/7] Adds Elf64Phdr::file_range(). --- src/elf_parser/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/elf_parser/mod.rs b/src/elf_parser/mod.rs index 23fc272a..e6795c9a 100644 --- a/src/elf_parser/mod.rs +++ b/src/elf_parser/mod.rs @@ -63,6 +63,14 @@ pub enum ElfParserError { } impl Elf64Phdr { + /// Returns the byte range the section spans in the file. + pub fn file_range(&self) -> Option> { + (self.p_type == PT_LOAD).then(|| { + let offset = self.p_offset as usize; + offset..offset.saturating_add(self.p_filesz as usize) + }) + } + /// Returns the segment virtual address range. pub fn vm_range(&self) -> Range { let addr = self.p_vaddr;