Handle the case where ehdr.e_phnum > 0xffff
If the number of segments is greater than or equal to PN_XNUM (0xffff), e_phnum is set to PN_XNUM, and the actual number of program header table entries is contained in the sh_info field of the section header at index 0. The phnum.m68k.so is a sample object file that tests this code path but then actually only has 1 segment - it just indirects phnum through shdr0.
This commit is contained in:
parent
c5fba3b0ab
commit
2e05d70302
BIN
sample-objects/phnum.m68k.so
Normal file
BIN
sample-objects/phnum.m68k.so
Normal file
Binary file not shown.
@ -328,6 +328,13 @@ pub const EV_NONE: u8 = 0;
|
|||||||
/// Current version
|
/// Current version
|
||||||
pub const EV_CURRENT: u8 = 1;
|
pub const EV_CURRENT: u8 = 1;
|
||||||
|
|
||||||
|
/// If the number of program headers is greater than or equal to PN_XNUM (0xffff),
|
||||||
|
/// this member has the value PN_XNUM (0xffff). The actual number of
|
||||||
|
/// program header table entries is contained in the sh_info field of the
|
||||||
|
/// section header at index 0. Otherwise, the sh_info member of the initial
|
||||||
|
/// section header entry contains the value zero.
|
||||||
|
pub const PN_XNUM: u16 = 0xffff;
|
||||||
|
|
||||||
/// PF_* define constants for the ELF Program Header's p_flags field.
|
/// PF_* define constants for the ELF Program Header's p_flags field.
|
||||||
/// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which
|
/// Represented as Elf32_Word in Elf32_Ehdr and Elf64_Word in Elf64_Ehdr which
|
||||||
/// are both 4-byte unsigned integers with 4-byte alignment
|
/// are both 4-byte unsigned integers with 4-byte alignment
|
||||||
|
@ -112,13 +112,32 @@ fn find_phdrs<'data, E: EndianParse>(
|
|||||||
ehdr: &FileHeader,
|
ehdr: &FileHeader,
|
||||||
data: &'data [u8],
|
data: &'data [u8],
|
||||||
) -> Result<Option<SegmentTable<'data, E>>, ParseError> {
|
) -> Result<Option<SegmentTable<'data, E>>, ParseError> {
|
||||||
match ehdr.get_phdrs_data_range()? {
|
// It's Ok to have no program headers
|
||||||
Some((start, end)) => {
|
if ehdr.e_phoff == 0 {
|
||||||
let buf = data.get_bytes(start..end)?;
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the number of segments is greater than or equal to PN_XNUM (0xffff),
|
||||||
|
// e_phnum is set to PN_XNUM, and the actual number of program header table
|
||||||
|
// entries is contained in the sh_info field of the section header at index 0.
|
||||||
|
let mut phnum = ehdr.e_phnum as usize;
|
||||||
|
if phnum == abi::PN_XNUM as usize {
|
||||||
|
let shoff: usize = ehdr.e_shoff.try_into()?;
|
||||||
|
let mut offset = shoff;
|
||||||
|
let shdr0 = SectionHeader::parse_at(endian, ehdr.class, &mut offset, data)?;
|
||||||
|
phnum = shdr0.sh_info.try_into()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate phentsize before trying to read the table so that we can error early for corrupted files
|
||||||
|
let entsize = ProgramHeader::validate_entsize(ehdr.class, ehdr.e_phentsize as usize)?;
|
||||||
|
|
||||||
|
let phoff: usize = ehdr.e_phoff.try_into()?;
|
||||||
|
let size = entsize
|
||||||
|
.checked_mul(phnum)
|
||||||
|
.ok_or(ParseError::IntegerOverflow)?;
|
||||||
|
let end = phoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
|
||||||
|
let buf = data.get_bytes(phoff..end)?;
|
||||||
Ok(Some(SegmentTable::new(endian, ehdr.class, buf)))
|
Ok(Some(SegmentTable::new(endian, ehdr.class, buf)))
|
||||||
}
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct collects the common sections found in ELF objects
|
/// This struct collects the common sections found in ELF objects
|
||||||
@ -858,6 +877,33 @@ mod interface_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn segments_phnum_in_shdr0() {
|
||||||
|
let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so");
|
||||||
|
let file_data = std::fs::read(path).expect("Could not read file.");
|
||||||
|
let slice = file_data.as_slice();
|
||||||
|
let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
|
||||||
|
|
||||||
|
let segments: Vec<ProgramHeader> = file
|
||||||
|
.segments()
|
||||||
|
.expect("File should have a segment table")
|
||||||
|
.iter()
|
||||||
|
.collect();
|
||||||
|
assert_eq!(
|
||||||
|
segments[0],
|
||||||
|
ProgramHeader {
|
||||||
|
p_type: abi::PT_PHDR,
|
||||||
|
p_offset: 92,
|
||||||
|
p_vaddr: 0,
|
||||||
|
p_paddr: 0,
|
||||||
|
p_filesz: 32,
|
||||||
|
p_memsz: 32,
|
||||||
|
p_flags: 0x20003,
|
||||||
|
p_align: 0x40000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn section_headers() {
|
fn section_headers() {
|
||||||
let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
|
let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
|
||||||
|
@ -80,15 +80,37 @@ fn parse_program_headers<E: EndianParse, S: Read + Seek>(
|
|||||||
ehdr: &FileHeader,
|
ehdr: &FileHeader,
|
||||||
reader: &mut CachingReader<S>,
|
reader: &mut CachingReader<S>,
|
||||||
) -> Result<Vec<ProgramHeader>, ParseError> {
|
) -> Result<Vec<ProgramHeader>, ParseError> {
|
||||||
match ehdr.get_phdrs_data_range()? {
|
// It's Ok to have no program headers
|
||||||
Some((start, end)) => {
|
if ehdr.e_phoff == 0 {
|
||||||
reader.load_bytes(start..end)?;
|
return Ok(Vec::default());
|
||||||
let buf = reader.get_bytes(start..end);
|
}
|
||||||
|
|
||||||
|
// If the number of segments is greater than or equal to PN_XNUM (0xffff),
|
||||||
|
// e_phnum is set to PN_XNUM, and the actual number of program header table
|
||||||
|
// entries is contained in the sh_info field of the section header at index 0.
|
||||||
|
let mut phnum = ehdr.e_phnum as usize;
|
||||||
|
if phnum == abi::PN_XNUM as usize {
|
||||||
|
let shoff: usize = ehdr.e_shoff.try_into()?;
|
||||||
|
let end = shoff
|
||||||
|
.checked_add(SectionHeader::size_for(ehdr.class))
|
||||||
|
.ok_or(ParseError::IntegerOverflow)?;
|
||||||
|
let data = reader.read_bytes(shoff, end)?;
|
||||||
|
let mut offset = 0;
|
||||||
|
let shdr0 = SectionHeader::parse_at(endian, ehdr.class, &mut offset, data)?;
|
||||||
|
phnum = shdr0.sh_info.try_into()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate phentsize before trying to read the table so that we can error early for corrupted files
|
||||||
|
let entsize = ProgramHeader::validate_entsize(ehdr.class, ehdr.e_phentsize as usize)?;
|
||||||
|
|
||||||
|
let phoff: usize = ehdr.e_phoff.try_into()?;
|
||||||
|
let size = entsize
|
||||||
|
.checked_mul(phnum)
|
||||||
|
.ok_or(ParseError::IntegerOverflow)?;
|
||||||
|
let end = phoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
|
||||||
|
let buf = reader.read_bytes(phoff, end)?;
|
||||||
let phdrs_vec = SegmentTable::new(endian, ehdr.class, buf).iter().collect();
|
let phdrs_vec = SegmentTable::new(endian, ehdr.class, buf).iter().collect();
|
||||||
Ok(phdrs_vec)
|
Ok(phdrs_vec)
|
||||||
}
|
|
||||||
None => Ok(Vec::default()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EndianParse, S: std::io::Read + std::io::Seek> ElfStream<E, S> {
|
impl<E: EndianParse, S: std::io::Read + std::io::Seek> ElfStream<E, S> {
|
||||||
@ -825,6 +847,27 @@ mod interface_tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn segments_phnum_in_shdr0() {
|
||||||
|
let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so");
|
||||||
|
let io = std::fs::File::open(path).expect("Could not open file.");
|
||||||
|
let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
file.segments()[0],
|
||||||
|
ProgramHeader {
|
||||||
|
p_type: abi::PT_PHDR,
|
||||||
|
p_offset: 92,
|
||||||
|
p_vaddr: 0,
|
||||||
|
p_paddr: 0,
|
||||||
|
p_filesz: 32,
|
||||||
|
p_memsz: 32,
|
||||||
|
p_flags: 0x20003,
|
||||||
|
p_align: 0x40000,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn symbol_table() {
|
fn symbol_table() {
|
||||||
let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
|
let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
|
||||||
|
25
src/file.rs
25
src/file.rs
@ -1,8 +1,7 @@
|
|||||||
//! Parsing the ELF File Header
|
//! Parsing the ELF File Header
|
||||||
use crate::abi;
|
use crate::abi;
|
||||||
use crate::endian::{AnyEndian, EndianParse};
|
use crate::endian::{AnyEndian, EndianParse};
|
||||||
use crate::parse::{ParseAt, ParseError};
|
use crate::parse::ParseError;
|
||||||
use crate::segment::ProgramHeader;
|
|
||||||
|
|
||||||
/// Represents the ELF file data format (little-endian vs big-endian)
|
/// Represents the ELF file data format (little-endian vs big-endian)
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
@ -185,28 +184,6 @@ impl FileHeader {
|
|||||||
e_shstrndx,
|
e_shstrndx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the (start, end) range in bytes for where the ProgramHeader table resides in
|
|
||||||
/// the ELF file containing this FileHeader.
|
|
||||||
///
|
|
||||||
/// Returns Ok(None) if the file does not contain any ProgramHeaders.
|
|
||||||
/// Returns a ParseError if the range could not fit in the system's usize or encountered overflow
|
|
||||||
pub(crate) fn get_phdrs_data_range(self) -> Result<Option<(usize, usize)>, ParseError> {
|
|
||||||
if self.e_phnum == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate ph entsize. We do this when calculating the range before so that we can error
|
|
||||||
// early for corrupted files.
|
|
||||||
let entsize = ProgramHeader::validate_entsize(self.class, self.e_phentsize as usize)?;
|
|
||||||
|
|
||||||
let start: usize = self.e_phoff.try_into()?;
|
|
||||||
let size = entsize
|
|
||||||
.checked_mul(self.e_phnum as usize)
|
|
||||||
.ok_or(ParseError::IntegerOverflow)?;
|
|
||||||
let end = start.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
|
|
||||||
Ok(Some((start, end)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user