Decouple the Ehdr.e_data endianness byte and the parsing endianness switch

This patch adds verification to the initial e_ident[] parsing verification which returns
an error if the file's e_ident contains an unknown endianness value. This allows the later
integer parsing methods (read_u*) to assume that they are given a valid endianness configuration
(either little or big).
This commit is contained in:
Christopher Cole 2022-10-06 17:53:40 -07:00
parent 34ab5c54b7
commit eca60d8f49
6 changed files with 71 additions and 138 deletions

View File

@ -1,5 +1,5 @@
use crate::gabi;
use crate::parse::Parse;
use crate::parse::{Endian, Parse};
use crate::utils::{read_u16, read_u32, read_u64};
/// Encapsulates the contents of the ELF File Header
@ -88,6 +88,14 @@ impl FileHeader {
)));
}
// Verify endianness is something we know how to parse
let endian = buf[gabi::EI_DATA];
if endian != gabi::ELFDATA2LSB && endian != gabi::ELFDATA2MSB {
return Err(crate::ParseError(format!(
"Unsupported ELF Endianness: {endian:?}"
)))
}
return Ok(());
}
}
@ -101,7 +109,7 @@ where
Self::parse_ident(reader, &mut ident)?;
let class = Class(ident[gabi::EI_CLASS]);
let endian = Endian(ident[gabi::EI_DATA]);
let endian = if ident[gabi::EI_DATA] == gabi::ELFDATA2LSB { Endian::Little } else { Endian::Big };
let elftype = ObjectFileType(read_u16(endian, reader)?);
let arch = Architecture(read_u16(endian, reader)?);
let version = read_u32(endian, reader)?;
@ -189,28 +197,6 @@ impl std::fmt::Display for Class {
}
}
/// Represents the ELF file data format (little-endian vs big-endian)
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Endian(pub u8);
impl std::fmt::Debug for Endian {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl std::fmt::Display for Endian {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let str = match self.0 {
gabi::ELFDATANONE => "Invalid",
gabi::ELFDATA2LSB => "2's complement, little endian",
gabi::ELFDATA2MSB => "2's complement, big endian",
_ => "Unknown",
};
write!(f, "{}", str)
}
}
/// Represents the ELF file OS ABI
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct OSABI(pub u8);
@ -666,14 +652,14 @@ mod tests {
let slice: &mut [u8] = data.as_mut_slice();
assert_eq!(
FileHeader::parse(
Endian(gabi::ELFDATANONE),
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
.unwrap(),
FileHeader {
class: Class(gabi::ELFCLASS32),
endianness: Endian(gabi::ELFDATA2LSB),
endianness: Endian::Little,
version: 0x7060504,
osabi: OSABI(gabi::ELFOSABI_LINUX),
abiversion: 7,
@ -718,7 +704,7 @@ mod tests {
for n in 0..36 {
let slice = data.as_mut_slice().split_at(gabi::EI_NIDENT + n).0;
assert!(FileHeader::parse(
Endian(gabi::ELFDATANONE),
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
@ -754,14 +740,14 @@ mod tests {
let slice: &mut [u8] = data.as_mut_slice();
assert_eq!(
FileHeader::parse(
Endian(gabi::ELFDATANONE),
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
.unwrap(),
FileHeader {
class: Class(gabi::ELFCLASS64),
endianness: Endian(gabi::ELFDATA2MSB),
endianness: Endian::Big,
version: 0x04050607,
osabi: OSABI(gabi::ELFOSABI_LINUX),
abiversion: 7,
@ -806,7 +792,7 @@ mod tests {
for n in 0..48 {
let slice = data.as_mut_slice().split_at(gabi::EI_NIDENT + n).0;
assert!(FileHeader::parse(
Endian(gabi::ELFDATANONE),
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)

View File

@ -10,7 +10,7 @@ pub mod section;
pub mod symbol;
pub mod parse;
use crate::parse::Parse;
use crate::parse::{Endian, Parse};
mod utils;
@ -73,7 +73,7 @@ impl File {
}
pub fn open_stream<T: Read + Seek>(io_file: &mut T) -> Result<File, ParseError> {
let ehdr = file::FileHeader::parse(file::Endian(gabi::ELFDATANONE), file::Class(gabi::ELFCLASSNONE), io_file)?;
let ehdr = file::FileHeader::parse(Endian::Little, file::Class(gabi::ELFCLASSNONE), io_file)?;
// Parse the program headers
io_file.seek(io::SeekFrom::Start(ehdr.e_phoff))?;

View File

@ -1,4 +1,21 @@
use crate::file::{Class, Endian};
use crate::file::Class;
/// Represents the ELF file data format (little-endian vs big-endian)
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Endian {
Little,
Big
}
impl std::fmt::Display for Endian {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let str = match self {
Endian::Little => "2's complement, little endian",
Endian::Big => "2's complement, big endian",
};
write!(f, "{}", str)
}
}
pub trait Parse<R>: Copy {
fn parse(endian: Endian, class: Class, reader: &mut R) -> Result<Self, crate::ParseError>

View File

@ -1,6 +1,6 @@
use crate::file::{Class, Endian};
use crate::file::Class;
use crate::gabi;
use crate::parse::Parse;
use crate::parse::{Endian, Parse};
use crate::utils::{read_u32, read_u64};
/// Encapsulates the contents of an ELF Section Header
@ -134,9 +134,9 @@ impl std::fmt::Display for SectionFlag {
#[cfg(test)]
mod tests {
use crate::file::{Class, Endian};
use crate::file::Class;
use crate::gabi;
use crate::parse::Parse;
use crate::parse::{Endian, Parse};
use crate::section::{SectionFlag, SectionHeader, SectionType};
#[test]
@ -145,7 +145,7 @@ mod tests {
for n in 0..40 {
let slice = data.split_at(n).0;
assert!(SectionHeader::parse(
Endian(gabi::ELFDATA2LSB),
Endian::Little,
Class(gabi::ELFCLASS32),
&mut slice.as_ref()
)
@ -162,7 +162,7 @@ mod tests {
assert_eq!(
SectionHeader::parse(
Endian(gabi::ELFDATA2LSB),
Endian::Little,
Class(gabi::ELFCLASS32),
&mut data.as_ref()
)
@ -188,7 +188,7 @@ mod tests {
for n in 0..64 {
let slice = data.split_at(n).0;
assert!(SectionHeader::parse(
Endian(gabi::ELFDATA2LSB),
Endian::Big,
Class(gabi::ELFCLASS64),
&mut slice.as_ref()
)
@ -205,7 +205,7 @@ mod tests {
assert_eq!(
SectionHeader::parse(
Endian(gabi::ELFDATA2MSB),
Endian::Big,
Class(gabi::ELFCLASS64),
&mut data.as_ref()
)

View File

@ -1,6 +1,6 @@
use crate::file::{Class, Endian};
use crate::file::Class;
use crate::gabi;
use crate::parse::Parse;
use crate::parse::{Endian, Parse};
use crate::utils::{read_u32, read_u64};
/// Encapsulates the contents of an ELF Program Header
@ -144,9 +144,9 @@ impl std::fmt::Display for ProgType {
#[cfg(test)]
mod tests {
use crate::file::{Class, Endian};
use crate::file::Class;
use crate::gabi;
use crate::parse::Parse;
use crate::parse::{Endian, Parse};
use crate::segment::{ProgFlag, ProgType, ProgramHeader};
#[test]
@ -155,7 +155,7 @@ mod tests {
for n in 0..32 {
let slice = data.split_at(n).0;
assert!(ProgramHeader::parse(
Endian(gabi::ELFDATA2LSB),
Endian::Little,
Class(gabi::ELFCLASS32),
&mut slice.as_ref()
)
@ -172,7 +172,7 @@ mod tests {
assert_eq!(
ProgramHeader::parse(
Endian(gabi::ELFDATA2LSB),
Endian::Little,
Class(gabi::ELFCLASS32),
&mut data.as_ref()
)
@ -196,7 +196,7 @@ mod tests {
for n in 0..56 {
let slice = data.split_at(n).0;
assert!(ProgramHeader::parse(
Endian(gabi::ELFDATA2LSB),
Endian::Big,
Class(gabi::ELFCLASS64),
&mut slice.as_ref()
)
@ -213,7 +213,7 @@ mod tests {
assert_eq!(
ProgramHeader::parse(
Endian(gabi::ELFDATA2MSB),
Endian::Big,
Class(gabi::ELFCLASS64),
&mut data.as_ref()
)

View File

@ -1,5 +1,4 @@
use crate::file::Endian;
use crate::gabi;
use crate::parse::Endian;
use std::io;
use crate::ParseError;
@ -7,15 +6,9 @@ use crate::ParseError;
pub fn read_u16<T: io::Read>(endian: Endian, io: &mut T) -> Result<u16, ParseError> {
let mut buf = [0u8; 2];
io.read_exact(&mut buf)?;
match endian.0 {
gabi::ELFDATA2LSB => Ok(u16::from_le_bytes(buf)),
gabi::ELFDATA2MSB => Ok(u16::from_be_bytes(buf)),
gabi::ELFDATANONE => {
return Err(ParseError(format!("Unsupported Endianness: {endian}")));
}
_ => {
return Err(ParseError(format!("Unsupported Endianness: {endian}")));
}
match endian {
Endian::Little => Ok(u16::from_le_bytes(buf)),
Endian::Big => Ok(u16::from_be_bytes(buf)),
}
}
@ -23,15 +16,9 @@ pub fn read_u16<T: io::Read>(endian: Endian, io: &mut T) -> Result<u16, ParseErr
pub fn read_u32<T: io::Read>(endian: Endian, io: &mut T) -> Result<u32, ParseError> {
let mut buf = [0u8; 4];
io.read_exact(&mut buf)?;
match endian.0 {
gabi::ELFDATA2LSB => Ok(u32::from_le_bytes(buf)),
gabi::ELFDATA2MSB => Ok(u32::from_be_bytes(buf)),
gabi::ELFDATANONE => {
return Err(ParseError(format!("Unsupported Endianness: {endian}")));
}
_ => {
return Err(ParseError(format!("Unsupported Endianness: {endian}")));
}
match endian {
Endian::Little => Ok(u32::from_le_bytes(buf)),
Endian::Big => Ok(u32::from_be_bytes(buf)),
}
}
@ -39,15 +26,9 @@ pub fn read_u32<T: io::Read>(endian: Endian, io: &mut T) -> Result<u32, ParseErr
pub fn read_u64<T: io::Read>(endian: Endian, io: &mut T) -> Result<u64, ParseError> {
let mut buf = [0u8; 8];
io.read_exact(&mut buf)?;
match endian.0 {
gabi::ELFDATA2LSB => Ok(u64::from_le_bytes(buf)),
gabi::ELFDATA2MSB => Ok(u64::from_be_bytes(buf)),
gabi::ELFDATANONE => {
return Err(ParseError(format!("Unsupported Endianness: {endian}")));
}
_ => {
return Err(ParseError(format!("Unsupported Endianness: {endian}")));
}
match endian {
Endian::Little => Ok(u64::from_le_bytes(buf)),
Endian::Big => Ok(u64::from_be_bytes(buf)),
}
}
@ -71,78 +52,45 @@ pub fn get_string(data: &[u8], start: usize) -> Result<String, std::string::From
mod tests {
use super::*;
const NONE: Endian = Endian(gabi::ELFDATANONE);
const LITTLE: Endian = Endian(gabi::ELFDATA2LSB);
const BIG: Endian = Endian(gabi::ELFDATA2MSB);
const INVALID: Endian = Endian(42);
#[test]
fn test_read_u16_lsb() {
let data = [0x10u8, 0x20u8];
let result = read_u16(LITTLE, &mut data.as_ref()).unwrap();
let result = read_u16(Endian::Little, &mut data.as_ref()).unwrap();
assert_eq!(result, 0x2010u16);
}
#[test]
fn test_read_u16_msb() {
let data = [0x10u8, 0x20u8];
let result = read_u16(BIG, &mut data.as_ref()).unwrap();
let result = read_u16(Endian::Big, &mut data.as_ref()).unwrap();
assert_eq!(result, 0x1020u16);
}
#[test]
fn test_read_u16_none() {
let data = [0x10u8, 0x20u8];
let result: Result<u16, ParseError> = read_u16(NONE, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u16_invalid_endianness() {
let data = [0x10u8, 0x20u8];
let result: Result<u16, ParseError> = read_u16(INVALID, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u16_too_short() {
let data = [0x10u8];
let result: Result<u16, ParseError> = read_u16(LITTLE, &mut data.as_ref());
let result: Result<u16, ParseError> = read_u16(Endian::Little, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u32_lsb() {
let data = [0x10u8, 0x20u8, 0x30u8, 0x40u8];
let result = read_u32(LITTLE, &mut data.as_ref()).unwrap();
let result = read_u32(Endian::Little, &mut data.as_ref()).unwrap();
assert_eq!(result, 0x40302010u32);
}
#[test]
fn test_read_u32_msb() {
let data = [0x10u8, 0x20u8, 0x30u8, 0x40u8];
let result = read_u32(BIG, &mut data.as_ref()).unwrap();
let result = read_u32(Endian::Big, &mut data.as_ref()).unwrap();
assert_eq!(result, 0x10203040u32);
}
#[test]
fn test_read_u32_none() {
let data = [0x10u8, 0x20u8, 0x30u8, 0x40u8];
let result = read_u32(NONE, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u32_invalid_endianness() {
let data = [0x10u8, 0x20u8, 0x30u8, 0x40u8];
let result: Result<u32, ParseError> = read_u32(INVALID, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u32_too_short() {
let data = [0x10u8, 0x20u8];
let result: Result<u32, ParseError> = read_u32(LITTLE, &mut data.as_ref());
let result: Result<u32, ParseError> = read_u32(Endian::Little, &mut data.as_ref());
assert!(result.is_err());
}
@ -151,7 +99,7 @@ mod tests {
let data = [
0x10u8, 0x20u8, 0x30u8, 0x40u8, 0x50u8, 0x60u8, 0x70u8, 0x80u8,
];
let result = read_u64(LITTLE, &mut data.as_ref()).unwrap();
let result = read_u64(Endian::Little, &mut data.as_ref()).unwrap();
assert_eq!(result, 0x8070605040302010u64);
}
@ -160,32 +108,14 @@ mod tests {
let data = [
0x10u8, 0x20u8, 0x30u8, 0x40u8, 0x50u8, 0x60u8, 0x70u8, 0x80u8,
];
let result = read_u64(BIG, &mut data.as_ref()).unwrap();
let result = read_u64(Endian::Big, &mut data.as_ref()).unwrap();
assert_eq!(result, 0x1020304050607080u64);
}
#[test]
fn test_read_u64_none() {
let data = [
0x10u8, 0x20u8, 0x30u8, 0x40u8, 0x50u8, 0x60u8, 0x70u8, 0x80u8,
];
let result: Result<u64, ParseError> = read_u64(NONE, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u64_invalid_endianness() {
let data = [
0x10u8, 0x20u8, 0x30u8, 0x40u8, 0x50u8, 0x60u8, 0x70u8, 0x80u8,
];
let result: Result<u64, ParseError> = read_u64(INVALID, &mut data.as_ref());
assert!(result.is_err());
}
#[test]
fn test_read_u64_too_short() {
let data = [0x10u8, 0x20u8];
let result: Result<u64, ParseError> = read_u64(LITTLE, &mut data.as_ref());
let result: Result<u64, ParseError> = read_u64(Endian::Little, &mut data.as_ref());
assert!(result.is_err());
}
}