elf_lookup must defend against bad chain in hash table

https://github.com/upx/upx/issues/871
fuzzing from leon.weiss AT @ruhr-uni-bochum.de
This commit is contained in:
John Reiser 2025-01-30 10:56:45 -08:00 committed by Markus F.X.J. Oberhumer
parent 0a61ffea3a
commit 0870514194

View File

@ -8650,25 +8650,27 @@ unsigned PackLinuxElf::elf_hash(char const *p)
Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const
{ {
if (hashtab && dynsym && dynstr) { if (hashtab && dynsym && dynstr) {
unsigned const nbucket = get_te32(&hashtab[0]); unsigned const n_bucket = get_te32(&hashtab[0]);
unsigned const *const buckets = &hashtab[2]; unsigned const *const buckets = &hashtab[2];
unsigned const *const chains = &buckets[nbucket]; unsigned const *const chains = &buckets[n_bucket];
if (nbucket) { if (n_bucket) {
unsigned const m = elf_hash(name) % nbucket; unsigned const m = elf_hash(name) % n_bucket;
unsigned nvisit = 0; unsigned nvisit = 0;
unsigned si; unsigned si;
for (si= get_te32(&buckets[m]); si; ) { if ((file_size + file_image) <= (void const *)chains) {
throwCantPack("bad n_bucket %#x\n", n_bucket);
}
for (si= get_te32(&buckets[m]); si; si = get_te32(&chains[si])) {
if (n_bucket <= si) {
throwCantPack("bad DT_HASH chain %d\n", si);
}
char const *const p= get_dynsym_name(si, (unsigned)-1); char const *const p= get_dynsym_name(si, (unsigned)-1);
if (p && 0==strcmp(name, p)) { if (p && 0==strcmp(name, p)) {
return &dynsym[si]; return &dynsym[si];
} }
if (nbucket <= ++nvisit) { if (n_bucket <= ++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;
}
} }
} }
} }
@ -8737,25 +8739,27 @@ Elf32_Sym const *PackLinuxElf32::elf_lookup(char const *name) const
Elf64_Sym const *PackLinuxElf64::elf_lookup(char const *name) const Elf64_Sym const *PackLinuxElf64::elf_lookup(char const *name) const
{ {
if (hashtab && dynsym && dynstr) { if (hashtab && dynsym && dynstr) {
unsigned const nbucket = get_te32(&hashtab[0]); unsigned const n_bucket = get_te32(&hashtab[0]);
unsigned const *const buckets = &hashtab[2]; unsigned const *const buckets = &hashtab[2];
unsigned const *const chains = &buckets[nbucket]; unsigned const *const chains = &buckets[n_bucket];
if (nbucket) { // -rust-musl can have "empty" hashtab if (n_bucket) { // -rust-musl can have "empty" hashtab
unsigned const m = elf_hash(name) % nbucket; unsigned const m = elf_hash(name) % n_bucket;
unsigned nvisit = 0; unsigned nvisit = 0;
unsigned si; unsigned si;
for (si= get_te32(&buckets[m]); si; ) { if ((file_size + file_image) <= (void const *)chains) {
throwCantPack("bad n_bucket %#x\n", n_bucket);
}
for (si= get_te32(&buckets[m]); si; si = get_te32(&chains[si])) {
if (n_bucket <= si) {
throwCantPack("bad DT_HASH chain %d\n", si);
}
char const *const p= get_dynsym_name(si, (unsigned)-1); char const *const p= get_dynsym_name(si, (unsigned)-1);
if (p && 0==strcmp(name, p)) { if (p && 0==strcmp(name, p)) {
return &dynsym[si]; return &dynsym[si];
} }
if (nbucket <= ++nvisit) { if (n_bucket <= ++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;
}
} }
} }
} }