Skip to content
This repository was archived by the owner on Jan 10, 2025. It is now read-only.

Commit 03e267b

Browse files
committed
Adds Executable::load_with_strict_parser().
1 parent 273e9af commit 03e267b

File tree

3 files changed

+207
-2
lines changed

3 files changed

+207
-2
lines changed

src/elf.rs

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,11 +391,211 @@ impl<C: ContextObject> Executable<C> {
391391
return Err(ElfError::UnsupportedSBPFVersion);
392392
}
393393

394-
let mut executable = Self::load_with_lenient_parser(bytes, loader)?;
394+
let mut executable = if sbpf_version.enable_stricter_elf_headers() {
395+
Self::load_with_strict_parser(bytes, loader)?
396+
} else {
397+
Self::load_with_lenient_parser(bytes, loader)?
398+
};
395399
executable.sbpf_version = sbpf_version;
396400
Ok(executable)
397401
}
398402

403+
/// Loads an ELF without relocation
404+
fn load_with_strict_parser(
405+
bytes: &[u8],
406+
loader: Arc<BuiltinProgram<C>>,
407+
) -> Result<Self, ElfParserError> {
408+
use crate::elf_parser::{
409+
consts::{
410+
ELFMAG, EV_CURRENT, PF_R, PF_W, PF_X, PT_GNU_STACK, PT_LOAD, SHN_UNDEF, STT_FUNC,
411+
},
412+
types::{Elf64Ehdr, Elf64Shdr, Elf64Sym},
413+
};
414+
415+
let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
416+
let elf_bytes = aligned_memory.as_slice();
417+
418+
let (file_header_range, file_header) = Elf64::parse_file_header(elf_bytes)?;
419+
let program_header_table_range = mem::size_of::<Elf64Ehdr>()
420+
..mem::size_of::<Elf64Phdr>()
421+
.saturating_mul(file_header.e_phnum as usize)
422+
.saturating_add(mem::size_of::<Elf64Ehdr>());
423+
if file_header.e_ident.ei_mag != ELFMAG
424+
|| file_header.e_ident.ei_class != ELFCLASS64
425+
|| file_header.e_ident.ei_data != ELFDATA2LSB
426+
|| file_header.e_ident.ei_version != EV_CURRENT as u8
427+
|| file_header.e_ident.ei_osabi != ELFOSABI_NONE
428+
|| file_header.e_ident.ei_abiversion != 0x00
429+
|| file_header.e_ident.ei_pad != [0x00; 7]
430+
|| file_header.e_type != ET_DYN
431+
|| file_header.e_machine != EM_SBPF
432+
|| file_header.e_version != EV_CURRENT
433+
// file_header.e_entry
434+
|| file_header.e_phoff != mem::size_of::<Elf64Ehdr>() as u64
435+
// file_header.e_shoff
436+
// file_header.e_flags
437+
|| file_header.e_ehsize != mem::size_of::<Elf64Ehdr>() as u16
438+
|| file_header.e_phentsize != mem::size_of::<Elf64Phdr>() as u16
439+
|| file_header.e_phnum < EXPECTED_PROGRAM_HEADERS.len() as u16
440+
|| program_header_table_range.end >= elf_bytes.len()
441+
|| file_header.e_shentsize != mem::size_of::<Elf64Shdr>() as u16
442+
// file_header.e_shnum
443+
|| file_header.e_shstrndx >= file_header.e_shnum
444+
{
445+
return Err(ElfParserError::InvalidFileHeader);
446+
}
447+
448+
const EXPECTED_PROGRAM_HEADERS: [(u32, u32, u64); 5] = [
449+
(PT_LOAD, PF_R | PF_X, ebpf::MM_BYTECODE_START), // byte code
450+
(PT_LOAD, PF_R, ebpf::MM_RODATA_START), // read only data
451+
(PT_GNU_STACK, PF_R | PF_W, ebpf::MM_STACK_START), // stack
452+
(PT_LOAD, PF_R | PF_W, ebpf::MM_HEAP_START), // heap
453+
(PT_LOAD, PF_R, 0xFFFFFFFF00000000), // dynamic symbol table
454+
];
455+
let program_header_table =
456+
Elf64::slice_from_bytes::<Elf64Phdr>(elf_bytes, program_header_table_range.clone())?;
457+
for (program_header, (p_type, p_flags, p_vaddr)) in program_header_table
458+
.iter()
459+
.zip(EXPECTED_PROGRAM_HEADERS.iter())
460+
{
461+
let p_filesz = if (*p_flags & PF_W) != 0 {
462+
0
463+
} else {
464+
program_header.p_memsz
465+
};
466+
if program_header.p_type != *p_type
467+
|| program_header.p_flags != *p_flags
468+
|| program_header.p_offset < program_header_table_range.end as u64
469+
|| program_header.p_offset >= elf_bytes.len() as u64
470+
|| program_header.p_offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
471+
|| program_header.p_vaddr != *p_vaddr
472+
|| program_header.p_paddr != *p_vaddr
473+
|| program_header.p_filesz != p_filesz
474+
|| program_header.p_filesz
475+
> (elf_bytes.len() as u64).saturating_sub(program_header.p_offset)
476+
|| program_header.p_memsz >= ebpf::MM_REGION_SIZE
477+
{
478+
return Err(ElfParserError::InvalidProgramHeader);
479+
}
480+
}
481+
482+
let config = loader.get_config();
483+
let symbol_names_section_header = if config.enable_symbol_and_section_labels {
484+
let (_section_header_table_range, section_header_table) =
485+
Elf64::parse_section_header_table(
486+
elf_bytes,
487+
file_header_range.clone(),
488+
file_header,
489+
program_header_table_range.clone(),
490+
)?;
491+
let section_names_section_header = (file_header.e_shstrndx != SHN_UNDEF)
492+
.then(|| {
493+
section_header_table
494+
.get(file_header.e_shstrndx as usize)
495+
.ok_or(ElfParserError::OutOfBounds)
496+
})
497+
.transpose()?
498+
.ok_or(ElfParserError::NoSectionNameStringTable)?;
499+
let mut symbol_names_section_header = None;
500+
for section_header in section_header_table.iter() {
501+
let section_name = Elf64::get_string_in_section(
502+
elf_bytes,
503+
section_names_section_header,
504+
section_header.sh_name,
505+
64,
506+
)?;
507+
if section_name == b".strtab" {
508+
symbol_names_section_header = Some(section_header);
509+
}
510+
}
511+
symbol_names_section_header
512+
} else {
513+
None
514+
};
515+
let bytecode_header = &program_header_table[0];
516+
let rodata_header = &program_header_table[1];
517+
let dynamic_symbol_table: &[Elf64Sym] =
518+
Elf64::slice_from_program_header(elf_bytes, &program_header_table[4])?;
519+
let mut function_registry = FunctionRegistry::<usize>::default();
520+
let mut expected_symbol_address = bytecode_header.p_vaddr;
521+
for symbol in dynamic_symbol_table {
522+
if symbol.st_info & STT_FUNC == 0 {
523+
continue;
524+
}
525+
if symbol.st_value != expected_symbol_address {
526+
return Err(ElfParserError::OutOfBounds);
527+
}
528+
if symbol.st_size == 0 || symbol.st_size.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
529+
{
530+
return Err(ElfParserError::InvalidSize);
531+
}
532+
if symbol.st_size
533+
> bytecode_header
534+
.vm_range()
535+
.end
536+
.saturating_sub(symbol.st_value)
537+
{
538+
return Err(ElfParserError::OutOfBounds);
539+
}
540+
let target_pc = symbol
541+
.st_value
542+
.saturating_sub(bytecode_header.p_vaddr)
543+
.checked_div(ebpf::INSN_SIZE as u64)
544+
.unwrap_or_default() as usize;
545+
let name = if config.enable_symbol_and_section_labels {
546+
Elf64::get_string_in_section(
547+
elf_bytes,
548+
symbol_names_section_header
549+
.as_ref()
550+
.ok_or(ElfParserError::NoStringTable)?,
551+
symbol.st_name as Elf64Word,
552+
1024, // SYMBOL_NAME_LENGTH_MAXIMUM,
553+
)?
554+
} else {
555+
&[]
556+
};
557+
function_registry
558+
.register_function(target_pc as u32, name, target_pc)
559+
.unwrap();
560+
expected_symbol_address = symbol.st_value.saturating_add(symbol.st_size);
561+
}
562+
if expected_symbol_address != bytecode_header.vm_range().end {
563+
return Err(ElfParserError::OutOfBounds);
564+
}
565+
if !bytecode_header.vm_range().contains(&file_header.e_entry)
566+
|| file_header.e_entry.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
567+
{
568+
return Err(ElfParserError::InvalidFileHeader);
569+
}
570+
let entry_pc = file_header
571+
.e_entry
572+
.saturating_sub(bytecode_header.p_vaddr)
573+
.checked_div(ebpf::INSN_SIZE as u64)
574+
.unwrap_or_default() as usize;
575+
if function_registry.lookup_by_key(entry_pc as u32).is_none() {
576+
return Err(ElfParserError::InvalidFileHeader);
577+
}
578+
579+
let text_section_vaddr = bytecode_header.p_vaddr;
580+
let text_section_range = bytecode_header.file_range().unwrap_or_default();
581+
let ro_section = Section::Borrowed(
582+
rodata_header.p_vaddr as usize,
583+
rodata_header.file_range().unwrap_or_default(),
584+
);
585+
Ok(Self {
586+
elf_bytes: aligned_memory,
587+
sbpf_version: SBPFVersion::Reserved, // Is set in Self::load()
588+
ro_section,
589+
text_section_vaddr,
590+
text_section_range,
591+
entry_pc,
592+
function_registry,
593+
loader,
594+
#[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
595+
compiled_program: None,
596+
})
597+
}
598+
399599
/// Loads an ELF with relocation
400600
fn load_with_lenient_parser(
401601
bytes: &[u8],

src/elf_parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ impl<'a> Elf64<'a> {
573573
}
574574

575575
/// Returns the `&[T]` contained at `bytes[range]`
576-
fn slice_from_bytes<T: 'static>(
576+
pub fn slice_from_bytes<T: 'static>(
577577
bytes: &[u8],
578578
range: Range<usize>,
579579
) -> Result<&[T], ElfParserError> {

src/program.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ impl SBPFVersion {
8585
pub fn move_memory_instruction_classes(self) -> bool {
8686
self != SBPFVersion::V1
8787
}
88+
89+
/// Constrain ELF format to ignore section headers and relocations
90+
pub fn enable_stricter_elf_headers(self) -> bool {
91+
self != SBPFVersion::V1
92+
}
8893
}
8994

9095
/// Holds the function symbols of an Executable

0 commit comments

Comments
 (0)