rtld: fix SysV hash function overflow

Quoting from https://maskray.me/blog/2023-04-12-elf-hash-function:

The System V Application Binary Interface (generic ABI) specifies the
ELF object file format. When producing an output executable or shared
object needing a dynamic symbol table (.dynsym), a linker generates a
.hash section with type SHT_HASH to hold a symbol hash table. A DT_HASH
tag is produced to hold the address of .hash.

The function is supposed to return a value no larger than 0x0fffffff.
Unfortunately, there is a bug. When unsigned long consists of more than
32 bits, the return value may be larger than UINT32_MAX. For instance,
elf_hash((const unsigned char *)"\xff\x0f\x0f\x0f\x0f\x0f\x12") returns
0x100000002, which is clearly unintended, as the function should behave
the same way regardless of whether long represents a 32-bit integer or
a 64-bit integer.

Reviewed by:	kib, Fangrui Song
Sponsored by:	The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D39517
This commit is contained in:
Ed Maste
2023-04-12 11:07:26 -04:00
parent 87443cb6c6
commit 29e3a06510
2 changed files with 7 additions and 10 deletions
+6 -9
View File
@@ -1796,23 +1796,20 @@ donelist_check(DoneList *dlp, const Obj_Entry *obj)
} }
/* /*
* Hash function for symbol table lookup. Don't even think about changing * SysV hash function for symbol table lookup. It is a slightly optimized
* this. It is specified by the System V ABI. * version of the hash specified by the System V ABI.
*/ */
unsigned long Elf32_Word
elf_hash(const char *name) elf_hash(const char *name)
{ {
const unsigned char *p = (const unsigned char *)name; const unsigned char *p = (const unsigned char *)name;
unsigned long h = 0; Elf32_Word h = 0;
unsigned long g;
while (*p != '\0') { while (*p != '\0') {
h = (h << 4) + *p++; h = (h << 4) + *p++;
if ((g = h & 0xf0000000) != 0) h ^= (h >> 24) & 0xf0;
h ^= g >> 24;
h &= ~g;
} }
return (h); return (h & 0x0fffffff);
} }
/* /*
+1 -1
View File
@@ -379,7 +379,7 @@ void dump_Elf_Rela(Obj_Entry *, const Elf_Rela *, u_long);
*/ */
uintptr_t rtld_round_page(uintptr_t); uintptr_t rtld_round_page(uintptr_t);
uintptr_t rtld_trunc_page(uintptr_t); uintptr_t rtld_trunc_page(uintptr_t);
unsigned long elf_hash(const char *); Elf32_Word elf_hash(const char *);
const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *,
const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *);
void lockdflt_init(void); void lockdflt_init(void);