jupnet_rbpf/
elf.rs

1//! This module relocates a BPF ELF
2
3// Note: Typically ELF shared objects are loaded using the program headers and
4// not the section headers.  Since we are leveraging the elfkit crate its much
5// easier to use the section headers.  There are cases (reduced size, obfuscation)
6// where the section headers may be removed from the ELF.  If that happens then
7// this loader will need to be re-written to use the program headers instead.
8
9use 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/// Error definitions
39#[derive(Debug, thiserror::Error, PartialEq, Eq)]
40pub enum ElfError {
41    /// Failed to parse ELF file
42    #[error("Failed to parse ELF file: {0}")]
43    FailedToParse(String),
44    /// Entrypoint out of bounds
45    #[error("Entrypoint out of bounds")]
46    EntrypointOutOfBounds,
47    /// Invalid entrypoint
48    #[error("Invalid entrypoint")]
49    InvalidEntrypoint,
50    /// Failed to get section
51    #[error("Failed to get section {0}")]
52    FailedToGetSection(String),
53    /// Unresolved symbol
54    #[error("Unresolved symbol ({0}) at instruction #{1:?} (ELF file offset {2:#x})")]
55    UnresolvedSymbol(String, usize, usize),
56    /// Section not found
57    #[error("Section not found: {0}")]
58    SectionNotFound(String),
59    /// Relative jump out of bounds
60    #[error("Relative jump out of bounds at instruction #{0}")]
61    RelativeJumpOutOfBounds(usize),
62    /// Symbol hash collision
63    #[error("Symbol hash collision {0:#x}")]
64    SymbolHashCollision(u32),
65    /// Incompatible ELF: wrong endianess
66    #[error("Incompatible ELF: wrong endianess")]
67    WrongEndianess,
68    /// Incompatible ELF: wrong ABI
69    #[error("Incompatible ELF: wrong ABI")]
70    WrongAbi,
71    /// Incompatible ELF: wrong machine
72    #[error("Incompatible ELF: wrong machine")]
73    WrongMachine,
74    /// Incompatible ELF: wrong class
75    #[error("Incompatible ELF: wrong class")]
76    WrongClass,
77    /// Not one text section
78    #[error("Multiple or no text sections, consider removing llc option: -function-sections")]
79    NotOneTextSection,
80    /// Read-write data not supported
81    #[error("Found writable section ({0}) in ELF, read-write data not supported")]
82    WritableSectionNotSupported(String),
83    /// Relocation failed, no loadable section contains virtual address
84    #[error("Relocation failed, no loadable section contains virtual address {0:#x}")]
85    AddressOutsideLoadableSection(u64),
86    /// Relocation failed, invalid referenced virtual address
87    #[error("Relocation failed, invalid referenced virtual address {0:#x}")]
88    InvalidVirtualAddress(u64),
89    /// Relocation failed, unknown type
90    #[error("Relocation failed, unknown type {0:?}")]
91    UnknownRelocation(u32),
92    /// Failed to read relocation info
93    #[error("Failed to read relocation info")]
94    FailedToReadRelocationInfo,
95    /// Incompatible ELF: wrong type
96    #[error("Incompatible ELF: wrong type")]
97    WrongType,
98    /// Unknown symbol
99    #[error("Unknown symbol with index {0}")]
100    UnknownSymbol(usize),
101    /// Offset or value is out of bounds
102    #[error("Offset or value is out of bounds")]
103    ValueOutOfBounds,
104    /// Detected sbpf_version required by the executable which are not enabled
105    #[error("Detected sbpf_version required by the executable which are not enabled")]
106    UnsupportedSBPFVersion,
107    /// Invalid program header
108    #[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
148// For more information on the BPF instruction set:
149// https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
150
151// msb                                                        lsb
152// +------------------------+----------------+----+----+--------+
153// |immediate               |offset          |src |dst |opcode  |
154// +------------------------+----------------+----+----+--------+
155
156// From least significant to most significant bit:
157//   8 bit opcode
158//   4 bit destination register (dst)
159//   4 bit source register (src)
160//   16 bit offset
161//   32 bit immediate (imm)
162
163/// Byte offset of the immediate field in the instruction
164const BYTE_OFFSET_IMMEDIATE: usize = 4;
165/// Byte length of the immediate field
166const BYTE_LENGTH_IMMEDIATE: usize = 4;
167
168/// BPF relocation types.
169#[allow(non_camel_case_types)]
170#[derive(Debug, PartialEq, Copy, Clone)]
171enum BpfRelocationType {
172    /// No relocation, placeholder
173    R_Bpf_None = 0,
174    /// R_BPF_64_64 relocation type is used for ld_imm64 instruction.
175    /// The actual to-be-relocated data (0 or section offset) is
176    /// stored at r_offset + 4 and the read/write data bitsize is 32
177    /// (4 bytes). The relocation can be resolved with the symbol
178    /// value plus implicit addend.
179    R_Bpf_64_64 = 1,
180    /// 64 bit relocation of a ldxdw instruction.  The ldxdw
181    /// instruction occupies two instruction slots. The 64-bit address
182    /// to load from is split into the 32-bit imm field of each
183    /// slot. The first slot's pre-relocation imm field contains the
184    /// virtual address (typically same as the file offset) of the
185    /// location to load. Relocation involves calculating the
186    /// post-load 64-bit physical address referenced by the imm field
187    /// and writing that physical address back into the imm fields of
188    /// the ldxdw instruction.
189    R_Bpf_64_Relative = 8,
190    /// Relocation of a call instruction.  The existing imm field
191    /// contains either an offset of the instruction to jump to (think
192    /// local function call) or a special value of "-1".  If -1 the
193    /// symbol must be looked up in the symbol table.  The relocation
194    /// entry contains the symbol number to call.  In order to support
195    /// both local jumps and calling external symbols a 32-bit hash is
196    /// computed and stored in the the call instruction's 32-bit imm
197    /// field.  The hash is used later to look up the 64-bit address
198    /// to jump to.  In the case of a local jump the hash is
199    /// calculated using the current program counter and in the case
200    /// of a symbol the hash is calculated using the name of the
201    /// symbol.
202    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 section data.
219    ///
220    /// The first field is virtual address of the section.
221    /// The second field is the actual section data.
222    Owned(usize, Vec<u8>),
223    /// Borrowed section data.
224    ///
225    /// The first field is virtual address of the section.
226    /// The second field can be used to index the input ELF buffer to
227    /// retrieve the section data.
228    Borrowed(usize, Range<usize>),
229}
230
231/// Elf loader/relocator
232#[derive(Debug, PartialEq)]
233pub struct Executable<C: ContextObject> {
234    /// Loaded and executable elf
235    elf_bytes: AlignedMemory<{ HOST_ALIGN }>,
236    /// Required SBPF capabilities
237    sbpf_version: SBPFVersion,
238    /// Read-only section
239    ro_section: Section,
240    /// Text section virtual address
241    text_section_vaddr: u64,
242    /// Text section range in `elf_bytes`
243    text_section_range: Range<usize>,
244    /// Address of the entry point
245    entry_pc: usize,
246    /// Call resolution map (hash, pc, name)
247    function_registry: FunctionRegistry<usize>,
248    /// Loader built-in program
249    loader: Arc<BuiltinProgram<C>>,
250    /// Compiled program and argument
251    #[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    /// Get the configuration settings
257    pub fn get_config(&self) -> &Config {
258        self.loader.get_config()
259    }
260
261    /// Get the executable sbpf_version
262    pub fn get_sbpf_version(&self) -> SBPFVersion {
263        self.sbpf_version
264    }
265
266    /// Get the .text section virtual address and bytes
267    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    /// Get the concatenated read-only sections (including the text section)
275    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    /// Get a memory region that can be used to access the merged readonly section
285    pub fn get_ro_region(&self) -> MemoryRegion {
286        get_ro_region(&self.ro_section, self.elf_bytes.as_slice())
287    }
288
289    /// Get the entry point offset into the text section
290    pub fn get_entrypoint_instruction_offset(&self) -> usize {
291        self.entry_pc
292    }
293
294    /// Get the text section offset in the ELF file
295    #[cfg(feature = "debugger")]
296    pub fn get_text_section_offset(&self) -> u64 {
297        self.text_section_range.start as u64
298    }
299
300    /// Get the loader built-in program
301    pub fn get_loader(&self) -> &Arc<BuiltinProgram<C>> {
302        &self.loader
303    }
304
305    /// Get the JIT compiled program
306    #[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    /// Verify the executable
312    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    /// JIT compile the executable
324    #[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    /// Get the function registry
332    pub fn get_function_registry(&self) -> &FunctionRegistry<usize> {
333        &self.function_registry
334    }
335
336    /// Create from raw text section bytes (list of instructions)
337    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    /// Fully loads an ELF
374    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    /// Loads an ELF without relocation
411    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_entry
442            || file_header.e_phoff != mem::size_of::<Elf64Ehdr>() as u64
443            // file_header.e_shoff
444            // file_header.e_flags
445            || 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_shnum
451            || 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), // byte code
458            (PT_LOAD, PF_R, ebpf::MM_RODATA_START),   // read only data
459            (PT_GNU_STACK, PF_R | PF_W, ebpf::MM_STACK_START), // stack
460            (PT_LOAD, PF_R | PF_W, ebpf::MM_HEAP_START), // heap
461            (PT_NULL, 0, 0xFFFFFFFF00000000),         // dynamic symbol table
462        ];
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, // Is set in Self::load()
596            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    /// Loads an ELF with relocation
608    fn load_with_lenient_parser(
609        bytes: &[u8],
610        loader: Arc<BuiltinProgram<C>>,
611    ) -> Result<Self, ElfError> {
612        // We always need one memory copy to take ownership and for relocations
613        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                // We might need another memory copy to ensure alignment
619                (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        // calculate the text section info
634        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        // relocate symbols
655        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        // calculate entrypoint offset into the text section
664        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    /// Calculate the total memory size of the executable
707    #[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            // elf bytes
713            .saturating_add(self.elf_bytes.mem_size())
714            // ro section
715            .saturating_add(match &self.ro_section {
716                Section::Owned(_, data) => data.capacity(),
717                Section::Borrowed(_, _) => 0,
718            })
719            // bpf functions
720            .saturating_add(self.function_registry.mem_size());
721
722        #[cfg(all(feature = "jit", not(target_os = "windows"), target_arch = "x86_64"))]
723        {
724            // compiled programs
725            total = total.saturating_add(self.compiled_program.as_ref().map_or(0, |program| program.mem_size()));
726        }
727
728        total
729    }
730
731    // Functions exposed for tests
732
733    /// Validates the ELF
734    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                // When optimize_rodata=false, we allocate a vector and copy all
764                // rodata sections into it. In that case we can't allow virtual
765                // addresses or we'd potentially have to do huge allocations.
766                return Err(ElfError::UnsupportedSBPFVersion);
767            }
768
769            // The toolchain currently emits up to 4 program headers. 10 is a
770            // future proof nice round number.
771            //
772            // program_headers() returns an ExactSizeIterator so count doesn't
773            // actually iterate again.
774            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        // the lowest section address
835        let mut lowest_addr = usize::MAX;
836        // the highest section address
837        let mut highest_addr = 0;
838        // the aggregated section length, not including gaps between sections
839        let mut ro_fill_length = 0usize;
840        let mut invalid_offsets = false;
841        // when sbpf_version.enable_elf_vaddr()=true, we allow section_addr != sh_offset
842        // if section_addr - sh_offset is constant across all sections. That is,
843        // we allow sections to be translated by a fixed virtual offset.
844        let mut addr_file_offset = None;
845
846        // keep track of where ro sections are so we can tell whether they're
847        // contiguous
848        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            // sh_offset handling:
872            //
873            // If sbpf_version.enable_elf_vaddr()=true, we allow section_addr >
874            // sh_offset, if section_addr - sh_offset is constant across all
875            // sections. That is, we allow the linker to align rodata to a
876            // positive base address (MM_RODATA_START) as long as the mapping
877            // to sh_offset(s) stays linear.
878            //
879            // If sbpf_version.enable_elf_vaddr()=false, section_addr must match
880            // sh_offset for backwards compatibility
881            if !invalid_offsets {
882                if sbpf_version.enable_elf_vaddr() {
883                    // This is enforced in validate()
884                    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                            // The sections are not all translated by the same
891                            // constant. We won't be able to borrow, but unless
892                            // config.reject_broken_elf=true, we're still going
893                            // to accept this file for backwards compatibility.
894                            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            // Read only sections are grouped together with no intermixed non-ro
941            // sections. We can borrow.
942
943            // When sbpf_version.enable_elf_vaddr()=true, section addresses and their
944            // corresponding buffer offsets can be translated by a constant
945            // amount. Subtract the constant to get buffer positions.
946            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                // The first field of Section::Borrowed is an offset from
953                // ebpf::MM_RODATA_START so if the linker has already put the
954                // sections within ebpf::MM_RODATA_START, we need to subtract
955                // it now.
956                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            // Read only and other non-ro sections are mixed. Zero the non-ro
967            // sections and and copy the ro ones at their intended offsets.
968
969            if config.optimize_rodata {
970                // The rodata region starts at MM_RODATA_START + offset,
971                // [MM_RODATA_START, MM_RODATA_START + offset) is not
972                // mappable. We only need to allocate highest_addr - lowest_addr
973                // bytes.
974                highest_addr = highest_addr.saturating_sub(lowest_addr);
975            } else {
976                // For backwards compatibility, the whole [MM_RODATA_START,
977                // MM_RODATA_START + highest_addr) range is mappable. We need
978                // to allocate the whole address range.
979                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    /// Relocates the ELF in-place
1006    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        // Fixup all program counter relative call instructions
1021        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        // Fixup all the relocations in the relocation section if exists
1062        for relocation in elf.dynamic_relocations_table().unwrap_or_default().iter() {
1063            let mut r_offset = relocation.r_offset as usize;
1064
1065            // When sbpf_version.enable_elf_vaddr()=true, we allow section.sh_addr !=
1066            // section.sh_offset so we need to bring r_offset to the correct
1067            // byte offset.
1068            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                    // Offset of the immediate field
1087                    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                    // Read the instruction's immediate field which contains virtual
1099                    // address to convert to physical
1100                    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                    // The relocated address is relative to the address of the
1111                    // symbol at index `r_sym`
1112                    let mut addr = symbol.st_value.saturating_add(refd_addr);
1113
1114                    // The "physical address" from the VM's perspective is rooted
1115                    // at `MM_RODATA_START`. If the linker hasn't already put
1116                    // the symbol within `MM_RODATA_START`, we need to do so
1117                    // now.
1118                    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                        // Write the low side of the relocate address
1132                        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                        // Write the high side of the relocate address
1141                        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                    // Relocation between different sections, where the target
1160                    // memory is not associated to a symbol (eg some compiler
1161                    // generated rodata that doesn't have an explicit symbol).
1162
1163                    // Offset of the immediate field
1164                    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                        // We're relocating a lddw instruction, which spans two
1172                        // instruction slots. The address to be relocated is
1173                        // split in two halves in the two imms of the
1174                        // instruction slots.
1175                        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                        // Read the low side of the address
1181                        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                        // Read the high side of the address
1190                        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                        // Put the address back together
1199                        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                            // The linker hasn't already placed rodata within
1207                            // MM_RODATA_START, so we do so now
1208                            refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1209                        }
1210
1211                        // Write back the low half
1212                        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                        // Write back the high half
1221                        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                            // We're relocating an address inside a data section (eg .rodata). The
1234                            // address is encoded as a simple u64.
1235
1236                            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                                // Not within MM_RODATA_START, do it now
1242                                refd_addr = ebpf::MM_RODATA_START.saturating_add(refd_addr);
1243                            }
1244                            refd_addr
1245                        } else {
1246                            // There used to be a bug in toolchains before
1247                            // https://github.com/solana-labs/llvm-project/pull/35 where for 64 bit
1248                            // relocations we were encoding only the low 32 bits, shifted 32 bits to
1249                            // the left. Our relocation code used to be compatible with that, so we
1250                            // need to keep supporting this case for backwards compatibility.
1251                            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                    // The .text section has an unresolved call to symbol instruction
1266                    // Hash the symbol name and stick it into the call instruction's imm
1267                    // field.  Later that hash will be used to look up the function location.
1268
1269                    // Offset of the immediate field
1270                    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                    // If the symbol is defined, this is a bpf-to-bpf call
1282                    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                        // Else it's a syscall
1298                        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            // Register all known function names from the symbol table
1327            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    // If offset > 0, the region will start at MM_RODATA_START + the offset of
1374    // the first read only byte. [MM_RODATA_START, MM_RODATA_START + offset)
1375    // will be unmappable, see MemoryRegion::vm_to_host.
1376    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            // FIXME consts::{ELFCLASS32, ELFDATA2MSB, ET_REL},
1385            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        // Check that the unmodified file can be parsed
1422        {
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        // Check that using a reserved SBPF version fails
1436        {
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        // Check that an empty file fails
1444        let err = ElfExecutable::load_with_strict_parser(&[], loader.clone()).unwrap_err();
1445        assert_eq!(err, ElfParserError::OutOfBounds);
1446
1447        // Break the file header one byte at a time
1448        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        // Break the program header table one byte at a time
1465        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        // Break the dynamic symbol table one byte at a time
1500        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        // Check that an empty function symbol fails
1519        {
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        // Check that bytecode not covered by function symbols fails
1528        {
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        // Check that an entrypoint not covered by function symbols fails
1537        {
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        // the new parser rejects anything other than ELFCLASS64 directly
1565        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        // the new parser only supports little endian
1575        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        // The default allocator allocates aligned memory. Move the ELF slice to
1622        // elf_bytes.as_ptr() + 1 to make it unaligned and test unaligned
1623        // parsing.
1624        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        // Random bytes, will mostly fail due to lack of ELF header so just do a few
1690        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        // Take a real elf and mangle it
1699
1700        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        // focus on elf header, small typically 64 bytes
1707        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        // focus on section headers
1720        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        // mangle whole elf randomly
1733        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        // there's a non-rodata section between two rodata sections
1769        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        // s2 is at a custom sh_offset. We need to merge into an owned buffer so
1798        // s2 can be moved to the right address offset.
1799        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        // sh_offset > sh_addr is invalid
1856        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        // The sections don't have a constant offset. This is rejected since it
1878        // makes it impossible to efficiently map virtual addresses to byte
1879        // offsets
1880        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        // the sections have a constant offset (100)
1902        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        // need an owned buffer so we can zero the address space taken by s2
1922        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        // [0..s3.sh_addr + s3.sh_size] is the valid ro memory area
1941        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        // one byte past the ro section is not mappable
1947        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        // the first section starts at a non-zero offset
1963        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        // V2 requires optimize_rodata=true
1973        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        // [s1.sh_addr..s3.sh_addr + s3.sh_size] is where the readonly data is.
1983        // But for backwards compatibility (config.optimize_rodata=false)
1984        // [0..s1.sh_addr] is mappable too (and zeroed).
1985        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        // one byte past the ro section is not mappable
1991        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        // the first section starts at a non-zero offset
2004        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        // s1 starts at sh_addr=10 so [MM_RODATA_START..MM_RODATA_START + 10] is not mappable
2023
2024        // the low bound of the initial gap is not mappable
2025        assert_error!(
2026            ro_region.vm_to_host(ebpf::MM_RODATA_START, 1),
2027            "InvalidVirtualAddress({})",
2028            ebpf::MM_RODATA_START
2029        );
2030
2031        // the hi bound of the initial gap is not mappable
2032        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        // [s1.sh_addr..s3.sh_addr + s3.sh_size] is the valid ro memory area
2039        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        // one byte past the ro section is not mappable
2048        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        // s1 and s2 are contiguous, the rodata section can be borrowed from the
2064        // original elf input but config.borrow_rodata=false
2065        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, // v2 requires optimize_rodata=true
2074                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            // s1 starts at sh_offset=0 so [0..s2.sh_offset + s2.sh_size]
2131            // is the valid ro memory area
2132            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            // one byte past the ro section is not mappable
2138            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            // s2 starts at sh_addr=10 so [0..10] is not mappable
2168
2169            // the low bound of the initial gap is not mappable
2170            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            // the hi bound of the initial gap is not mappable
2177            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            // [s2.sh_offset..s3.sh_offset + s3.sh_size] is the valid ro memory area
2184            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            // one byte past the ro section is not mappable
2193            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        // no overlap
2210        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        // no overlap
2219        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        // overlap
2228        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        // valid start but start + size overlap
2237        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}