Skip to content
This repository was archived by the owner on Jan 10, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 112 additions & 94 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,9 @@ impl<V: Verifier, C: ContextObject> Executable<V, C> {
.saturating_add(1)
.saturating_sub(first_ro_section)
== n_ro_sections;
if sbpf_version.enable_elf_vaddr() && !can_borrow {
return Err(ElfError::ValueOutOfBounds);
}
let ro_section = if config.optimize_rodata && can_borrow {
// Read only sections are grouped together with no intermixed non-ro
// sections. We can borrow.
Expand All @@ -924,6 +927,9 @@ impl<V: Verifier, C: ContextObject> Executable<V, C> {
// it now.
lowest_addr.saturating_sub(ebpf::MM_PROGRAM_START as usize)
} else {
if sbpf_version.enable_elf_vaddr() {
return Err(ElfError::ValueOutOfBounds);
}
lowest_addr
};

Expand Down Expand Up @@ -1760,7 +1766,9 @@ mod test {
fn new_section(sh_addr: u64, sh_size: u64) -> Elf64Shdr {
Elf64Shdr {
sh_addr,
sh_offset: sh_addr,
sh_offset: sh_addr
.checked_sub(ebpf::MM_PROGRAM_START)
.unwrap_or(sh_addr),
sh_size,
sh_name: 0,
sh_type: 0,
Expand Down Expand Up @@ -1790,7 +1798,7 @@ mod test {
assert!(matches!(
ElfExecutable::parse_ro_sections(
&config,
&SBPFVersion::V2,
&SBPFVersion::V1,
sections,
&elf_bytes,
),
Expand All @@ -1817,7 +1825,7 @@ mod test {
assert!(matches!(
ElfExecutable::parse_ro_sections(
&config,
&SBPFVersion::V2,
&SBPFVersion::V1,
sections,
&elf_bytes,
),
Expand Down Expand Up @@ -1895,7 +1903,7 @@ mod test {
let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
[(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
assert_eq!(
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes,),
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes),
Err(ElfError::ValueOutOfBounds)
);
}
Expand All @@ -1917,7 +1925,7 @@ mod test {
let sections: [(Option<&[u8]>, &Elf64Shdr); 2] =
[(Some(b".text"), &s1), (Some(b".rodata"), &s2)];
assert_eq!(
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes,),
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes),
Ok(Section::Borrowed(10, 100..120))
);
}
Expand All @@ -1938,7 +1946,7 @@ mod test {
(Some(b".rodata"), &s3),
];
let ro_section =
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes)
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V1, sections, &elf_bytes)
.unwrap();
let ro_region = get_ro_region(&ro_section, &elf_bytes);
let owned_section = match &ro_section {
Expand Down Expand Up @@ -2020,7 +2028,7 @@ mod test {
(Some(b".rodata"), &s3),
];
let ro_section =
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes)
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V1, sections, &elf_bytes)
.unwrap();
let owned_section = match &ro_section {
Section::Owned(_offset, data) => data.as_slice(),
Expand Down Expand Up @@ -2091,107 +2099,117 @@ mod test {
fn test_borrowed_ro_sections() {
let config = Config::default();
let elf_bytes = [0u8; 512];

let s1 = new_section(0, 10);
let s2 = new_section(20, 10);
let s3 = new_section(40, 10);
let s4 = new_section(50, 10);

let sections: [(Option<&[u8]>, &Elf64Shdr); 4] = [
(Some(b".dynsym"), &s1),
(Some(b".text"), &s2),
(Some(b".rodata"), &s3),
(Some(b".dynamic"), &s4),
];
assert_eq!(
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes,),
Ok(Section::Borrowed(20, 20..50))
);
for (vaddr_base, sbpf_version) in [
(0, SBPFVersion::V1),
(ebpf::MM_PROGRAM_START, SBPFVersion::V2),
] {
let s1 = new_section(vaddr_base, 10);
let s2 = new_section(vaddr_base + 20, 10);
let s3 = new_section(vaddr_base + 40, 10);
let s4 = new_section(vaddr_base + 50, 10);
let sections: [(Option<&[u8]>, &Elf64Shdr); 4] = [
(Some(b".dynsym"), &s1),
(Some(b".text"), &s2),
(Some(b".rodata"), &s3),
(Some(b".dynamic"), &s4),
];
assert_eq!(
ElfExecutable::parse_ro_sections(&config, &sbpf_version, sections, &elf_bytes),
Ok(Section::Borrowed(20, 20..50))
);
}
}

#[test]
fn test_borrowed_ro_region_no_initial_gap() {
let config = Config::default();
let elf_bytes = [0u8; 512];
for (vaddr_base, sbpf_version) in [
(0, SBPFVersion::V1),
(ebpf::MM_PROGRAM_START, SBPFVersion::V2),
] {
let s1 = new_section(vaddr_base, 10);
let s2 = new_section(vaddr_base + 10, 10);
let s3 = new_section(vaddr_base + 20, 10);
let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
(Some(b".text"), &s1),
(Some(b".rodata"), &s2),
(Some(b".dynamic"), &s3),
];
let ro_section =
ElfExecutable::parse_ro_sections(&config, &sbpf_version, sections, &elf_bytes)
.unwrap();
let ro_region = get_ro_region(&ro_section, &elf_bytes);

// s1 starts at sh_offset=0 so [0..s2.sh_offset + s2.sh_size]
// is the valid ro memory area
assert!(matches!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s1.sh_offset, s2.sh_offset + s2.sh_size),
ProgramResult::Ok(ptr) if ptr == elf_bytes.as_ptr() as u64,
));

let s1 = new_section(0, 10);
let s2 = new_section(10, 10);
let s3 = new_section(10, 10);

let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
(Some(b".text"), &s1),
(Some(b".rodata"), &s2),
(Some(b".dynamic"), &s3),
];
let ro_section =
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes)
.unwrap();
let ro_region = get_ro_region(&ro_section, &elf_bytes);

// s1 starts at sh_addr=0 so [0..s2.sh_addr + s2.sh_size] is the valid
// ro memory area
assert!(matches!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START, s2.sh_addr + s2.sh_size),
ProgramResult::Ok(ptr) if ptr == elf_bytes.as_ptr() as u64,
));

// one byte past the ro section is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s2.sh_addr + s2.sh_size, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + s2.sh_addr + s2.sh_size
);
// one byte past the ro section is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s3.sh_offset, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + s3.sh_offset
);
}
}

#[test]
fn test_borrowed_ro_region_initial_gap() {
let config = Config::default();
let elf_bytes = [0u8; 512];
let s1 = new_section(0, 10);
let s2 = new_section(10, 10);
let s3 = new_section(20, 10);

let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
(Some(b".dynamic"), &s1),
(Some(b".text"), &s2),
(Some(b".rodata"), &s3),
];
let ro_section =
ElfExecutable::parse_ro_sections(&config, &SBPFVersion::V2, sections, &elf_bytes)
.unwrap();
let ro_region = get_ro_region(&ro_section, &elf_bytes);

// s2 starts at sh_addr=10 so [0..10] is not mappable

// the low bound of the initial gap is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START
);

// the hi bound of the initial gap is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s2.sh_addr - 1, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + 9
);

// [s2.sh_addr..s3.sh_addr + s3.sh_size] is the valid ro memory area
assert!(matches!(
ro_region.vm_to_host(
ebpf::MM_PROGRAM_START + s2.sh_addr,
s3.sh_addr + s3.sh_size - s2.sh_addr
),
ProgramResult::Ok(ptr) if ptr == elf_bytes[s2.sh_addr as usize..].as_ptr() as u64,
));
for (vaddr_base, sbpf_version) in [
(0, SBPFVersion::V1),
(ebpf::MM_PROGRAM_START, SBPFVersion::V2),
] {
let s1 = new_section(vaddr_base, 10);
let s2 = new_section(vaddr_base + 10, 10);
let s3 = new_section(vaddr_base + 20, 10);
let sections: [(Option<&[u8]>, &Elf64Shdr); 3] = [
(Some(b".dynamic"), &s1),
(Some(b".text"), &s2),
(Some(b".rodata"), &s3),
];
let ro_section =
ElfExecutable::parse_ro_sections(&config, &sbpf_version, sections, &elf_bytes)
.unwrap();
let ro_region = get_ro_region(&ro_section, &elf_bytes);

// s2 starts at sh_addr=10 so [0..10] is not mappable

// the low bound of the initial gap is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s1.sh_offset, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + s1.sh_offset
);

// the hi bound of the initial gap is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s2.sh_offset - 1, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + s2.sh_offset - 1
);

// [s2.sh_offset..s3.sh_offset + s3.sh_size] is the valid ro memory area
assert!(matches!(
ro_region.vm_to_host(
ebpf::MM_PROGRAM_START + s2.sh_offset,
s3.sh_offset + s3.sh_size - s2.sh_offset
),
ProgramResult::Ok(ptr) if ptr == elf_bytes[s2.sh_offset as usize..].as_ptr() as u64,
));

// one byte past the ro section is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s3.sh_addr + s3.sh_size, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + s3.sh_addr + s3.sh_size
);
// one byte past the ro section is not mappable
assert_error!(
ro_region.vm_to_host(ebpf::MM_PROGRAM_START + s3.sh_offset + s3.sh_size, 1),
"InvalidVirtualAddress({})",
ebpf::MM_PROGRAM_START + s3.sh_offset + s3.sh_size
);
}
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions tests/elfs/elf.ld
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SECTIONS
. = SIZEOF_HEADERS;
.text : { *(.text) } :text
.rodata : { *(.rodata*) } :rodata
.data.rel.ro : { *(.data.rel.ro) } :rodata
.dynamic : { *(.dynamic) } :dynamic
.dynsym : { *(.dynsym) } :dynamic
.dynstr : { *(.dynstr) } :dynamic
Expand Down
6 changes: 1 addition & 5 deletions tests/elfs/elfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ rm reloc_64_relative.o
rm reloc_64_relative_data.o

"$LLVM_DIR"clang $CC_FLAGS -o reloc_64_relative_data.o -c reloc_64_relative_data.c
"$LLVM_DIR"ld.lld $LD_FLAGS --section-start=.text=0x100000000 -o reloc_64_relative_data.so reloc_64_relative_data.o
rm reloc_64_relative_data.o

"$LLVM_DIR"clang $CC_FLAGS_V1 -o reloc_64_relative_data.o -c reloc_64_relative_data.c
"$LLVM_DIR"ld.lld $LD_FLAGS_V1 -o reloc_64_relative_data_sbpfv1.so reloc_64_relative_data.o
"$LLVM_DIR"ld.lld $LD_FLAGS -o reloc_64_relative_data.so reloc_64_relative_data.o
rm reloc_64_relative_data.o

"$LLVM_DIR"clang $CC_FLAGS -o scratch_registers.o -c scratch_registers.c
Expand Down
Binary file modified tests/elfs/reloc_64_relative_data.so
Binary file not shown.
Binary file modified tests/elfs/struct_func_pointer.so
Binary file not shown.