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:
parent
34ab5c54b7
commit
eca60d8f49
46
src/file.rs
46
src/file.rs
@ -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()
|
||||
)
|
||||
|
@ -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))?;
|
||||
|
19
src/parse.rs
19
src/parse.rs
@ -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>
|
||||
|
@ -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()
|
||||
)
|
||||
|
@ -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()
|
||||
)
|
||||
|
108
src/utils.rs
108
src/utils.rs
@ -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());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user