Avoid crash on outrageous fuzzed input

https://github.com/upx/upx/issues/828
	modified:   p_lx_elf.cpp
This commit is contained in:
John Reiser 2024-06-04 15:43:11 -07:00 committed by Markus F.X.J. Oberhumer
parent 056865f59a
commit 937ae4c5e0

View File

@ -294,7 +294,7 @@ PackLinuxElf32::PackLinuxElf32help1(InputFile *f)
if (last_Phdr < e_phoff // wrap-around if (last_Phdr < e_phoff // wrap-around
|| e_phoff != sizeof(Elf32_Ehdr) // must be contiguous || e_phoff != sizeof(Elf32_Ehdr) // must be contiguous
|| (unsigned long)file_size < last_Phdr) { || (unsigned long)file_size < last_Phdr) {
throwCantUnpack("bad e_phoff"); throwCantUnpack("bad e_phoff %#x", e_phoff);
} }
e_shoff = get_te32(&ehdri.e_shoff); e_shoff = get_te32(&ehdri.e_shoff);
e_shstrndx = get_te16(&ehdri.e_shstrndx); e_shstrndx = get_te16(&ehdri.e_shstrndx);
@ -303,7 +303,7 @@ PackLinuxElf32::PackLinuxElf32help1(InputFile *f)
|| (e_shnum && e_shoff < last_Phdr) || (e_shnum && e_shoff < last_Phdr)
|| (unsigned long)file_size < last_Shdr) { || (unsigned long)file_size < last_Shdr) {
if (opt->cmd == CMD_COMPRESS) { if (opt->cmd == CMD_COMPRESS) {
throwCantUnpack("bad e_shoff"); throwCantUnpack("bad e_shoff %#x", e_shoff);
} }
} }
sz_phdrs = e_phnum * e_phentsize; sz_phdrs = e_phnum * e_phentsize;
@ -438,11 +438,21 @@ off_t PackLinuxElf::pack3(OutputFile *fo, Filter &ft) // return length of output
return total_out; return total_out;
} }
static unsigned
is_pow2(u64_t x)
{
return !((-1+ x) & x);
}
Elf32_Phdr const * Elf32_Phdr const *
PackLinuxElf32::elf_find_Phdr_for_va(upx_uint32_t addr, Elf32_Phdr const *phdr, unsigned phnum) PackLinuxElf32::elf_find_Phdr_for_va(upx_uint32_t addr, Elf32_Phdr const *phdr, unsigned phnum)
{ {
for (unsigned j = 0; j < phnum; ++phdr) { for (unsigned j = 0; j < phnum; ++j, ++phdr) {
if ((addr - get_te32(&phdr->p_vaddr)) < get_te32(&phdr->p_filesz)) { u32_t align = get_te32(&phdr->p_align);
if (PT_LOAD32 == get_te32(&phdr->p_type)
&& is_pow2(align) && !((-1+ align) &
(get_te32(&phdr->p_vaddr) ^ get_te32(&phdr->p_offset)))
&& (addr - get_te32(&phdr->p_vaddr)) < get_te32(&phdr->p_filesz)) {
return phdr; return phdr;
} }
} }
@ -452,8 +462,12 @@ PackLinuxElf32::elf_find_Phdr_for_va(upx_uint32_t addr, Elf32_Phdr const *phdr,
Elf64_Phdr const * Elf64_Phdr const *
PackLinuxElf64::elf_find_Phdr_for_va(upx_uint64_t addr, Elf64_Phdr const *phdr, unsigned phnum) PackLinuxElf64::elf_find_Phdr_for_va(upx_uint64_t addr, Elf64_Phdr const *phdr, unsigned phnum)
{ {
for (unsigned j = 0; j < phnum; ++phdr) { for (unsigned j = 0; j < phnum; ++j, ++phdr) {
if ((addr - get_te64(&phdr->p_vaddr)) < get_te64(&phdr->p_filesz)) { u64_t align = get_te64(&phdr->p_align);
if (PT_LOAD64 == get_te32(&phdr->p_type)
&& is_pow2(align) && !((-1+ align) &
(get_te64(&phdr->p_vaddr) ^ get_te64(&phdr->p_offset)))
&& (addr - get_te64(&phdr->p_vaddr)) < get_te64(&phdr->p_filesz)) {
return phdr; return phdr;
} }
} }
@ -1006,7 +1020,7 @@ PackLinuxElf64::PackLinuxElf64help1(InputFile *f)
if (last_Phdr < e_phoff // wrap-around if (last_Phdr < e_phoff // wrap-around
|| e_phoff != sizeof(Elf64_Ehdr) // must be contiguous || e_phoff != sizeof(Elf64_Ehdr) // must be contiguous
|| (unsigned long)file_size < last_Phdr) { || (unsigned long)file_size < last_Phdr) {
throwCantUnpack("bad e_phoff"); throwCantUnpack("bad e_phoff %p", (void *)e_phoff);
} }
e_shoff = get_te64(&ehdri.e_shoff); e_shoff = get_te64(&ehdri.e_shoff);
upx_uint64_t const last_Shdr = e_shoff + e_shnum * sizeof(Elf64_Shdr); upx_uint64_t const last_Shdr = e_shoff + e_shnum * sizeof(Elf64_Shdr);
@ -1014,7 +1028,7 @@ PackLinuxElf64::PackLinuxElf64help1(InputFile *f)
|| (e_shnum && e_shoff < last_Phdr) || (e_shnum && e_shoff < last_Phdr)
|| (unsigned long)file_size < last_Shdr) { || (unsigned long)file_size < last_Shdr) {
if (opt->cmd == CMD_COMPRESS) { if (opt->cmd == CMD_COMPRESS) {
throwCantUnpack("bad e_shoff"); throwCantUnpack("bad e_shoff %p", (void *)e_shoff);
} }
} }
sz_phdrs = e_phnum * e_phentsize; sz_phdrs = e_phnum * e_phentsize;
@ -2087,8 +2101,11 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp, u32_t headway)
unsigned const z_str = dt_table[Elf32_Dyn::DT_STRSZ]; unsigned const z_str = dt_table[Elf32_Dyn::DT_STRSZ];
strtab_max = !z_str ? 0 : get_te32(&dynp0[-1+ z_str].d_val); strtab_max = !z_str ? 0 : get_te32(&dynp0[-1+ z_str].d_val);
unsigned const z_tab = dt_table[Elf32_Dyn::DT_STRTAB]; unsigned const z_tab = dt_table[Elf32_Dyn::DT_STRTAB];
unsigned const strtab_beg = !z_tab ? 0 unsigned const tmp1 = get_te32(&dynp0[-1+ z_tab].d_val);
: elf_get_offset_from_address(get_te32(&dynp0[-1+ z_tab].d_val)); if (tmp1 < sz_elf_hdrs) {
throwCantPack("bad DT_STRTAB %#x", tmp1);
}
unsigned const strtab_beg = !z_tab ? 0 : elf_get_offset_from_address(tmp1);
if (!z_str || !z_tab if (!z_str || !z_tab
|| (this->file_size - strtab_beg) < strtab_max // strtab overlaps EOF || (this->file_size - strtab_beg) < strtab_max // strtab overlaps EOF
@ -2101,8 +2118,9 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp, u32_t headway)
} }
// Find end of DT_SYMTAB // Find end of DT_SYMTAB
symnum_max = elf_find_table_size( unsigned const tmp2 = elf_find_table_size(Elf32_Dyn::DT_SYMTAB,
Elf32_Dyn::DT_SYMTAB, Elf32_Shdr::SHT_DYNSYM) / sizeof(Elf32_Sym); Elf32_Shdr::SHT_DYNSYM);
symnum_max = (~0u == tmp2) ? 0 : tmp1 / sizeof(Elf32_Sym);
unsigned v_sym = dt_table[Elf32_Dyn::DT_SYMTAB]; unsigned v_sym = dt_table[Elf32_Dyn::DT_SYMTAB];
if (v_sym) { if (v_sym) {
@ -2365,9 +2383,8 @@ char const *PackLinuxElf64::get_str_name(unsigned st_name, unsigned symnum) cons
char const *PackLinuxElf64::get_dynsym_name(unsigned symnum, unsigned relnum) const char const *PackLinuxElf64::get_dynsym_name(unsigned symnum, unsigned relnum) const
{ {
if (symnum_max <= symnum) { if (symnum_max <= symnum) {
char msg[70]; snprintf(msg, sizeof(msg), (void)relnum;
"bad symnum %#x in Elf64_Rel[%d]", symnum, relnum); return nullptr;
throwCantPack(msg);
} }
return get_str_name(get_te32(&dynsym[symnum].st_name), symnum); return get_str_name(get_te32(&dynsym[symnum].st_name), symnum);
} }
@ -2402,9 +2419,8 @@ char const *PackLinuxElf32::get_str_name(unsigned st_name, unsigned symnum) cons
char const *PackLinuxElf32::get_dynsym_name(unsigned symnum, unsigned relnum) const char const *PackLinuxElf32::get_dynsym_name(unsigned symnum, unsigned relnum) const
{ {
if (symnum_max <= symnum) { if (symnum_max <= symnum) {
char msg[70]; snprintf(msg, sizeof(msg), (void)relnum;
"bad symnum %#x in Elf32_Rel[%d]\n", symnum, relnum); return nullptr;
throwCantPack(msg);
} }
return get_str_name(get_te32(&dynsym[symnum].st_name), symnum); return get_str_name(get_te32(&dynsym[symnum].st_name), symnum);
} }
@ -8020,8 +8036,12 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp, upx_uint64_t headway)
unsigned const z_str = dt_table[Elf64_Dyn::DT_STRSZ]; unsigned const z_str = dt_table[Elf64_Dyn::DT_STRSZ];
strtab_max = !z_str ? 0 : get_te64(&dynp0[-1+ z_str].d_val); strtab_max = !z_str ? 0 : get_te64(&dynp0[-1+ z_str].d_val);
unsigned const z_tab = dt_table[Elf64_Dyn::DT_STRTAB]; unsigned const z_tab = dt_table[Elf64_Dyn::DT_STRTAB];
unsigned const strtab_beg = !z_tab ? 0 unsigned const tmp1 = get_te64(&dynp0[-1+ z_tab].d_val);
: elf_get_offset_from_address(get_te64(&dynp0[-1+ z_tab].d_val)); if (tmp1 < sz_elf_hdrs) {
throwCantPack("bad DT_STRTAB %#x", tmp1);
}
unsigned const strtab_beg = !z_tab ? 0 : elf_get_offset_from_address(tmp1);
if (!z_str || !z_tab if (!z_str || !z_tab
|| (this->file_size - strtab_beg) < strtab_max // strtab overlaps EOF || (this->file_size - strtab_beg) < strtab_max // strtab overlaps EOF
// last string in table must have terminating NUL // last string in table must have terminating NUL
@ -8033,8 +8053,9 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp, upx_uint64_t headway)
} }
// Find end of DT_SYMTAB // Find end of DT_SYMTAB
symnum_max = elf_find_table_size( unsigned const tmp2 = elf_find_table_size(Elf64_Dyn::DT_SYMTAB,
Elf64_Dyn::DT_SYMTAB, Elf64_Shdr::SHT_DYNSYM) / sizeof(Elf64_Sym); Elf64_Shdr::SHT_DYNSYM);
symnum_max = (~0u == tmp2) ? 0 : tmp1 / sizeof(Elf64_Sym);
unsigned v_sym = dt_table[Elf64_Dyn::DT_SYMTAB]; unsigned v_sym = dt_table[Elf64_Dyn::DT_SYMTAB];
if (v_sym) { if (v_sym) {
@ -8216,14 +8237,18 @@ Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const
unsigned const m = elf_hash(name) % nbucket; unsigned const m = elf_hash(name) % nbucket;
unsigned nvisit = 0; unsigned nvisit = 0;
unsigned si; unsigned si;
for (si= get_te32(&buckets[m]); 0!=si; si= get_te32(&chains[si])) { for (si= get_te32(&buckets[m]); si; ) {
char const *const p= get_dynsym_name(si, (unsigned)-1); char const *const p= get_dynsym_name(si, (unsigned)-1);
if (0==strcmp(name, p)) { if (p && 0==strcmp(name, p)) {
return &dynsym[si]; return &dynsym[si];
} }
if (nbucket <= ++nvisit) { if (nbucket <= ++nvisit) {
throwCantPack("circular DT_HASH chain %d\n", si); throwCantPack("circular DT_HASH chain %d\n", si);
} }
si= get_te32(&chains[si]);
if (nbucket <= si) { // bad hashtab
break;
}
} }
} }
} }
@ -8299,14 +8324,18 @@ Elf64_Sym const *PackLinuxElf64::elf_lookup(char const *name) const
unsigned const m = elf_hash(name) % nbucket; unsigned const m = elf_hash(name) % nbucket;
unsigned nvisit = 0; unsigned nvisit = 0;
unsigned si; unsigned si;
for (si= get_te32(&buckets[m]); 0!=si; si= get_te32(&chains[si])) { for (si= get_te32(&buckets[m]); si; ) {
char const *const p= get_dynsym_name(si, (unsigned)-1); char const *const p= get_dynsym_name(si, (unsigned)-1);
if (0==strcmp(name, p)) { if (p && 0==strcmp(name, p)) {
return &dynsym[si]; return &dynsym[si];
} }
if (nbucket <= ++nvisit) { if (nbucket <= ++nvisit) {
throwCantPack("circular DT_HASH chain %d\n", si); throwCantPack("circular DT_HASH chain %d\n", si);
} }
si = get_te32(&chains[si]);
if (nbucket <= si) { // bad hashtab
break;
}
} }
} }
} }