1use crate::{
10 aligned_memory::{is_memory_aligned, AlignedMemory},
11 ebpf::{self, EF_SBPF_V2, HOST_ALIGN, INSN_SIZE},
12 elf_parser::{
13 consts::{
14 ELFCLASS64, ELFDATA2LSB, ELFOSABI_NONE, EM_BPF, EM_SBPF, ET_DYN, R_X86_64_32,
15 R_X86_64_64, R_X86_64_NONE, R_X86_64_RELATIVE,
16 },
17 types::{Elf64Phdr, Elf64Shdr, Elf64Word},
18 Elf64, ElfParserError,
19 },
20 error::EbpfError,
21 memory_region::MemoryRegion,
22 program::{BuiltinProgram, FunctionRegistry, SBPFVersion},
23 verifier::Verifier,
24 vm::{Config, ContextObject},
25};
26
27#[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
28use crate::jit::{JitCompiler, JitProgram};
29use byteorder::{ByteOrder, LittleEndian};
30use std::{collections::BTreeMap, fmt::Debug, mem, ops::Range, str};
31
32#[cfg(not(feature = "shuttle-test"))]
33use std::sync::Arc;
34
35#[cfg(feature = "shuttle-test")]
36use shuttle::sync::Arc;
37
38#[derive(Debug, thiserror::Error, PartialEq, Eq)]
40pub enum ElfError {
41 #[error("Failed to parse ELF file: {0}")]
43 FailedToParse(String),
44 #[error("Entrypoint out of bounds")]
46 EntrypointOutOfBounds,
47 #[error("Invalid entrypoint")]
49 InvalidEntrypoint,
50 #[error("Failed to get section {0}")]
52 FailedToGetSection(String),
53 #[error("Unresolved symbol ({0}) at instruction #{1:?} (ELF file offset {2:#x})")]
55 UnresolvedSymbol(String, usize, usize),
56 #[error("Section not found: {0}")]
58 SectionNotFound(String),
59 #[error("Relative jump out of bounds at instruction #{0}")]
61 RelativeJumpOutOfBounds(usize),
62 #[error("Symbol hash collision {0:#x}")]
64 SymbolHashCollision(u32),
65 #[error("Incompatible ELF: wrong endianess")]
67 WrongEndianess,
68 #[error("Incompatible ELF: wrong ABI")]
70 WrongAbi,
71 #[error("Incompatible ELF: wrong machine")]
73 WrongMachine,
74 #[error("Incompatible ELF: wrong class")]
76 WrongClass,
77 #[error("Multiple or no text sections, consider removing llc option: -function-sections")]
79 NotOneTextSection,
80 #[error("Found writable section ({0}) in ELF, read-write data not supported")]
82 WritableSectionNotSupported(String),
83 #[error("Relocation failed, no loadable section contains virtual address {0:#x}")]
85 AddressOutsideLoadableSection(u64),
86 #[error("Relocation failed, invalid referenced virtual address {0:#x}")]
88 InvalidVirtualAddress(u64),
89 #[error("Relocation failed, unknown type {0:?}")]
91 UnknownRelocation(u32),
92 #[error("Failed to read relocation info")]
94 FailedToReadRelocationInfo,
95 #[error("Incompatible ELF: wrong type")]
97 WrongType,
98 #[error("Unknown symbol with index {0}")]
100 UnknownSymbol(usize),
101 #[error("Offset or value is out of bounds")]
103 ValueOutOfBounds,
104 #[error("Detected sbpf_version required by the executable which are not enabled")]
106 UnsupportedSBPFVersion,
107 #[error("Invalid ELF program header")]
109 InvalidProgramHeader,
110}
111
112impl From<ElfParserError> for ElfError {
113 fn from(err: ElfParserError) -> Self {
114 match err {
115 ElfParserError::InvalidSectionHeader
116 | ElfParserError::InvalidString
117 | ElfParserError::InvalidSize
118 | ElfParserError::Overlap
119 | ElfParserError::SectionNotInOrder
120 | ElfParserError::NoSectionNameStringTable
121 | ElfParserError::InvalidDynamicSectionTable
122 | ElfParserError::InvalidRelocationTable
123 | ElfParserError::InvalidAlignment
124 | ElfParserError::NoStringTable
125 | ElfParserError::NoDynamicStringTable
126 | ElfParserError::InvalidFileHeader
127 | ElfParserError::StringTooLong(_, _) => ElfError::FailedToParse(err.to_string()),
128 ElfParserError::InvalidProgramHeader => ElfError::InvalidProgramHeader,
129 ElfParserError::OutOfBounds => ElfError::ValueOutOfBounds,
130 }
131 }
132}
133
134fn get_section(elf: &Elf64, name: &[u8]) -> Result<Elf64Shdr, ElfError> {
135 for section_header in elf.section_header_table() {
136 if elf.section_name(section_header.sh_name)? == name {
137 return Ok(section_header.clone());
138 }
139 }
140
141 Err(ElfError::SectionNotFound(
142 std::str::from_utf8(name)
143 .unwrap_or("UTF-8 error")
144 .to_string(),
145 ))
146}
147
148const BYTE_OFFSET_IMMEDIATE: usize = 4;
165const BYTE_LENGTH_IMMEDIATE: usize = 4;
167
168#[allow(non_camel_case_types)]
170#[derive(Debug, PartialEq, Copy, Clone)]
171enum BpfRelocationType {
172 R_Bpf_None = 0,
174 R_Bpf_64_64 = 1,
180 R_Bpf_64_Relative = 8,
190 R_Bpf_64_32 = 10,
203}
204impl BpfRelocationType {
205 fn from_x86_relocation_type(from: u32) -> Option<BpfRelocationType> {
206 match from {
207 R_X86_64_NONE => Some(BpfRelocationType::R_Bpf_None),
208 R_X86_64_64 => Some(BpfRelocationType::R_Bpf_64_64),
209 R_X86_64_RELATIVE => Some(BpfRelocationType::R_Bpf_64_Relative),
210 R_X86_64_32 => Some(BpfRelocationType::R_Bpf_64_32),
211 _ => None,
212 }
213 }
214}
215
216#[derive(Debug, PartialEq)]
217pub(crate) enum Section {
218 Owned(usize, Vec<u8>),
223 Borrowed(usize, Range<usize>),
229}
230
231#[derive(Debug, PartialEq)]
233pub struct Executable<C: ContextObject> {
234 elf_bytes: AlignedMemory<{ HOST_ALIGN }>,
236 sbpf_version: SBPFVersion,
238 ro_section: Section,
240 text_section_vaddr: u64,
242 text_section_range: Range<usize>,
244 entry_pc: usize,
246 function_registry: FunctionRegistry<usize>,
248 loader: Arc<BuiltinProgram<C>>,
250 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
252 compiled_program: Option<JitProgram>,
253}
254
255impl<C: ContextObject> Executable<C> {
256 pub fn get_config(&self) -> &Config {
258 self.loader.get_config()
259 }
260
261 pub fn get_sbpf_version(&self) -> SBPFVersion {
263 self.sbpf_version
264 }
265
266 pub fn get_text_bytes(&self) -> (u64, &[u8]) {
268 (
269 self.text_section_vaddr,
270 &self.elf_bytes.as_slice()[self.text_section_range.clone()],
271 )
272 }
273
274 pub fn get_ro_section(&self) -> &[u8] {
276 match &self.ro_section {
277 Section::Owned(_offset, data) => data.as_slice(),
278 Section::Borrowed(_offset, byte_range) => {
279 &self.elf_bytes.as_slice()[byte_range.clone()]
280 }
281 }
282 }
283
284 pub fn get_ro_region(&self) -> MemoryRegion {
286 get_ro_region(&self.ro_section, self.elf_bytes.as_slice())
287 }
288
289 pub fn get_entrypoint_instruction_offset(&self) -> usize {
291 self.entry_pc
292 }
293
294 #[cfg(feature = "debugger")]
296 pub fn get_text_section_offset(&self) -> u64 {
297 self.text_section_range.start as u64
298 }
299
300 pub fn get_loader(&self) -> &Arc<BuiltinProgram<C>> {
302 &self.loader
303 }
304
305 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
307 pub fn get_compiled_program(&self) -> Option<&JitProgram> {
308 self.compiled_program.as_ref()
309 }
310
311 pub fn verify<V: Verifier>(&self) -> Result<(), EbpfError> {
313 <V as Verifier>::verify(
314 self.get_text_bytes().1,
315 self.get_config(),
316 self.get_sbpf_version(),
317 self.get_function_registry(),
318 self.loader.get_function_registry(self.get_sbpf_version()),
319 )?;
320 Ok(())
321 }
322
323 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
325 pub fn jit_compile(&mut self) -> Result<(), crate::error::EbpfError> {
326 let jit = JitCompiler::<C>::new(self)?;
327 self.compiled_program = Some(jit.compile()?);
328 Ok(())
329 }
330
331 pub fn get_function_registry(&self) -> &FunctionRegistry<usize> {
333 &self.function_registry
334 }
335
336 pub fn new_from_text_bytes(
338 text_bytes: &[u8],
339 loader: Arc<BuiltinProgram<C>>,
340 sbpf_version: SBPFVersion,
341 mut function_registry: FunctionRegistry<usize>,
342 ) -> Result<Self, ElfError> {
343 let elf_bytes = AlignedMemory::from_slice(text_bytes);
344 let entry_pc = if let Some((_name, pc)) = function_registry.lookup_by_name(b"entrypoint") {
345 pc
346 } else {
347 function_registry.register_function_hashed_legacy(
348 &loader,
349 !sbpf_version.static_syscalls(),
350 *b"entrypoint",
351 0,
352 )?;
353 0
354 };
355 Ok(Self {
356 elf_bytes,
357 sbpf_version,
358 ro_section: Section::Borrowed(ebpf::MM_RODATA_START as usize, 0..text_bytes.len()),
359 text_section_vaddr: if sbpf_version.enable_lower_bytecode_vaddr() {
360 ebpf::MM_BYTECODE_START
361 } else {
362 ebpf::MM_RODATA_START
363 },
364 text_section_range: 0..text_bytes.len(),
365 entry_pc,
366 function_registry,
367 loader,
368 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
369 compiled_program: None,
370 })
371 }
372
373 pub fn load(bytes: &[u8], loader: Arc<BuiltinProgram<C>>) -> Result<Self, ElfError> {
375 const E_FLAGS_OFFSET: usize = 48;
376 let e_flags = LittleEndian::read_u32(
377 bytes
378 .get(E_FLAGS_OFFSET..E_FLAGS_OFFSET.saturating_add(std::mem::size_of::<u32>()))
379 .ok_or(ElfParserError::OutOfBounds)?,
380 );
381 let config = loader.get_config();
382 let sbpf_version = if config.enabled_sbpf_versions.end() == &SBPFVersion::V0 {
383 if e_flags == EF_SBPF_V2 {
384 SBPFVersion::Reserved
385 } else {
386 SBPFVersion::V0
387 }
388 } else {
389 match e_flags {
390 0 => SBPFVersion::V0,
391 1 => SBPFVersion::V1,
392 2 => SBPFVersion::V2,
393 3 => SBPFVersion::V3,
394 _ => SBPFVersion::Reserved,
395 }
396 };
397 if !config.enabled_sbpf_versions.contains(&sbpf_version) {
398 return Err(ElfError::UnsupportedSBPFVersion);
399 }
400
401 let mut executable = if sbpf_version.enable_stricter_elf_headers() {
402 Self::load_with_strict_parser(bytes, loader)?
403 } else {
404 Self::load_with_lenient_parser(bytes, loader)?
405 };
406 executable.sbpf_version = sbpf_version;
407 Ok(executable)
408 }
409
410 fn load_with_strict_parser(
412 bytes: &[u8],
413 loader: Arc<BuiltinProgram<C>>,
414 ) -> Result<Self, ElfParserError> {
415 use crate::elf_parser::{
416 consts::{
417 ELFMAG, EV_CURRENT, PF_R, PF_W, PF_X, PT_GNU_STACK, PT_LOAD, PT_NULL, SHN_UNDEF,
418 STT_FUNC,
419 },
420 types::{Elf64Ehdr, Elf64Shdr, Elf64Sym},
421 };
422
423 let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
424 let elf_bytes = aligned_memory.as_slice();
425
426 let (file_header_range, file_header) = Elf64::parse_file_header(elf_bytes)?;
427 let program_header_table_range = mem::size_of::<Elf64Ehdr>()
428 ..mem::size_of::<Elf64Phdr>()
429 .saturating_mul(file_header.e_phnum as usize)
430 .saturating_add(mem::size_of::<Elf64Ehdr>());
431 if file_header.e_ident.ei_mag != ELFMAG
432 || file_header.e_ident.ei_class != ELFCLASS64
433 || file_header.e_ident.ei_data != ELFDATA2LSB
434 || file_header.e_ident.ei_version != EV_CURRENT as u8
435 || file_header.e_ident.ei_osabi != ELFOSABI_NONE
436 || file_header.e_ident.ei_abiversion != 0x00
437 || file_header.e_ident.ei_pad != [0x00; 7]
438 || file_header.e_type != ET_DYN
439 || file_header.e_machine != EM_SBPF
440 || file_header.e_version != EV_CURRENT
441 || file_header.e_phoff != mem::size_of::<Elf64Ehdr>() as u64
443 || file_header.e_ehsize != mem::size_of::<Elf64Ehdr>() as u16
446 || file_header.e_phentsize != mem::size_of::<Elf64Phdr>() as u16
447 || file_header.e_phnum < EXPECTED_PROGRAM_HEADERS.len() as u16
448 || program_header_table_range.end >= elf_bytes.len()
449 || file_header.e_shentsize != mem::size_of::<Elf64Shdr>() as u16
450 || file_header.e_shstrndx >= file_header.e_shnum
452 {
453 return Err(ElfParserError::InvalidFileHeader);
454 }
455
456 const EXPECTED_PROGRAM_HEADERS: [(u32, u32, u64); 5] = [
457 (PT_LOAD, PF_X, ebpf::MM_BYTECODE_START), (PT_LOAD, PF_R, ebpf::MM_RODATA_START), (PT_GNU_STACK, PF_R | PF_W, ebpf::MM_STACK_START), (PT_LOAD, PF_R | PF_W, ebpf::MM_HEAP_START), (PT_NULL, 0, 0xFFFFFFFF00000000), ];
463 let program_header_table =
464 Elf64::slice_from_bytes::<Elf64Phdr>(elf_bytes, program_header_table_range.clone())?;
465 for (program_header, (p_type, p_flags, p_vaddr)) in program_header_table
466 .iter()
467 .zip(EXPECTED_PROGRAM_HEADERS.iter())
468 {
469 let p_filesz = if (*p_flags & PF_W) != 0 {
470 0
471 } else {
472 program_header.p_memsz
473 };
474 if program_header.p_type != *p_type
475 || program_header.p_flags != *p_flags
476 || program_header.p_offset < program_header_table_range.end as u64
477 || program_header.p_offset >= elf_bytes.len() as u64
478 || program_header.p_offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
479 || program_header.p_vaddr != *p_vaddr
480 || program_header.p_paddr != *p_vaddr
481 || program_header.p_filesz != p_filesz
482 || program_header.p_filesz
483 > (elf_bytes.len() as u64).saturating_sub(program_header.p_offset)
484 || program_header.p_memsz >= ebpf::MM_REGION_SIZE
485 {
486 return Err(ElfParserError::InvalidProgramHeader);
487 }
488 }
489
490 let config = loader.get_config();
491 let symbol_names_section_header = if config.enable_symbol_and_section_labels {
492 let (_section_header_table_range, section_header_table) =
493 Elf64::parse_section_header_table(
494 elf_bytes,
495 file_header_range.clone(),
496 file_header,
497 program_header_table_range.clone(),
498 )?;
499 let section_names_section_header = (file_header.e_shstrndx != SHN_UNDEF)
500 .then(|| {
501 section_header_table
502 .get(file_header.e_shstrndx as usize)
503 .ok_or(ElfParserError::OutOfBounds)
504 })
505 .transpose()?
506 .ok_or(ElfParserError::NoSectionNameStringTable)?;
507 let mut symbol_names_section_header = None;
508 for section_header in section_header_table.iter() {
509 let section_name = Elf64::get_string_in_section(
510 elf_bytes,
511 section_names_section_header,
512 section_header.sh_name,
513 64,
514 )?;
515 if section_name == b".dynstr" {
516 symbol_names_section_header = Some(section_header);
517 }
518 }
519 symbol_names_section_header
520 } else {
521 None
522 };
523 let bytecode_header = &program_header_table[0];
524 let rodata_header = &program_header_table[1];
525 let dynamic_symbol_table: &[Elf64Sym] =
526 Elf64::slice_from_program_header(elf_bytes, &program_header_table[4])?;
527 let mut function_registry = FunctionRegistry::<usize>::default();
528 let mut expected_symbol_address = bytecode_header.p_vaddr;
529 for symbol in dynamic_symbol_table {
530 if symbol.st_info & STT_FUNC == 0 {
531 continue;
532 }
533 if symbol.st_value != expected_symbol_address {
534 return Err(ElfParserError::OutOfBounds);
535 }
536 if symbol.st_size == 0 || symbol.st_size.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
537 {
538 return Err(ElfParserError::InvalidSize);
539 }
540 if symbol.st_size
541 > bytecode_header
542 .vm_range()
543 .end
544 .saturating_sub(symbol.st_value)
545 {
546 return Err(ElfParserError::OutOfBounds);
547 }
548 let target_pc = symbol
549 .st_value
550 .saturating_sub(bytecode_header.p_vaddr)
551 .checked_div(ebpf::INSN_SIZE as u64)
552 .unwrap_or_default() as usize;
553 let name = if config.enable_symbol_and_section_labels {
554 Elf64::get_string_in_section(
555 elf_bytes,
556 symbol_names_section_header
557 .as_ref()
558 .ok_or(ElfParserError::NoStringTable)?,
559 symbol.st_name as Elf64Word,
560 u8::MAX as usize,
561 )?
562 } else {
563 &[]
564 };
565 function_registry
566 .register_function(target_pc as u32, name, target_pc)
567 .unwrap();
568 expected_symbol_address = symbol.st_value.saturating_add(symbol.st_size);
569 }
570 if expected_symbol_address != bytecode_header.vm_range().end {
571 return Err(ElfParserError::OutOfBounds);
572 }
573 if !bytecode_header.vm_range().contains(&file_header.e_entry)
574 || file_header.e_entry.checked_rem(ebpf::INSN_SIZE as u64) != Some(0)
575 {
576 return Err(ElfParserError::InvalidFileHeader);
577 }
578 let entry_pc = file_header
579 .e_entry
580 .saturating_sub(bytecode_header.p_vaddr)
581 .checked_div(ebpf::INSN_SIZE as u64)
582 .unwrap_or_default() as usize;
583 if function_registry.lookup_by_key(entry_pc as u32).is_none() {
584 return Err(ElfParserError::InvalidFileHeader);
585 }
586
587 let text_section_vaddr = bytecode_header.p_vaddr;
588 let text_section_range = bytecode_header.file_range().unwrap_or_default();
589 let ro_section = Section::Borrowed(
590 rodata_header.p_vaddr as usize,
591 rodata_header.file_range().unwrap_or_default(),
592 );
593 Ok(Self {
594 elf_bytes: aligned_memory,
595 sbpf_version: SBPFVersion::Reserved, ro_section,
597 text_section_vaddr,
598 text_section_range,
599 entry_pc,
600 function_registry,
601 loader,
602 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
603 compiled_program: None,
604 })
605 }
606
607 fn load_with_lenient_parser(
609 bytes: &[u8],
610 loader: Arc<BuiltinProgram<C>>,
611 ) -> Result<Self, ElfError> {
612 let aligned_memory = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);
614 let (mut elf_bytes, unrelocated_elf_bytes) =
615 if is_memory_aligned(bytes.as_ptr() as usize, HOST_ALIGN) {
616 (aligned_memory, bytes)
617 } else {
618 (aligned_memory.clone(), aligned_memory.as_slice())
620 };
621 let elf = Elf64::parse(unrelocated_elf_bytes)?;
622
623 let config = loader.get_config();
624 let header = elf.file_header();
625 let sbpf_version = if header.e_flags == EF_SBPF_V2 {
626 SBPFVersion::Reserved
627 } else {
628 SBPFVersion::V0
629 };
630
631 Self::validate(config, &elf, elf_bytes.as_slice())?;
632
633 let text_section = get_section(&elf, b".text")?;
635 let text_section_vaddr =
636 if sbpf_version.enable_elf_vaddr() && text_section.sh_addr >= ebpf::MM_RODATA_START {
637 text_section.sh_addr
638 } else {
639 text_section.sh_addr.saturating_add(ebpf::MM_RODATA_START)
640 };
641 let vaddr_end = if sbpf_version.reject_rodata_stack_overlap() {
642 text_section_vaddr.saturating_add(text_section.sh_size)
643 } else {
644 text_section_vaddr
645 };
646 if (config.reject_broken_elfs
647 && !sbpf_version.enable_elf_vaddr()
648 && text_section.sh_addr != text_section.sh_offset)
649 || vaddr_end > ebpf::MM_STACK_START
650 {
651 return Err(ElfError::ValueOutOfBounds);
652 }
653
654 let mut function_registry = FunctionRegistry::default();
656 Self::relocate(
657 &mut function_registry,
658 &loader,
659 &elf,
660 elf_bytes.as_slice_mut(),
661 )?;
662
663 let offset = header.e_entry.saturating_sub(text_section.sh_addr);
665 if offset.checked_rem(ebpf::INSN_SIZE as u64) != Some(0) {
666 return Err(ElfError::InvalidEntrypoint);
667 }
668 let entry_pc = if let Some(entry_pc) = (offset as usize).checked_div(ebpf::INSN_SIZE) {
669 if !sbpf_version.static_syscalls() {
670 function_registry.unregister_function(ebpf::hash_symbol_name(b"entrypoint"));
671 }
672 function_registry.register_function_hashed_legacy(
673 &loader,
674 !sbpf_version.static_syscalls(),
675 *b"entrypoint",
676 entry_pc,
677 )?;
678 entry_pc
679 } else {
680 return Err(ElfError::InvalidEntrypoint);
681 };
682
683 let ro_section = Self::parse_ro_sections(
684 config,
685 &sbpf_version,
686 elf.section_header_table()
687 .iter()
688 .map(|s| (elf.section_name(s.sh_name).ok(), s)),
689 elf_bytes.as_slice(),
690 )?;
691
692 Ok(Self {
693 elf_bytes,
694 sbpf_version,
695 ro_section,
696 text_section_vaddr,
697 text_section_range: text_section.file_range().unwrap_or_default(),
698 entry_pc,
699 function_registry,
700 loader,
701 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
702 compiled_program: None,
703 })
704 }
705
706 #[rustfmt::skip]
708 #[allow(clippy::size_of_ref)]
709 pub fn mem_size(&self) -> usize {
710 let mut total = mem::size_of::<Self>();
711 total = total
712 .saturating_add(self.elf_bytes.mem_size())
714 .saturating_add(match &self.ro_section {
716 Section::Owned(_, data) => data.capacity(),
717 Section::Borrowed(_, _) => 0,
718 })
719 .saturating_add(self.function_registry.mem_size());
721
722 #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
723 {
724 total = total.saturating_add(self.compiled_program.as_ref().map_or(0, |program| program.mem_size()));
726 }
727
728 total
729 }
730
731 pub fn validate(config: &Config, elf: &Elf64, elf_bytes: &[u8]) -> Result<(), ElfError> {
735 let header = elf.file_header();
736 if header.e_ident.ei_class != ELFCLASS64 {
737 return Err(ElfError::WrongClass);
738 }
739 if header.e_ident.ei_data != ELFDATA2LSB {
740 return Err(ElfError::WrongEndianess);
741 }
742 if header.e_ident.ei_osabi != ELFOSABI_NONE {
743 return Err(ElfError::WrongAbi);
744 }
745 if header.e_machine != EM_BPF && header.e_machine != EM_SBPF {
746 return Err(ElfError::WrongMachine);
747 }
748 if header.e_type != ET_DYN {
749 return Err(ElfError::WrongType);
750 }
751
752 let sbpf_version = if header.e_flags == EF_SBPF_V2 {
753 SBPFVersion::Reserved
754 } else {
755 SBPFVersion::V0
756 };
757 if !config.enabled_sbpf_versions.contains(&sbpf_version) {
758 return Err(ElfError::UnsupportedSBPFVersion);
759 }
760
761 if sbpf_version.enable_elf_vaddr() {
762 if !config.optimize_rodata {
763 return Err(ElfError::UnsupportedSBPFVersion);
767 }
768
769 if elf.program_header_table().iter().count() >= 10 {
775 return Err(ElfError::InvalidProgramHeader);
776 }
777 }
778
779 let num_text_sections =
780 elf.section_header_table()
781 .iter()
782 .fold(0, |count: usize, section_header| {
783 if let Ok(this_name) = elf.section_name(section_header.sh_name) {
784 if this_name == b".text" {
785 return count.saturating_add(1);
786 }
787 }
788 count
789 });
790 if 1 != num_text_sections {
791 return Err(ElfError::NotOneTextSection);
792 }
793
794 for section_header in elf.section_header_table().iter() {
795 if let Ok(name) = elf.section_name(section_header.sh_name) {
796 if name.starts_with(b".bss")
797 || (section_header.is_writable()
798 && (name.starts_with(b".data") && !name.starts_with(b".data.rel")))
799 {
800 return Err(ElfError::WritableSectionNotSupported(
801 String::from_utf8_lossy(name).to_string(),
802 ));
803 }
804 }
805 }
806
807 for section_header in elf.section_header_table().iter() {
808 let start = section_header.sh_offset as usize;
809 let end = section_header
810 .sh_offset
811 .checked_add(section_header.sh_size)
812 .ok_or(ElfError::ValueOutOfBounds)? as usize;
813 let _ = elf_bytes
814 .get(start..end)
815 .ok_or(ElfError::ValueOutOfBounds)?;
816 }
817 let text_section = get_section(elf, b".text")?;
818 if !text_section.vm_range().contains(&header.e_entry) {
819 return Err(ElfError::EntrypointOutOfBounds);
820 }
821
822 Ok(())
823 }
824
825 pub(crate) fn parse_ro_sections<
826 'a,
827 S: IntoIterator<Item = (Option<&'a [u8]>, &'a Elf64Shdr)>,
828 >(
829 config: &Config,
830 sbpf_version: &SBPFVersion,
831 sections: S,
832 elf_bytes: &[u8],
833 ) -> Result<Section, ElfError> {
834 let mut lowest_addr = usize::MAX;
836 let mut highest_addr = 0;
838 let mut ro_fill_length = 0usize;
840 let mut invalid_offsets = false;
841 let mut addr_file_offset = None;
845
846 let mut first_ro_section = 0;
849 let mut last_ro_section = 0;
850 let mut n_ro_sections = 0usize;
851
852 let mut ro_slices = vec![];
853 for (i, (name, section_header)) in sections.into_iter().enumerate() {
854 match name {
855 Some(name)
856 if name == b".text"
857 || name == b".rodata"
858 || name == b".data.rel.ro"
859 || name == b".eh_frame" => {}
860 _ => continue,
861 }
862
863 if n_ro_sections == 0 {
864 first_ro_section = i;
865 }
866 last_ro_section = i;
867 n_ro_sections = n_ro_sections.saturating_add(1);
868
869 let section_addr = section_header.sh_addr;
870
871 if !invalid_offsets {
882 if sbpf_version.enable_elf_vaddr() {
883 debug_assert!(config.optimize_rodata);
885 if section_addr < section_header.sh_offset {
886 invalid_offsets = true;
887 } else {
888 let offset = section_addr.saturating_sub(section_header.sh_offset);
889 if *addr_file_offset.get_or_insert(offset) != offset {
890 invalid_offsets = true;
895 }
896 }
897 } else if section_addr != section_header.sh_offset {
898 invalid_offsets = true;
899 }
900 }
901
902 let mut vaddr_end =
903 if sbpf_version.enable_elf_vaddr() && section_addr >= ebpf::MM_RODATA_START {
904 section_addr
905 } else {
906 section_addr.saturating_add(ebpf::MM_RODATA_START)
907 };
908 if sbpf_version.reject_rodata_stack_overlap() {
909 vaddr_end = vaddr_end.saturating_add(section_header.sh_size);
910 }
911 if (config.reject_broken_elfs && invalid_offsets) || vaddr_end > ebpf::MM_STACK_START {
912 return Err(ElfError::ValueOutOfBounds);
913 }
914
915 let section_data = elf_bytes
916 .get(section_header.file_range().unwrap_or_default())
917 .ok_or(ElfError::ValueOutOfBounds)?;
918
919 let section_addr = section_addr as usize;
920 lowest_addr = lowest_addr.min(section_addr);
921 highest_addr = highest_addr.max(section_addr.saturating_add(section_data.len()));
922 ro_fill_length = ro_fill_length.saturating_add(section_data.len());
923
924 ro_slices.push((section_addr, section_data));
925 }
926
927 if config.reject_broken_elfs && lowest_addr.saturating_add(ro_fill_length) > highest_addr {
928 return Err(ElfError::ValueOutOfBounds);
929 }
930
931 let can_borrow = !invalid_offsets
932 && last_ro_section
933 .saturating_add(1)
934 .saturating_sub(first_ro_section)
935 == n_ro_sections;
936 if sbpf_version.enable_elf_vaddr() && !can_borrow {
937 return Err(ElfError::ValueOutOfBounds);
938 }
939 let ro_section = if config.optimize_rodata && can_borrow {
940 let buf_offset_start =
947 lowest_addr.saturating_sub(addr_file_offset.unwrap_or(0) as usize);
948 let buf_offset_end =
949 highest_addr.saturating_sub(addr_file_offset.unwrap_or(0) as usize);
950
951 let addr_offset = if lowest_addr >= ebpf::MM_RODATA_START as usize {
952 lowest_addr
957 } else {
958 if sbpf_version.enable_elf_vaddr() {
959 return Err(ElfError::ValueOutOfBounds);
960 }
961 lowest_addr.saturating_add(ebpf::MM_RODATA_START as usize)
962 };
963
964 Section::Borrowed(addr_offset, buf_offset_start..buf_offset_end)
965 } else {
966 if config.optimize_rodata {
970 highest_addr = highest_addr.saturating_sub(lowest_addr);
975 } else {
976 lowest_addr = 0;
980 };
981
982 let buf_len = highest_addr;
983 if buf_len > elf_bytes.len() {
984 return Err(ElfError::ValueOutOfBounds);
985 }
986
987 let mut ro_section = vec![0; buf_len];
988 for (section_addr, slice) in ro_slices.iter() {
989 let buf_offset_start = section_addr.saturating_sub(lowest_addr);
990 ro_section[buf_offset_start..buf_offset_start.saturating_add(slice.len())]
991 .copy_from_slice(slice);
992 }
993
994 let addr_offset = if lowest_addr >= ebpf::MM_RODATA_START as usize {
995 lowest_addr
996 } else {
997 lowest_addr.saturating_add(ebpf::MM_RODATA_START as usize)
998 };
999 Section::Owned(addr_offset, ro_section)
1000 };
1001
1002 Ok(ro_section)
1003 }
1004
1005 fn relocate(
1007 function_registry: &mut FunctionRegistry<usize>,
1008 loader: &BuiltinProgram<C>,
1009 elf: &Elf64,
1010 elf_bytes: &mut [u8],
1011 ) -> Result<(), ElfError> {
1012 let mut syscall_cache = BTreeMap::new();
1013 let text_section = get_section(elf, b".text")?;
1014 let sbpf_version = if elf.file_header().e_flags == EF_SBPF_V2 {
1015 SBPFVersion::Reserved
1016 } else {
1017 SBPFVersion::V0
1018 };
1019
1020 let config = loader.get_config();
1022 let text_bytes = elf_bytes
1023 .get_mut(text_section.file_range().unwrap_or_default())
1024 .ok_or(ElfError::ValueOutOfBounds)?;
1025 let instruction_count = text_bytes
1026 .len()
1027 .checked_div(ebpf::INSN_SIZE)
1028 .ok_or(ElfError::ValueOutOfBounds)?;
1029 for i in 0..instruction_count {
1030 let insn = ebpf::get_insn(text_bytes, i);
1031 if insn.opc == ebpf::CALL_IMM && insn.imm != -1 {
1032 let target_pc = (i as isize)
1033 .saturating_add(1)
1034 .saturating_add(insn.imm as isize);
1035 if target_pc < 0 || target_pc >= instruction_count as isize {
1036 return Err(ElfError::RelativeJumpOutOfBounds(i));
1037 }
1038 let name = if config.enable_symbol_and_section_labels {
1039 format!("function_{target_pc}")
1040 } else {
1041 String::default()
1042 };
1043 let key = function_registry.register_function_hashed_legacy(
1044 loader,
1045 !sbpf_version.static_syscalls(),
1046 name.as_bytes(),
1047 target_pc as usize,
1048 )?;
1049 if !sbpf_version.static_syscalls() {
1050 let offset = i.saturating_mul(ebpf::INSN_SIZE).saturating_add(4);
1051 let checked_slice = text_bytes
1052 .get_mut(offset..offset.saturating_add(4))
1053 .ok_or(ElfError::ValueOutOfBounds)?;
1054 LittleEndian::write_u32(checked_slice, key);
1055 }
1056 }
1057 }
1058
1059 let mut program_header: Option<&Elf64Phdr> = None;
1060
1061 for relocation in elf.dynamic_relocations_table().unwrap_or_default().iter() {
1063 let mut r_offset = relocation.r_offset as usize;
1064
1065 if sbpf_version.enable_elf_vaddr() {
1069 match program_header {
1070 Some(header) if header.vm_range().contains(&(r_offset as u64)) => {}
1071 _ => {
1072 program_header = elf
1073 .program_header_table()
1074 .iter()
1075 .find(|header| header.vm_range().contains(&(r_offset as u64)))
1076 }
1077 }
1078 let header = program_header.as_ref().ok_or(ElfError::ValueOutOfBounds)?;
1079 r_offset = r_offset
1080 .saturating_sub(header.p_vaddr as usize)
1081 .saturating_add(header.p_offset as usize);
1082 }
1083
1084 match BpfRelocationType::from_x86_relocation_type(relocation.r_type()) {
1085 Some(BpfRelocationType::R_Bpf_64_64) => {
1086 let imm_offset = if text_section
1088 .file_range()
1089 .unwrap_or_default()
1090 .contains(&r_offset)
1091 || sbpf_version == SBPFVersion::V0
1092 {
1093 r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE)
1094 } else {
1095 r_offset
1096 };
1097
1098 let checked_slice = elf_bytes
1101 .get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1102 .ok_or(ElfError::ValueOutOfBounds)?;
1103 let refd_addr = LittleEndian::read_u32(checked_slice) as u64;
1104
1105 let symbol = elf
1106 .dynamic_symbol_table()
1107 .and_then(|table| table.get(relocation.r_sym() as usize).cloned())
1108 .ok_or_else(|| ElfError::UnknownSymbol(relocation.r_sym() as usize))?;
1109
1110 let mut addr = symbol.st_value.saturating_add(refd_addr);
1113
1114 if addr < ebpf::MM_RODATA_START {
1119 addr = ebpf::MM_RODATA_START.saturating_add(addr);
1120 }
1121
1122 if text_section
1123 .file_range()
1124 .unwrap_or_default()
1125 .contains(&r_offset)
1126 || sbpf_version == SBPFVersion::V0
1127 {
1128 let imm_low_offset = imm_offset;
1129 let imm_high_offset = imm_low_offset.saturating_add(INSN_SIZE);
1130
1131 let imm_slice = elf_bytes
1133 .get_mut(
1134 imm_low_offset
1135 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1136 )
1137 .ok_or(ElfError::ValueOutOfBounds)?;
1138 LittleEndian::write_u32(imm_slice, (addr & 0xFFFFFFFF) as u32);
1139
1140 let imm_slice = elf_bytes
1142 .get_mut(
1143 imm_high_offset
1144 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1145 )
1146 .ok_or(ElfError::ValueOutOfBounds)?;
1147 LittleEndian::write_u32(
1148 imm_slice,
1149 addr.checked_shr(32).unwrap_or_default() as u32,
1150 );
1151 } else {
1152 let imm_slice = elf_bytes
1153 .get_mut(imm_offset..imm_offset.saturating_add(8))
1154 .ok_or(ElfError::ValueOutOfBounds)?;
1155 LittleEndian::write_u64(imm_slice, addr);
1156 }
1157 }
1158 Some(BpfRelocationType::R_Bpf_64_Relative) => {
1159 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1165
1166 if text_section
1167 .file_range()
1168 .unwrap_or_default()
1169 .contains(&r_offset)
1170 {
1171 let imm_low_offset = imm_offset;
1176 let imm_high_offset = r_offset
1177 .saturating_add(INSN_SIZE)
1178 .saturating_add(BYTE_OFFSET_IMMEDIATE);
1179
1180 let imm_slice = elf_bytes
1182 .get(
1183 imm_low_offset
1184 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1185 )
1186 .ok_or(ElfError::ValueOutOfBounds)?;
1187 let va_low = LittleEndian::read_u32(imm_slice) as u64;
1188
1189 let imm_slice = elf_bytes
1191 .get(
1192 imm_high_offset
1193 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1194 )
1195 .ok_or(ElfError::ValueOutOfBounds)?;
1196 let va_high = LittleEndian::read_u32(imm_slice) as u64;
1197
1198 let mut refd_addr = va_high.checked_shl(32).unwrap_or_default() | va_low;
1200
1201 if refd_addr == 0 {
1202 return Err(ElfError::InvalidVirtualAddress(refd_addr));
1203 }
1204
1205 if refd_addr < ebpf::MM_RODATA_START {
1206 refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1209 }
1210
1211 let imm_slice = elf_bytes
1213 .get_mut(
1214 imm_low_offset
1215 ..imm_low_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1216 )
1217 .ok_or(ElfError::ValueOutOfBounds)?;
1218 LittleEndian::write_u32(imm_slice, (refd_addr & 0xFFFFFFFF) as u32);
1219
1220 let imm_slice = elf_bytes
1222 .get_mut(
1223 imm_high_offset
1224 ..imm_high_offset.saturating_add(BYTE_LENGTH_IMMEDIATE),
1225 )
1226 .ok_or(ElfError::ValueOutOfBounds)?;
1227 LittleEndian::write_u32(
1228 imm_slice,
1229 refd_addr.checked_shr(32).unwrap_or_default() as u32,
1230 );
1231 } else {
1232 let refd_addr = if sbpf_version != SBPFVersion::V0 {
1233 let addr_slice = elf_bytes
1237 .get(r_offset..r_offset.saturating_add(mem::size_of::<u64>()))
1238 .ok_or(ElfError::ValueOutOfBounds)?;
1239 let mut refd_addr = LittleEndian::read_u64(addr_slice);
1240 if refd_addr < ebpf::MM_RODATA_START {
1241 refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1243 }
1244 refd_addr
1245 } else {
1246 let addr_slice = elf_bytes
1252 .get(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1253 .ok_or(ElfError::ValueOutOfBounds)?;
1254 let refd_addr = LittleEndian::read_u32(addr_slice) as u64;
1255 ebpf::MM_RODATA_START.saturating_add(refd_addr)
1256 };
1257
1258 let addr_slice = elf_bytes
1259 .get_mut(r_offset..r_offset.saturating_add(mem::size_of::<u64>()))
1260 .ok_or(ElfError::ValueOutOfBounds)?;
1261 LittleEndian::write_u64(addr_slice, refd_addr);
1262 }
1263 }
1264 Some(BpfRelocationType::R_Bpf_64_32) => {
1265 let imm_offset = r_offset.saturating_add(BYTE_OFFSET_IMMEDIATE);
1271
1272 let symbol = elf
1273 .dynamic_symbol_table()
1274 .and_then(|table| table.get(relocation.r_sym() as usize).cloned())
1275 .ok_or_else(|| ElfError::UnknownSymbol(relocation.r_sym() as usize))?;
1276
1277 let name = elf
1278 .dynamic_symbol_name(symbol.st_name as Elf64Word)
1279 .map_err(|_| ElfError::UnknownSymbol(symbol.st_name as usize))?;
1280
1281 let key = if symbol.is_function() && symbol.st_value != 0 {
1283 if !text_section.vm_range().contains(&symbol.st_value) {
1284 return Err(ElfError::ValueOutOfBounds);
1285 }
1286 let target_pc = (symbol.st_value.saturating_sub(text_section.sh_addr)
1287 as usize)
1288 .checked_div(ebpf::INSN_SIZE)
1289 .unwrap_or_default();
1290 function_registry.register_function_hashed_legacy(
1291 loader,
1292 !sbpf_version.static_syscalls(),
1293 name,
1294 target_pc,
1295 )?
1296 } else {
1297 let hash = *syscall_cache
1299 .entry(symbol.st_name)
1300 .or_insert_with(|| ebpf::hash_symbol_name(name));
1301 if config.reject_broken_elfs
1302 && loader
1303 .get_function_registry(SBPFVersion::V0)
1304 .lookup_by_key(hash)
1305 .is_none()
1306 {
1307 return Err(ElfError::UnresolvedSymbol(
1308 String::from_utf8_lossy(name).to_string(),
1309 r_offset.checked_div(ebpf::INSN_SIZE).unwrap_or(0),
1310 r_offset,
1311 ));
1312 }
1313 hash
1314 };
1315
1316 let checked_slice = elf_bytes
1317 .get_mut(imm_offset..imm_offset.saturating_add(BYTE_LENGTH_IMMEDIATE))
1318 .ok_or(ElfError::ValueOutOfBounds)?;
1319 LittleEndian::write_u32(checked_slice, key);
1320 }
1321 _ => return Err(ElfError::UnknownRelocation(relocation.r_type())),
1322 }
1323 }
1324
1325 if config.enable_symbol_and_section_labels {
1326 for symbol in elf.symbol_table().ok().flatten().unwrap_or_default().iter() {
1328 if symbol.st_info & 0xEF != 0x02 {
1329 continue;
1330 }
1331 if !text_section.vm_range().contains(&symbol.st_value) {
1332 return Err(ElfError::ValueOutOfBounds);
1333 }
1334 let target_pc = (symbol.st_value.saturating_sub(text_section.sh_addr) as usize)
1335 .checked_div(ebpf::INSN_SIZE)
1336 .unwrap_or_default();
1337 let name = elf
1338 .symbol_name(symbol.st_name as Elf64Word)
1339 .map_err(|_| ElfError::UnknownSymbol(symbol.st_name as usize))?;
1340 function_registry.register_function_hashed_legacy(
1341 loader,
1342 !sbpf_version.static_syscalls(),
1343 name,
1344 target_pc,
1345 )?;
1346 }
1347 }
1348
1349 Ok(())
1350 }
1351
1352 #[allow(dead_code)]
1353 fn dump_data(name: &str, prog: &[u8]) {
1354 let mut eight_bytes: Vec<u8> = Vec::new();
1355 println!("{name}");
1356 for i in prog.iter() {
1357 if eight_bytes.len() >= 7 {
1358 println!("{eight_bytes:02X?}");
1359 eight_bytes.clear();
1360 } else {
1361 eight_bytes.push(*i);
1362 }
1363 }
1364 }
1365}
1366
1367pub(crate) fn get_ro_region(ro_section: &Section, elf: &[u8]) -> MemoryRegion {
1368 let (offset, ro_data) = match ro_section {
1369 Section::Owned(offset, data) => (*offset, data.as_slice()),
1370 Section::Borrowed(offset, byte_range) => (*offset, &elf[byte_range.clone()]),
1371 };
1372
1373 MemoryRegion::new_readonly(ro_data, offset as u64)
1377}
1378
1379#[cfg(test)]
1380mod test {
1381 use super::*;
1382 use crate::{
1383 elf_parser::{
1384 consts::{ELFCLASS32, ELFDATA2MSB, ET_REL},
1386 types::{Elf64Ehdr, Elf64Shdr, Elf64Sym},
1387 SECTION_NAME_LENGTH_MAXIMUM,
1388 },
1389 error::ProgramResult,
1390 fuzz::fuzz,
1391 program::BuiltinFunction,
1392 syscalls,
1393 vm::TestContextObject,
1394 };
1395 use rand::{distributions::Uniform, Rng};
1396 use std::{fs::File, io::Read};
1397 use test_utils::assert_error;
1398 type ElfExecutable = Executable<TestContextObject>;
1399
1400 fn loader() -> Arc<BuiltinProgram<TestContextObject>> {
1401 let mut function_registry =
1402 FunctionRegistry::<BuiltinFunction<TestContextObject>>::default();
1403 function_registry
1404 .register_function_hashed(*b"log", syscalls::SyscallString::vm)
1405 .unwrap();
1406 function_registry
1407 .register_function_hashed(*b"log_64", syscalls::SyscallU64::vm)
1408 .unwrap();
1409 Arc::new(BuiltinProgram::new_loader(
1410 Config::default(),
1411 function_registry,
1412 ))
1413 }
1414
1415 #[test]
1416 fn test_strict_header() {
1417 let elf_bytes =
1418 std::fs::read("tests/elfs/strict_header.so").expect("failed to read elf file");
1419 let loader = loader();
1420
1421 {
1423 let loader = Arc::new(BuiltinProgram::new_loader(
1424 Config {
1425 enable_symbol_and_section_labels: true,
1426 ..Config::default()
1427 },
1428 FunctionRegistry::<BuiltinFunction<TestContextObject>>::default(),
1429 ));
1430 let executable = ElfExecutable::load(&elf_bytes, loader.clone()).unwrap();
1431 let (name, _pc) = executable.get_function_registry().lookup_by_key(4).unwrap();
1432 assert_eq!(name, b"entrypoint");
1433 }
1434
1435 {
1437 let mut elf_bytes = elf_bytes.clone();
1438 elf_bytes[0x0030] = 0xFF;
1439 let err = ElfExecutable::load(&elf_bytes, loader.clone()).unwrap_err();
1440 assert_eq!(err, ElfError::UnsupportedSBPFVersion);
1441 }
1442
1443 let err = ElfExecutable::load_with_strict_parser(&[], loader.clone()).unwrap_err();
1445 assert_eq!(err, ElfParserError::OutOfBounds);
1446
1447 let expected_results = std::iter::repeat(&Err(ElfParserError::InvalidFileHeader))
1449 .take(40)
1450 .chain(std::iter::repeat(&Ok(())).take(12))
1451 .chain(std::iter::repeat(&Err(ElfParserError::InvalidFileHeader)).take(4))
1452 .chain(std::iter::repeat(&Err(ElfParserError::InvalidProgramHeader)).take(1))
1453 .chain(std::iter::repeat(&Err(ElfParserError::InvalidFileHeader)).take(3))
1454 .chain(std::iter::repeat(&Ok(())).take(2))
1455 .chain(std::iter::repeat(&Err(ElfParserError::InvalidFileHeader)).take(2));
1456 for (offset, expected) in (0..std::mem::size_of::<Elf64Ehdr>()).zip(expected_results) {
1457 let mut elf_bytes = elf_bytes.clone();
1458 elf_bytes[offset] = 0xAF;
1459 let result =
1460 ElfExecutable::load_with_strict_parser(&elf_bytes, loader.clone()).map(|_| ());
1461 assert_eq!(&result, expected);
1462 }
1463
1464 let expected_results_readonly =
1466 std::iter::repeat(&Err(ElfParserError::InvalidProgramHeader))
1467 .take(48)
1468 .chain(std::iter::repeat(&Ok(())).take(8))
1469 .collect::<Vec<_>>();
1470 let expected_results_writable =
1471 std::iter::repeat(&Err(ElfParserError::InvalidProgramHeader))
1472 .take(40)
1473 .chain(std::iter::repeat(&Ok(())).take(4))
1474 .chain(std::iter::repeat(&Err(ElfParserError::InvalidProgramHeader)).take(4))
1475 .chain(std::iter::repeat(&Ok(())).take(8))
1476 .collect::<Vec<_>>();
1477 let expected_results = vec![
1478 expected_results_readonly.iter(),
1479 expected_results_readonly.iter(),
1480 expected_results_writable.iter(),
1481 expected_results_writable.iter(),
1482 expected_results_readonly.iter(),
1483 ];
1484 for (header_index, expected_results) in expected_results.into_iter().enumerate() {
1485 for (offset, expected) in (std::mem::size_of::<Elf64Ehdr>()
1486 + std::mem::size_of::<Elf64Phdr>() * header_index
1487 ..std::mem::size_of::<Elf64Ehdr>()
1488 + std::mem::size_of::<Elf64Phdr>() * (header_index + 1))
1489 .zip(expected_results)
1490 {
1491 let mut elf_bytes = elf_bytes.clone();
1492 elf_bytes[offset] = 0xAF;
1493 let result =
1494 ElfExecutable::load_with_strict_parser(&elf_bytes, loader.clone()).map(|_| ());
1495 assert_eq!(&&result, expected);
1496 }
1497 }
1498
1499 for index in 1..3 {
1501 let expected_results = std::iter::repeat(&Ok(()))
1502 .take(8)
1503 .chain(std::iter::repeat(&Err(ElfParserError::OutOfBounds)).take(8))
1504 .chain(std::iter::repeat(&Err(ElfParserError::InvalidSize)).take(1))
1505 .chain(std::iter::repeat(&Err(ElfParserError::OutOfBounds)).take(7));
1506 for (offset, expected) in (0x3000 + std::mem::size_of::<Elf64Sym>() * index
1507 ..0x3000 + std::mem::size_of::<Elf64Sym>() * (index + 1))
1508 .zip(expected_results)
1509 {
1510 let mut elf_bytes = elf_bytes.clone();
1511 elf_bytes[offset] = 0xAF;
1512 let result =
1513 ElfExecutable::load_with_strict_parser(&elf_bytes, loader.clone()).map(|_| ());
1514 assert_eq!(&result, expected);
1515 }
1516 }
1517
1518 {
1520 let mut elf_bytes = elf_bytes.clone();
1521 elf_bytes[0x3040] = 0x00;
1522 let err =
1523 ElfExecutable::load_with_strict_parser(&elf_bytes, loader.clone()).unwrap_err();
1524 assert_eq!(err, ElfParserError::InvalidSize);
1525 }
1526
1527 {
1529 let mut elf_bytes = elf_bytes.clone();
1530 elf_bytes[0x3040] = 0x08;
1531 let err =
1532 ElfExecutable::load_with_strict_parser(&elf_bytes, loader.clone()).unwrap_err();
1533 assert_eq!(err, ElfParserError::OutOfBounds);
1534 }
1535
1536 {
1538 let mut elf_bytes = elf_bytes.clone();
1539 elf_bytes[0x0018] = 0x10;
1540 let err =
1541 ElfExecutable::load_with_strict_parser(&elf_bytes, loader.clone()).unwrap_err();
1542 assert_eq!(err, ElfParserError::InvalidFileHeader);
1543 }
1544 }
1545
1546 #[test]
1547 fn test_validate() {
1548 let elf_bytes = std::fs::read("tests/elfs/relative_call_sbpfv0.so").unwrap();
1549 let elf = Elf64::parse(&elf_bytes).unwrap();
1550 let mut header = elf.file_header().clone();
1551
1552 let config = Config::default();
1553
1554 let write_header = |header: Elf64Ehdr| unsafe {
1555 let mut bytes = elf_bytes.clone();
1556 std::ptr::write(bytes.as_mut_ptr().cast::<Elf64Ehdr>(), header);
1557 bytes
1558 };
1559
1560 ElfExecutable::validate(&config, &elf, &elf_bytes).expect("validation failed");
1561
1562 header.e_ident.ei_class = ELFCLASS32;
1563 let bytes = write_header(header.clone());
1564 Elf64::parse(&bytes).expect_err("allowed bad class");
1566
1567 header.e_ident.ei_class = ELFCLASS64;
1568 let bytes = write_header(header.clone());
1569 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1570 .expect("validation failed");
1571
1572 header.e_ident.ei_data = ELFDATA2MSB;
1573 let bytes = write_header(header.clone());
1574 Elf64::parse(&bytes).expect_err("allowed big endian");
1576
1577 header.e_ident.ei_data = ELFDATA2LSB;
1578 let bytes = write_header(header.clone());
1579 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1580 .expect("validation failed");
1581
1582 header.e_ident.ei_osabi = 1;
1583 let bytes = write_header(header.clone());
1584 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1585 .expect_err("allowed wrong abi");
1586
1587 header.e_ident.ei_osabi = ELFOSABI_NONE;
1588 let bytes = write_header(header.clone());
1589 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1590 .expect("validation failed");
1591
1592 header.e_machine = 42;
1593 let bytes = write_header(header.clone());
1594 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1595 .expect_err("allowed wrong machine");
1596
1597 header.e_machine = EM_BPF;
1598 let bytes = write_header(header.clone());
1599 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1600 .expect("validation failed");
1601
1602 header.e_type = ET_REL;
1603 let bytes = write_header(header);
1604 ElfExecutable::validate(&config, &Elf64::parse(&bytes).unwrap(), &elf_bytes)
1605 .expect_err("allowed wrong type");
1606 }
1607
1608 #[test]
1609 fn test_load() {
1610 let mut file = File::open("tests/elfs/relative_call_sbpfv0.so").expect("file open failed");
1611 let mut elf_bytes = Vec::new();
1612 file.read_to_end(&mut elf_bytes)
1613 .expect("failed to read elf file");
1614 ElfExecutable::load(&elf_bytes, loader()).expect("validation failed");
1615 }
1616
1617 #[test]
1618 fn test_load_unaligned() {
1619 let mut elf_bytes =
1620 std::fs::read("tests/elfs/relative_call_sbpfv0.so").expect("failed to read elf file");
1621 elf_bytes.insert(0, 0);
1625 ElfExecutable::load(&elf_bytes[1..], loader()).expect("validation failed");
1626 }
1627
1628 #[test]
1629 fn test_entrypoint() {
1630 let loader = loader();
1631
1632 let mut file = File::open("tests/elfs/relative_call_sbpfv0.so").expect("file open failed");
1633 let mut elf_bytes = Vec::new();
1634 file.read_to_end(&mut elf_bytes)
1635 .expect("failed to read elf file");
1636 let elf = ElfExecutable::load(&elf_bytes, loader.clone()).expect("validation failed");
1637 let parsed_elf = Elf64::parse(&elf_bytes).unwrap();
1638 let executable: &Executable<TestContextObject> = &elf;
1639 assert_eq!(4, executable.get_entrypoint_instruction_offset());
1640
1641 let write_header = |header: Elf64Ehdr| unsafe {
1642 let mut bytes = elf_bytes.clone();
1643 std::ptr::write(bytes.as_mut_ptr().cast::<Elf64Ehdr>(), header);
1644 bytes
1645 };
1646
1647 let mut header = parsed_elf.file_header().clone();
1648 let initial_e_entry = header.e_entry;
1649
1650 header.e_entry += 8;
1651 let elf_bytes = write_header(header.clone());
1652 let elf = ElfExecutable::load(&elf_bytes, loader.clone()).expect("validation failed");
1653 let executable: &Executable<TestContextObject> = &elf;
1654 assert_eq!(5, executable.get_entrypoint_instruction_offset());
1655
1656 header.e_entry = 1;
1657 let elf_bytes = write_header(header.clone());
1658 assert!(matches!(
1659 ElfExecutable::load(&elf_bytes, loader.clone()),
1660 Err(ElfError::EntrypointOutOfBounds)
1661 ));
1662
1663 header.e_entry = u64::MAX;
1664 let elf_bytes = write_header(header.clone());
1665 assert!(matches!(
1666 ElfExecutable::load(&elf_bytes, loader.clone()),
1667 Err(ElfError::EntrypointOutOfBounds)
1668 ));
1669
1670 header.e_entry = initial_e_entry + ebpf::INSN_SIZE as u64 + 1;
1671 let elf_bytes = write_header(header.clone());
1672 assert!(matches!(
1673 ElfExecutable::load(&elf_bytes, loader.clone()),
1674 Err(ElfError::InvalidEntrypoint)
1675 ));
1676
1677 header.e_entry = initial_e_entry;
1678 let elf_bytes = write_header(header);
1679 let elf = ElfExecutable::load(&elf_bytes, loader).expect("validation failed");
1680 let executable: &Executable<TestContextObject> = &elf;
1681 assert_eq!(4, executable.get_entrypoint_instruction_offset());
1682 }
1683
1684 #[test]
1685 #[ignore]
1686 fn test_fuzz_load() {
1687 let loader = loader();
1688
1689 let mut rng = rand::thread_rng();
1691 let range = Uniform::new(0, 255);
1692 println!("random bytes");
1693 for _ in 0..1_000 {
1694 let elf_bytes: Vec<u8> = (0..100).map(|_| rng.sample(range)).collect();
1695 let _ = ElfExecutable::load(&elf_bytes, loader.clone());
1696 }
1697
1698 let mut file = File::open("tests/elfs/noop.so").expect("file open failed");
1701 let mut elf_bytes = Vec::new();
1702 file.read_to_end(&mut elf_bytes)
1703 .expect("failed to read elf file");
1704 let parsed_elf = Elf64::parse(&elf_bytes).unwrap();
1705
1706 println!("mangle elf header");
1708 fuzz(
1709 &elf_bytes,
1710 1_000_000,
1711 100,
1712 0..parsed_elf.file_header().e_ehsize as usize,
1713 0..255,
1714 |bytes: &mut [u8]| {
1715 let _ = ElfExecutable::load(bytes, loader.clone());
1716 },
1717 );
1718
1719 println!("mangle section headers");
1721 fuzz(
1722 &elf_bytes,
1723 1_000_000,
1724 100,
1725 parsed_elf.file_header().e_shoff as usize..elf_bytes.len(),
1726 0..255,
1727 |bytes: &mut [u8]| {
1728 let _ = ElfExecutable::load(bytes, loader.clone());
1729 },
1730 );
1731
1732 println!("mangle whole elf");
1734 fuzz(
1735 &elf_bytes,
1736 1_000_000,
1737 100,
1738 0..elf_bytes.len(),
1739 0..255,
1740 |bytes: &mut [u8]| {
1741 let _ = ElfExecutable::load(bytes, loader.clone());
1742 },
1743 );
1744 }
1745
1746 fn new_section(sh_addr: u64, sh_size: u64) -> Elf64Shdr {
1747 Elf64Shdr {
1748 sh_addr,
1749 sh_offset: sh_addr
1750 .checked_sub(ebpf::MM_RODATA_START)
1751 .unwrap_or(sh_addr),
1752 sh_size,
1753 sh_name: 0,
1754 sh_type: 0,
1755 sh_flags: 0,
1756 sh_link: 0,
1757 sh_info: 0,
1758 sh_addralign: 0,
1759 sh_entsize: 0,
1760 }
1761 }
1762
1763 #[test]
1764 fn test_owned_ro_sections_not_contiguous() {
1765 let config = Config::default();
1766 let elf_bytes = [0u8; 512];
1767
1768 let s1 = new_section(10, 10);
1770 let s2 = new_section(20, 10);
1771 let s3 = new_section(30, 10);
1772
1773 let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
1774 (Some(b".text"), &s1),
1775 (Some(b".dynamic"), &s2),
1776 (Some(b".rodata"), &s3),
1777 ];
1778 assert!(matches!(
1779 ElfExecutable::parse_ro_sections(
1780 &config,
1781 &SBPFVersion::V0,
1782 sections,
1783 &elf_bytes,
1784 ),
1785 Ok(Section::Owned(offset, data)) if offset == ebpf::MM_RODATA_START as usize + 10 && data.len() == 30
1786 ));
1787 }
1788
1789 #[test]
1790 fn test_owned_ro_sections_with_sh_offset() {
1791 let config = Config {
1792 reject_broken_elfs: false,
1793 ..Config::default()
1794 };
1795 let elf_bytes = [0u8; 512];
1796
1797 let s1 = new_section(10, 10);
1800 let mut s2 = new_section(20, 10);
1801 s2.sh_offset = 30;
1802
1803 let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
1804 [(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
1805 assert!(matches!(
1806 ElfExecutable::parse_ro_sections(
1807 &config,
1808 &SBPFVersion::V0,
1809 sections,
1810 &elf_bytes,
1811 ),
1812 Ok(Section::Owned(offset, data)) if offset == ebpf::MM_RODATA_START as usize + 10 && data.len() == 20
1813 ));
1814 }
1815
1816 #[test]
1817 fn test_sh_offset_not_same_as_vaddr() {
1818 let config = Config {
1819 reject_broken_elfs: true,
1820 enabled_sbpf_versions: SBPFVersion::V0..=SBPFVersion::V0,
1821 ..Config::default()
1822 };
1823 let elf_bytes = [0u8; 512];
1824
1825 let mut s1 = new_section(10, 10);
1826
1827 {
1828 let sections: [(Option<&[u8]>, &Elf64Shdr); 1] = [(Some(b".text"), &s1)];
1829 assert!(ElfExecutable::parse_ro_sections(
1830 &config,
1831 &SBPFVersion::V0,
1832 sections,
1833 &elf_bytes
1834 )
1835 .is_ok());
1836 }
1837
1838 s1.sh_offset = 0;
1839 let sections: [(Option<&[u8]>, &Elf64Shdr); 1] = [(Some(b".text"), &s1)];
1840 assert_eq!(
1841 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V0, sections, &elf_bytes),
1842 Err(ElfError::ValueOutOfBounds)
1843 );
1844 }
1845
1846 #[test]
1847 fn test_invalid_sh_offset_larger_than_vaddr() {
1848 let config = Config {
1849 reject_broken_elfs: true,
1850 ..Config::default()
1851 };
1852 let elf_bytes = [0u8; 512];
1853
1854 let s1 = new_section(10, 10);
1855 let mut s2 = new_section(20, 10);
1857 s2.sh_offset = 30;
1858
1859 let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
1860 [(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
1861 assert_eq!(
1862 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes,),
1863 Err(ElfError::ValueOutOfBounds)
1864 );
1865 }
1866
1867 #[test]
1868 fn test_reject_non_constant_sh_offset() {
1869 let config = Config {
1870 reject_broken_elfs: true,
1871 ..Config::default()
1872 };
1873 let elf_bytes = [0u8; 512];
1874
1875 let mut s1 = new_section(ebpf::MM_RODATA_START + 10, 10);
1876 let mut s2 = new_section(ebpf::MM_RODATA_START + 20, 10);
1877 s1.sh_offset = 100;
1881 s2.sh_offset = 120;
1882
1883 let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
1884 [(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
1885 assert_eq!(
1886 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V3, sections, &elf_bytes),
1887 Err(ElfError::ValueOutOfBounds)
1888 );
1889 }
1890
1891 #[test]
1892 fn test_borrowed_ro_sections_with_constant_sh_offset() {
1893 let config = Config {
1894 reject_broken_elfs: true,
1895 ..Config::default()
1896 };
1897 let elf_bytes = [0u8; 512];
1898
1899 let mut s1 = new_section(ebpf::MM_RODATA_START + 10, 10);
1900 let mut s2 = new_section(ebpf::MM_RODATA_START + 20, 10);
1901 s1.sh_offset = 100;
1903 s2.sh_offset = 110;
1904
1905 let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
1906 [(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
1907 assert_eq!(
1908 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V3, sections, &elf_bytes),
1909 Ok(Section::Borrowed(
1910 ebpf::MM_RODATA_START as usize + 10,
1911 100..120
1912 ))
1913 );
1914 }
1915
1916 #[test]
1917 fn test_owned_ro_region_no_initial_gap() {
1918 let config = Config::default();
1919 let elf_bytes = [0u8; 512];
1920
1921 let s1 = new_section(0, 10);
1923 let s2 = new_section(10, 10);
1924 let s3 = new_section(20, 10);
1925
1926 let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
1927 (Some(b".text"), &s1),
1928 (Some(b".dynamic"), &s2),
1929 (Some(b".rodata"), &s3),
1930 ];
1931 let ro_section =
1932 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V0, sections, &elf_bytes)
1933 .unwrap();
1934 let ro_region = get_ro_region(&ro_section, &elf_bytes);
1935 let owned_section = match &ro_section {
1936 Section::Owned(_offset, data) => data.as_slice(),
1937 _ => panic!(),
1938 };
1939
1940 assert!(matches!(
1942 ro_region.vm_to_host(ebpf::MM_RODATA_START, s3.sh_addr + s3.sh_size),
1943 ProgramResult::Ok(ptr) if ptr == owned_section.as_ptr() as u64,
1944 ));
1945
1946 assert_error!(
1948 ro_region.vm_to_host(ebpf::MM_RODATA_START + s3.sh_addr + s3.sh_size, 1),
1949 "InvalidVirtualAddress({})",
1950 ebpf::MM_RODATA_START + s3.sh_addr + s3.sh_size
1951 );
1952 }
1953
1954 #[test]
1955 fn test_owned_ro_region_initial_gap_mappable() {
1956 let config = Config {
1957 optimize_rodata: false,
1958 ..Config::default()
1959 };
1960 let elf_bytes = [0u8; 512];
1961
1962 let s1 = new_section(10, 10);
1964 let s2 = new_section(20, 10);
1965 let s3 = new_section(30, 10);
1966
1967 let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
1968 (Some(b".text"), &s1),
1969 (Some(b".dynamic"), &s2),
1970 (Some(b".rodata"), &s3),
1971 ];
1972 let ro_section =
1974 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V0, sections, &elf_bytes)
1975 .unwrap();
1976 let ro_region = get_ro_region(&ro_section, &elf_bytes);
1977 let owned_section = match &ro_section {
1978 Section::Owned(_offset, data) => data.as_slice(),
1979 _ => panic!(),
1980 };
1981
1982 assert!(matches!(
1986 ro_region.vm_to_host(ebpf::MM_RODATA_START, s3.sh_addr + s3.sh_size),
1987 ProgramResult::Ok(ptr) if ptr == owned_section.as_ptr() as u64,
1988 ));
1989
1990 assert_error!(
1992 ro_region.vm_to_host(ebpf::MM_RODATA_START + s3.sh_addr + s3.sh_size, 1),
1993 "InvalidVirtualAddress({})",
1994 ebpf::MM_RODATA_START + s3.sh_addr + s3.sh_size
1995 );
1996 }
1997
1998 #[test]
1999 fn test_owned_ro_region_initial_gap_map_error() {
2000 let config = Config::default();
2001 let elf_bytes = [0u8; 512];
2002
2003 let s1 = new_section(10, 10);
2005 let s2 = new_section(20, 10);
2006 let s3 = new_section(30, 10);
2007
2008 let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
2009 (Some(b".text"), &s1),
2010 (Some(b".dynamic"), &s2),
2011 (Some(b".rodata"), &s3),
2012 ];
2013 let ro_section =
2014 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V0, sections, &elf_bytes)
2015 .unwrap();
2016 let owned_section = match &ro_section {
2017 Section::Owned(_offset, data) => data.as_slice(),
2018 _ => panic!(),
2019 };
2020 let ro_region = get_ro_region(&ro_section, &elf_bytes);
2021
2022 assert_error!(
2026 ro_region.vm_to_host(ebpf::MM_RODATA_START, 1),
2027 "InvalidVirtualAddress({})",
2028 ebpf::MM_RODATA_START
2029 );
2030
2031 assert_error!(
2033 ro_region.vm_to_host(ebpf::MM_RODATA_START + s1.sh_addr - 1, 1),
2034 "InvalidVirtualAddress({})",
2035 ebpf::MM_RODATA_START + 9
2036 );
2037
2038 assert!(matches!(
2040 ro_region.vm_to_host(
2041 ebpf::MM_RODATA_START + s1.sh_addr,
2042 s3.sh_addr + s3.sh_size - s1.sh_addr
2043 ),
2044 ProgramResult::Ok(ptr) if ptr == owned_section.as_ptr() as u64,
2045 ));
2046
2047 assert_error!(
2049 ro_region.vm_to_host(ebpf::MM_RODATA_START + s3.sh_addr + s3.sh_size, 1),
2050 "InvalidVirtualAddress({})",
2051 ebpf::MM_RODATA_START + s3.sh_addr + s3.sh_size
2052 );
2053 }
2054
2055 #[test]
2056 fn test_borrowed_ro_sections_disabled() {
2057 let config = Config {
2058 optimize_rodata: false,
2059 ..Config::default()
2060 };
2061 let elf_bytes = [0u8; 512];
2062
2063 let s1 = new_section(0, 10);
2066 let s2 = new_section(10, 10);
2067
2068 let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
2069 [(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
2070 assert!(matches!(
2071 ElfExecutable::parse_ro_sections(
2072 &config,
2073 &SBPFVersion::V0, sections,
2075 &elf_bytes,
2076 ),
2077 Ok(Section::Owned(offset, data)) if offset == ebpf::MM_RODATA_START as usize && data.len() == 20
2078 ));
2079 }
2080
2081 #[test]
2082 fn test_borrowed_ro_sections() {
2083 let config = Config::default();
2084 let elf_bytes = [0u8; 512];
2085 for (vaddr_base, sbpf_version) in [
2086 (0, SBPFVersion::V0),
2087 (ebpf::MM_RODATA_START, SBPFVersion::V3),
2088 ] {
2089 let s1 = new_section(vaddr_base, 10);
2090 let s2 = new_section(vaddr_base + 20, 10);
2091 let s3 = new_section(vaddr_base + 40, 10);
2092 let s4 = new_section(vaddr_base + 50, 10);
2093 let sections: [(Option<&[u8]>, &Elf64Shdr); 4] = [
2094 (Some(b".dynsym"), &s1),
2095 (Some(b".text"), &s2),
2096 (Some(b".rodata"), &s3),
2097 (Some(b".dynamic"), &s4),
2098 ];
2099 assert_eq!(
2100 ElfExecutable::parse_ro_sections(&config, &sbpf_version, sections, &elf_bytes),
2101 Ok(Section::Borrowed(
2102 ebpf::MM_RODATA_START as usize + 20,
2103 20..50
2104 ))
2105 );
2106 }
2107 }
2108
2109 #[test]
2110 fn test_borrowed_ro_region_no_initial_gap() {
2111 let config = Config::default();
2112 let elf_bytes = [0u8; 512];
2113 for (vaddr_base, sbpf_version) in [
2114 (0, SBPFVersion::V0),
2115 (ebpf::MM_RODATA_START, SBPFVersion::V3),
2116 ] {
2117 let s1 = new_section(vaddr_base, 10);
2118 let s2 = new_section(vaddr_base + 10, 10);
2119 let s3 = new_section(vaddr_base + 20, 10);
2120 let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
2121 (Some(b".text"), &s1),
2122 (Some(b".rodata"), &s2),
2123 (Some(b".dynamic"), &s3),
2124 ];
2125 let ro_section =
2126 ElfExecutable::parse_ro_sections(&config, &sbpf_version, sections, &elf_bytes)
2127 .unwrap();
2128 let ro_region = get_ro_region(&ro_section, &elf_bytes);
2129
2130 assert!(matches!(
2133 ro_region.vm_to_host(ebpf::MM_RODATA_START + s1.sh_offset, s2.sh_offset + s2.sh_size),
2134 ProgramResult::Ok(ptr) if ptr == elf_bytes.as_ptr() as u64,
2135 ));
2136
2137 assert_error!(
2139 ro_region.vm_to_host(ebpf::MM_RODATA_START + s3.sh_offset, 1),
2140 "InvalidVirtualAddress({})",
2141 ebpf::MM_RODATA_START + s3.sh_offset
2142 );
2143 }
2144 }
2145
2146 #[test]
2147 fn test_borrowed_ro_region_initial_gap() {
2148 let config = Config::default();
2149 let elf_bytes = [0u8; 512];
2150 for (vaddr_base, sbpf_version) in [
2151 (0, SBPFVersion::V0),
2152 (ebpf::MM_RODATA_START, SBPFVersion::V3),
2153 ] {
2154 let s1 = new_section(vaddr_base, 10);
2155 let s2 = new_section(vaddr_base + 10, 10);
2156 let s3 = new_section(vaddr_base + 20, 10);
2157 let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
2158 (Some(b".dynamic"), &s1),
2159 (Some(b".text"), &s2),
2160 (Some(b".rodata"), &s3),
2161 ];
2162 let ro_section =
2163 ElfExecutable::parse_ro_sections(&config, &sbpf_version, sections, &elf_bytes)
2164 .unwrap();
2165 let ro_region = get_ro_region(&ro_section, &elf_bytes);
2166
2167 assert_error!(
2171 ro_region.vm_to_host(ebpf::MM_RODATA_START + s1.sh_offset, 1),
2172 "InvalidVirtualAddress({})",
2173 ebpf::MM_RODATA_START + s1.sh_offset
2174 );
2175
2176 assert_error!(
2178 ro_region.vm_to_host(ebpf::MM_RODATA_START + s2.sh_offset - 1, 1),
2179 "InvalidVirtualAddress({})",
2180 ebpf::MM_RODATA_START + s2.sh_offset - 1
2181 );
2182
2183 assert!(matches!(
2185 ro_region.vm_to_host(
2186 ebpf::MM_RODATA_START + s2.sh_offset,
2187 s3.sh_offset + s3.sh_size - s2.sh_offset
2188 ),
2189 ProgramResult::Ok(ptr) if ptr == elf_bytes[s2.sh_offset as usize..].as_ptr() as u64,
2190 ));
2191
2192 assert_error!(
2194 ro_region.vm_to_host(ebpf::MM_RODATA_START + s3.sh_offset + s3.sh_size, 1),
2195 "InvalidVirtualAddress({})",
2196 ebpf::MM_RODATA_START + s3.sh_offset + s3.sh_size
2197 );
2198 }
2199 }
2200
2201 #[test]
2202 fn test_reject_rodata_stack_overlap() {
2203 let config = Config {
2204 enabled_sbpf_versions: SBPFVersion::V0..=SBPFVersion::V3,
2205 ..Config::default()
2206 };
2207 let elf_bytes = [0u8; 512];
2208
2209 let mut s1 = new_section(ebpf::MM_STACK_START - 10, 10);
2211 s1.sh_offset = 0;
2212 let sections: [(Option<&[u8]>, &Elf64Shdr); 1] = [(Some(b".text"), &s1)];
2213 assert!(
2214 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V3, sections, &elf_bytes)
2215 .is_ok()
2216 );
2217
2218 let mut s1 = new_section(ebpf::MM_STACK_START, 0);
2220 s1.sh_offset = 0;
2221 let sections: [(Option<&[u8]>, &Elf64Shdr); 1] = [(Some(b".text"), &s1)];
2222 assert!(
2223 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V3, sections, &elf_bytes)
2224 .is_ok()
2225 );
2226
2227 let mut s1 = new_section(ebpf::MM_STACK_START, 1);
2229 s1.sh_offset = 0;
2230 let sections: [(Option<&[u8]>, &Elf64Shdr); 1] = [(Some(b".text"), &s1)];
2231 assert_eq!(
2232 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V3, sections, &elf_bytes),
2233 Err(ElfError::ValueOutOfBounds)
2234 );
2235
2236 let mut s1 = new_section(ebpf::MM_STACK_START - 10, 11);
2238 s1.sh_offset = 0;
2239 let sections: [(Option<&[u8]>, &Elf64Shdr); 1] = [(Some(b".text"), &s1)];
2240 assert_eq!(
2241 ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V3, sections, &elf_bytes),
2242 Err(ElfError::ValueOutOfBounds)
2243 );
2244 }
2245
2246 #[test]
2247 #[should_panic(expected = r#"validation failed: WritableSectionNotSupported(".data")"#)]
2248 fn test_writable_data_section() {
2249 let elf_bytes =
2250 std::fs::read("tests/elfs/data_section_sbpfv0.so").expect("failed to read elf file");
2251 ElfExecutable::load(&elf_bytes, loader()).expect("validation failed");
2252 }
2253
2254 #[test]
2255 #[should_panic(expected = r#"validation failed: WritableSectionNotSupported(".bss")"#)]
2256 fn test_bss_section() {
2257 let elf_bytes =
2258 std::fs::read("tests/elfs/bss_section_sbpfv0.so").expect("failed to read elf file");
2259 ElfExecutable::load(&elf_bytes, loader()).expect("validation failed");
2260 }
2261
2262 #[test]
2263 #[should_panic(expected = "validation failed: InvalidProgramHeader")]
2264 fn test_program_headers_overflow() {
2265 let elf_bytes = std::fs::read("tests/elfs/program_headers_overflow.so")
2266 .expect("failed to read elf file");
2267 ElfExecutable::load(&elf_bytes, loader()).expect("validation failed");
2268 }
2269
2270 #[test]
2271 #[should_panic(expected = "validation failed: RelativeJumpOutOfBounds(8)")]
2272 fn test_relative_call_oob_backward() {
2273 let mut elf_bytes =
2274 std::fs::read("tests/elfs/relative_call_sbpfv0.so").expect("failed to read elf file");
2275 LittleEndian::write_i32(&mut elf_bytes[0x1044..0x1048], -11i32);
2276 ElfExecutable::load(&elf_bytes, loader()).expect("validation failed");
2277 }
2278
2279 #[test]
2280 #[should_panic(expected = "validation failed: RelativeJumpOutOfBounds(11)")]
2281 fn test_relative_call_oob_forward() {
2282 let mut elf_bytes =
2283 std::fs::read("tests/elfs/relative_call_sbpfv0.so").expect("failed to read elf file");
2284 LittleEndian::write_i32(&mut elf_bytes[0x105C..0x1060], 5);
2285 ElfExecutable::load(&elf_bytes, loader()).expect("validation failed");
2286 }
2287
2288 #[test]
2289 #[should_panic(expected = "validation failed: UnresolvedSymbol(\"log\", 39, 312)")]
2290 fn test_err_unresolved_syscall_reloc_64_32() {
2291 let loader = BuiltinProgram::new_loader(
2292 Config {
2293 enabled_sbpf_versions: SBPFVersion::V0..=SBPFVersion::V0,
2294 reject_broken_elfs: true,
2295 ..Config::default()
2296 },
2297 FunctionRegistry::default(),
2298 );
2299 let elf_bytes = std::fs::read("tests/elfs/syscall_reloc_64_32_sbpfv0.so")
2300 .expect("failed to read elf file");
2301 ElfExecutable::load(&elf_bytes, Arc::new(loader)).expect("validation failed");
2302 }
2303
2304 #[test]
2305 fn test_long_section_name() {
2306 let elf_bytes = std::fs::read("tests/elfs/long_section_name.so").unwrap();
2307 assert_error!(
2308 Elf64::parse(&elf_bytes),
2309 "StringTooLong({:?}, {})",
2310 ".bss.__rust_no_alloc_shim_is_unstable"
2311 .get(0..SECTION_NAME_LENGTH_MAXIMUM)
2312 .unwrap(),
2313 SECTION_NAME_LENGTH_MAXIMUM
2314 );
2315 }
2316}