Skip to content

Commit

Permalink
Fix fuzz test crashes
Browse files Browse the repository at this point in the history
1. cleaning fuzz test crashes by removing some unwrap(), add slice boundary checks and using saturating_* functions.
2. reorganize some structs. elf_header, program_header and section_header mods are now standalone raw data structure, while the counterparts in elf mod contains reference for the whole ELF file for section content retrieval.
3. bump version to 0.3.0 as API changes.
  • Loading branch information
vincenthouyi committed Jan 16, 2023
1 parent e7aaaaf commit 3ec9935
Show file tree
Hide file tree
Showing 15 changed files with 312 additions and 286 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "elf_rs"
version = "0.2.0"
version = "0.3.0"
authors = ["Vincent Hou <vincent.houyi@gmail.com>"]
edition = "2018"
description = "A simple no_std ELF file reader for ELF32 and ELF64"
Expand All @@ -13,5 +13,5 @@ license-file = "LICENSE"
travis-ci = { repository = "vincenthouyi/elf_rs", branch = "master" }

[dependencies]
bitflags = "1.0.4"
bitflags = "1.3"
num-traits = { version = "0.2", default-features = false }
12 changes: 7 additions & 5 deletions examples/readelf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::io::Read;

use elf_rs::*;

fn read_elf(filename: &String) -> Result<(), ()> {
fn read_elf(filename: &str) -> Result<(), ()> {
let mut elf_file = File::open(filename).map_err(|e| {
println!("failed to open file {}: {}", filename, e);
()
Expand All @@ -23,18 +23,20 @@ fn read_elf(filename: &String) -> Result<(), ()> {
()
})?;

println!("{:?} header: {:?}", elf, elf.elf_header());
println!("{:#x?}", elf);

println!("{:#?}", elf.elf_header());

for p in elf.program_header_iter() {
println!("{:x?}", p);
println!("{:#x?}", p);
}

for s in elf.section_header_iter() {
println!("{:x?}", s);
println!("{:#x?}", s);
}

if let Some(s) = elf.lookup_section(b".text") {
println!(".test section {:?}", s);
println!(".test section: {:#x?}", s);
}

Ok(())
Expand Down
116 changes: 64 additions & 52 deletions src/elf/elf.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
use super::ElfFile;
use crate::elf_header::{ElfHeader, ElfHeader32, ElfHeader64, ElfHeaderWrapper};
use crate::program_header::{
ProgramHeader, ProgramHeader32, ProgramHeader64, ProgramHeaderIter, ProgramHeaderWrapper,
use super::{
ElfFile, ElfHeader, ElfType, ProgramHeaderEntry, ProgramHeaderIter, SectionHeaderEntry,
SectionHeaderIter,
};
use crate::section_header::{
SectionHeader, SectionHeader32, SectionHeader64, SectionHeaderIter, SectionHeaderWrapper,
};
use core::fmt::{Debug, Error, Formatter};
use crate::elf_header::{ElfClass, ElfHeader32, ElfHeader64, ELF_MAGIC};
use crate::program_header::{ProgramHeader32, ProgramHeader64};
use crate::section_header::{SectionHeader32, SectionHeader64};
use crate::Error;
use core::fmt;
use core::marker::PhantomData;
use core::mem::size_of;
use core::slice::from_raw_parts;

pub trait ElfType {
type ElfHeader: ElfHeader;
type ProgramHeader: ProgramHeader;
type SectionHeader: SectionHeader;
}

#[derive(Debug)]
pub enum ElfType64 {}

impl ElfType for ElfType64 {
type ElfHeader = ElfHeader64;
type ProgramHeader = ProgramHeader64;
type SectionHeader = SectionHeader64;

fn elf_class() -> ElfClass {
ElfClass::Elf64
}
}

#[derive(Debug)]
Expand All @@ -33,84 +31,97 @@ impl ElfType for ElfType32 {
type ElfHeader = ElfHeader32;
type ProgramHeader = ProgramHeader32;
type SectionHeader = SectionHeader32;

fn elf_class() -> ElfClass {
ElfClass::Elf32
}
}

pub struct ElfGen<'a, ET>(&'a [u8], PhantomData<ET>);
pub struct Elf<'a, ET>(&'a [u8], PhantomData<ET>);

impl<'a, ET: ElfType> ElfGen<'a, ET> {
pub fn new(buf: &'a [u8]) -> Self {
impl<'a, ET: ElfType> Elf<'a, ET> {
pub(crate) fn new(buf: &'a [u8]) -> Self {
Self(buf, PhantomData)
}

pub fn from_bytes(buf: &'a [u8]) -> Result<Self, crate::Error> {
pub fn from_bytes(buf: &'a [u8]) -> Result<Self, Error> {
if !buf.starts_with(&ELF_MAGIC) {
return Err(Error::InvalidMagic);
}

if buf.len() < size_of::<ET::ElfHeader>() {
return Err(crate::Error::BufferTooShort);
return Err(Error::BufferTooShort);
}
let elf = Self(buf, PhantomData);

let elf = Self::new(buf);
if buf.len() < elf.elf_header().elf_header_size() as usize {
Err(crate::Error::BufferTooShort)
} else {
Ok(elf)
return Err(Error::BufferTooShort);
}

if elf.elf_header().class() != ET::elf_class() {
return Err(Error::InvalidClass);
}

Ok(elf)
}

fn content(&self) -> &[u8] {
pub fn content(&self) -> &[u8] {
self.0
}

pub fn elf_header_raw(&self) -> &ET::ElfHeader {
unsafe { &*(self.content().as_ptr() as *const ET::ElfHeader) }
}

fn program_header_raw(&'a self) -> &'a [ET::ProgramHeader] {
pub fn program_headers_raw(&self) -> Option<&'a [ET::ProgramHeader]> {
let ph_off = self.elf_header().program_header_offset() as usize;
let ph_num = self.elf_header().program_header_entry_num() as usize;
unsafe {
let ph_ptr = self.content().as_ptr().add(ph_off);
from_raw_parts(ph_ptr as *const ET::ProgramHeader, ph_num)
}
let ph_top = ph_off.saturating_add(ph_num.saturating_mul(size_of::<ET::ProgramHeader>()));
self.content()
.get(ph_off..ph_top)
.map(|mem| unsafe { from_raw_parts(mem.as_ptr() as *const ET::ProgramHeader, ph_num) })
}

pub fn program_header_iter(&self) -> ProgramHeaderIter {
ProgramHeaderIter::new(self)
}

pub fn program_header_nth(&self, index: usize) -> Option<ProgramHeaderWrapper> {
self.program_header_raw()
.get(index)
.map(|ph| ProgramHeaderWrapper::new(self, ph))
pub fn program_header_nth(&self, index: usize) -> Option<ProgramHeaderEntry> {
self.program_headers_raw()
.and_then(|s| s.get(index))
.map(|ph| ProgramHeaderEntry::new(self, ph))
}

fn section_header_raw(&self) -> &[ET::SectionHeader] {
pub fn section_headers_raw(&'a self) -> Option<&'a [ET::SectionHeader]> {
let sh_off = self.elf_header().section_header_offset() as usize;
let sh_num = self.elf_header().section_header_entry_num() as usize;
unsafe {
let sh_ptr = self.content().as_ptr().add(sh_off);
from_raw_parts(sh_ptr as *const ET::SectionHeader, sh_num)
}
let sh_top = sh_off.saturating_add(sh_num.saturating_mul(size_of::<ET::SectionHeader>()));
self.content()
.get(sh_off..sh_top)
.map(|mem| unsafe { from_raw_parts(mem.as_ptr() as *const ET::SectionHeader, sh_num) })
}

pub fn section_header_iter(&self) -> SectionHeaderIter {
SectionHeaderIter::new(self)
}

pub fn section_header_nth(&self, index: usize) -> Option<SectionHeaderWrapper> {
self.section_header_raw()
.get(index)
.map(|sh| SectionHeaderWrapper::new(self, sh))
pub fn section_header_nth(&self, index: usize) -> Option<SectionHeaderEntry> {
self.section_headers_raw()
.and_then(|s| s.get(index))
.map(|sh| SectionHeaderEntry::new(self, sh))
}
}

impl<'a, ET: ElfType> ElfFile for ElfGen<'a, ET> {
impl<'a, ET: ElfType> ElfFile for Elf<'a, ET> {
fn content(&self) -> &[u8] {
self.content()
}

fn elf_header(&self) -> crate::elf_header::ElfHeaderWrapper {
ElfHeaderWrapper::new(self, self.elf_header_raw())
fn elf_header(&self) -> ElfHeader {
ElfHeader::new(self, self.elf_header_raw())
}

fn program_header_nth(&self, index: usize) -> Option<ProgramHeaderWrapper> {
fn program_header_nth(&self, index: usize) -> Option<ProgramHeaderEntry> {
self.program_header_nth(index)
}

Expand All @@ -122,18 +133,19 @@ impl<'a, ET: ElfType> ElfFile for ElfGen<'a, ET> {
self.section_header_iter()
}

fn section_header_nth(&self, index: usize) -> Option<SectionHeaderWrapper> {
fn section_header_nth(&self, index: usize) -> Option<SectionHeaderEntry> {
self.section_header_nth(index)
}
}

pub type Elf32<'a> = ElfGen<'a, ElfType32>;
pub type Elf64<'a> = ElfGen<'a, ElfType64>;
pub type Elf32<'a> = Elf<'a, ElfType32>;
pub type Elf64<'a> = Elf<'a, ElfType64>;

impl<'a, ET: ElfType> Debug for ElfGen<'a, ET> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
f.debug_struct("Elf File")
impl<'a, ET: ElfType> fmt::Debug for Elf<'a, ET> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ELF Buffer")
.field("Memory Location", &self.content().as_ptr())
.field("Buffer Size", &self.content().len())
.finish()
}
}
50 changes: 50 additions & 0 deletions src/elf/elf_header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::ElfFile;
use crate::elf_header::ElfHeaderRaw;
use core::fmt;
use core::ops;

pub struct ElfHeader<'a> {
_elf_file: &'a dyn ElfFile,
inner: &'a dyn ElfHeaderRaw,
}

impl<'a> ElfHeader<'a> {
pub fn new(elf_file: &'a dyn ElfFile, inner: &'a dyn ElfHeaderRaw) -> Self {
Self {
_elf_file: elf_file,
inner,
}
}
}

impl<'a> ops::Deref for ElfHeader<'a> {
type Target = dyn ElfHeaderRaw + 'a;
fn deref(&self) -> &Self::Target {
self.inner
}
}

impl<'a> fmt::Debug for ElfHeader<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ELF Header")
.field("Class", &self.class())
.field("Endianness", &self.endianness())
.field("ELF Header Version", &self.header_version())
.field("ABI", &self.abi())
.field("ABI Version", &self.abi_version())
.field("File Type", &self.elftype())
.field("Target Machine", &self.machine())
.field("ELF Version", &self.elf_version())
.field("Entry Point", &self.entry_point())
.field("Program Header Offset", &self.program_header_offset())
.field("Section Header Offset", &self.section_header_offset())
.field("Flags", &self.flags())
.field("ELF Header Size", &self.elf_header_size())
.field("Program Header Size", &self.program_header_entry_size())
.field("Program Header Number", &self.program_header_entry_num())
.field("Section Header Size", &self.section_header_entry_size())
.field("Section Header Number", &self.section_header_entry_num())
.field(".shstr Section Index", &self.shstr_index())
.finish()
}
}
33 changes: 23 additions & 10 deletions src/elf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,44 @@
use crate::elf_header::ElfHeaderWrapper;
use crate::program_header::{ProgramHeaderIter, ProgramHeaderWrapper};
use crate::section_header::{SectionHeaderIter, SectionHeaderWrapper};

mod elf;
pub use elf::{Elf32, Elf64};

mod elf_header;
pub use elf_header::ElfHeader;

mod program_header;
pub use program_header::{ProgramHeaderEntry, ProgramHeaderIter};

mod section_header;
pub use section_header::{SectionHeaderEntry, SectionHeaderIter};

pub trait ElfType {
type ElfHeader: crate::elf_header::ElfHeaderRaw;
type ProgramHeader: crate::program_header::ProgramHeaderRaw;
type SectionHeader: crate::section_header::SectionHeaderRaw;

fn elf_class() -> crate::elf_header::ElfClass;
}

pub trait ElfFile {
fn content(&self) -> &[u8];

fn elf_header(&self) -> ElfHeaderWrapper;
fn elf_header(&self) -> ElfHeader;

fn program_header_nth(&self, index: usize) -> Option<ProgramHeaderWrapper>;
fn program_header_nth(&self, index: usize) -> Option<ProgramHeaderEntry>;

fn program_header_iter(&self) -> ProgramHeaderIter;

fn section_header_nth(&self, index: usize) -> Option<SectionHeaderWrapper>;
fn section_header_nth(&self, index: usize) -> Option<SectionHeaderEntry>;

fn section_header_iter(&self) -> SectionHeaderIter;

fn shstr_section(&self) -> Option<SectionHeaderWrapper> {
fn shstr_section(&self) -> Option<SectionHeaderEntry> {
let shstr_index = self.elf_header().shstr_index() as usize;
self.section_header_nth(shstr_index)
}

fn lookup_section(&self, name: &[u8]) -> Option<SectionHeaderWrapper> {
fn lookup_section(&self, name: &[u8]) -> Option<SectionHeaderEntry> {
self.section_header_iter()
.find(|s| s.section_name() == name)
.find(|s| s.section_name() == Some(name))
}

fn entry_point(&self) -> u64 {
Expand Down

0 comments on commit 3ec9935

Please sign in to comment.