skip ELF image validation

This commit is contained in:
Gautham 2025-01-12 07:42:29 -06:00
parent a9cb14668e
commit 850a7dab74

View file

@ -199,170 +199,6 @@ static void GetOpts(int argc, char *argv[]) {
exepath = argv[optind]; exepath = argv[optind];
} }
static void ValidateElfImage(Elf64_Ehdr *e, Elf64_Off esize, //
const char *epath) {
// validate elf header
if (e->e_type != ET_EXEC && e->e_type != ET_DYN)
Die(epath, "elf binary isn't an executable");
if (!e->e_phnum)
Die(epath, "elf executable needs at least one program header");
if (e->e_phnum > 65534)
Die(epath, "elf with more than 65534 phdrs not supported");
if (e->e_phentsize != sizeof(Elf64_Phdr))
Die(epath, "elf e_phentsize isn't sizeof(Elf64_Phdr)");
if (e->e_phoff > esize)
Die(epath, "elf program header offset points past image eof");
if (e->e_phoff & 7)
Die(epath, "elf e_phoff must be aligned on an 8-byte boundary");
if (e->e_phoff + e->e_phoff + e->e_phnum * sizeof(Elf64_Phdr) > esize)
Die(epath, "elf program header array overlaps image eof");
// determine microprocessor page size requirement
//
// even though operating systems (windows) and c libraries (cosmo)
// sometimes impose a larger page size requirement than the actual
// microprocessor itself, the cpu page size is still the only page
// size that actually matters when it comes to executable loading.
unsigned long pagesz;
if (e->e_machine == EM_AARCH64) {
pagesz = 16384; // apple m1 (xnu, linux)
} else { //
pagesz = 4096; // x86-64, raspberry pi
}
// remove empty segment loads
// empty loads should be inconsequential; linux ignores them
// however they tend to trip up many systems such as openbsd
int i, j;
Elf64_Phdr *p = (Elf64_Phdr *)((char *)e + e->e_phoff);
for (i = 0; i < e->e_phnum;) {
if (p[i].p_type == PT_LOAD && !p[i].p_memsz) {
if (i + 1 < e->e_phnum) {
memmove(p + i, p + i + 1, (e->e_phnum - (i + 1)) * sizeof(*p));
}
--e->e_phnum;
} else {
++i;
}
}
// oracle says loadable segment entries in the program header table
// appear in ascending order, sorted on the p_vaddr member.
int found_load = 0;
Elf64_Addr last_vaddr = 0;
for (i = 0; i < e->e_phnum; ++i) {
if (p[i].p_type != PT_LOAD)
continue;
if (found_load && p[i].p_vaddr <= last_vaddr) {
Die(epath, "ELF PT_LOAD segments must be ordered by p_vaddr");
}
last_vaddr = p[i].p_vaddr;
found_load = 1;
}
if (!found_load) {
Die(epath, "ELF must have at least one PT_LOAD segment");
}
// merge adjacent loads that are contiguous with equal protection
for (i = 0; i + 1 < e->e_phnum;) {
if (p[i].p_type == PT_LOAD && p[i + 1].p_type == PT_LOAD &&
((p[i].p_flags & (PF_R | PF_W | PF_X)) ==
(p[i + 1].p_flags & (PF_R | PF_W | PF_X))) &&
((p[i].p_offset + p[i].p_filesz + (pagesz - 1)) & -pagesz) -
(p[i + 1].p_offset & -pagesz) <=
pagesz &&
((p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz) -
(p[i + 1].p_vaddr & -pagesz) <=
pagesz) {
p[i].p_memsz = (p[i + 1].p_vaddr + p[i + 1].p_memsz) - p[i].p_vaddr;
p[i].p_filesz = (p[i + 1].p_offset + p[i + 1].p_filesz) - p[i].p_offset;
if (i + 2 < e->e_phnum) {
memmove(p + i + 1, p + i + 2, (e->e_phnum - (i + 2)) * sizeof(*p));
}
--e->e_phnum;
} else {
++i;
}
}
// validate program headers
bool found_entry = false;
for (i = 0; i < e->e_phnum; ++i) {
if (p[i].p_type == PT_INTERP) {
Die(epath, "ELF has PT_INTERP which isn't supported");
}
if (p[i].p_type == PT_DYNAMIC) {
Die(epath, "ELF has PT_DYNAMIC which isn't supported");
}
if (p[i].p_type != PT_LOAD) {
continue;
}
if (!p[i].p_memsz) {
Die(epath, "ELF PT_LOAD p_memsz was zero");
}
if (p[i].p_offset > esize) {
Die(epath, "ELF PT_LOAD p_offset points past EOF");
}
if (p[i].p_filesz > p[i].p_memsz) {
Die(epath, "ELF PT_LOAD p_filesz exceeds p_memsz");
}
if (p[i].p_align > 1 && (p[i].p_align & (p[i].p_align - 1))) {
Die(epath, "ELF PT_LOAD p_align must be two power");
}
if (p[i].p_vaddr + p[i].p_memsz < p[i].p_vaddr ||
p[i].p_vaddr + p[i].p_memsz + (pagesz - 1) < p[i].p_vaddr) {
Die(epath, "ELF PT_LOAD p_vaddr + p_memsz overflow");
}
if (p[i].p_offset + p[i].p_filesz < p[i].p_offset ||
p[i].p_offset + p[i].p_filesz + (pagesz - 1) < p[i].p_offset) {
Die(epath, "ELF PT_LOAD p_offset + p_filesz overflow");
}
if (p[i].p_align > 1 && ((p[i].p_vaddr & (p[i].p_align - 1)) !=
(p[i].p_offset & (p[i].p_align - 1)))) {
Die(epath, "ELF p_vaddr incongruent w/ p_offset modulo p_align");
}
if ((p[i].p_vaddr & (pagesz - 1)) != (p[i].p_offset & (pagesz - 1))) {
Die(epath, "ELF p_vaddr incongruent w/ p_offset modulo AT_PAGESZ");
}
if (p[i].p_offset + p[i].p_filesz > esize) {
Die(epath, "ELF PT_LOAD p_offset and p_filesz overlaps image EOF");
}
Elf64_Off a = p[i].p_vaddr & -pagesz;
Elf64_Off b = (p[i].p_vaddr + p[i].p_memsz + (pagesz - 1)) & -pagesz;
for (j = i + 1; j < e->e_phnum; ++j) {
if (p[j].p_type != PT_LOAD)
continue;
Elf64_Off c = p[j].p_vaddr & -pagesz;
Elf64_Off d = (p[j].p_vaddr + p[j].p_memsz + (pagesz - 1)) & -pagesz;
if (MAX(a, c) < MIN(b, d)) {
Die(epath,
"ELF PT_LOAD phdrs %d and %d overlap each others virtual memory");
}
}
if (e->e_machine == EM_AARCH64 &&
p[i].p_vaddr + p[i].p_memsz > 0x0001000000000000) {
Die(epath, "this is an ARM ELF program but it loads to virtual"
" memory addresses outside the legal range [0,2^48)");
}
if (e->e_machine == EM_NEXGEN32E &&
!(-140737488355328 <= (Elf64_Sxword)p[i].p_vaddr &&
(Elf64_Sxword)(p[i].p_vaddr + p[i].p_memsz) <= 140737488355328)) {
Die(epath, "this is an x86-64 ELF program, but it loads to virtual"
" memory addresses outside the legal range [-2^47,2^47)");
}
if ((p[i].p_flags & PF_X) && //
p[i].p_vaddr <= e->e_entry &&
e->e_entry < p[i].p_vaddr + p[i].p_memsz) {
found_entry = 1;
}
}
if (!found_entry) {
Die(epath, "ELF entrypoint not found in PT_LOAD with PF_X");
}
}
struct Input { struct Input {
union { union {
char *map; char *map;
@ -417,7 +253,6 @@ int main(int argc, char *argv[]) {
GetOpts(argc, argv); GetOpts(argc, argv);
OpenInput(exepath); OpenInput(exepath);
ValidateElfImage(input.elf, input.size, input.path);
rodata = FindElfSectionByName( rodata = FindElfSectionByName(
input.elf, input.size, input.elf, input.size,
GetElfSectionNameStringTable(input.elf, input.size), ".rodata"); GetElfSectionNameStringTable(input.elf, input.size), ".rodata");