diff options
author | Ed Maste <emaste@FreeBSD.org> | 2023-04-12 15:07:26 +0000 |
---|---|---|
committer | Ed Maste <emaste@FreeBSD.org> | 2023-04-12 16:44:34 +0000 |
commit | 29e3a06510823edbb91667d21f530d3ec778116d (patch) | |
tree | 2798cfb82bf1c0442136a3fa271866578bb95237 /libexec | |
parent | 87443cb6c669b51ceaa7f85db63191374477efc1 (diff) | |
download | src-29e3a06510823edbb91667d21f530d3ec778116d.tar.gz src-29e3a06510823edbb91667d21f530d3ec778116d.zip |
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
Diffstat (limited to 'libexec')
-rw-r--r-- | libexec/rtld-elf/rtld.c | 15 | ||||
-rw-r--r-- | libexec/rtld-elf/rtld.h | 2 |
2 files changed, 7 insertions, 10 deletions
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 382b05191a81..0d2a2b6b4c24 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -1796,23 +1796,20 @@ donelist_check(DoneList *dlp, const Obj_Entry *obj) } /* - * Hash function for symbol table lookup. Don't even think about changing - * this. It is specified by the System V ABI. + * SysV hash function for symbol table lookup. It is a slightly optimized + * version of the hash specified by the System V ABI. */ -unsigned long +Elf32_Word elf_hash(const char *name) { const unsigned char *p = (const unsigned char *)name; - unsigned long h = 0; - unsigned long g; + Elf32_Word h = 0; while (*p != '\0') { h = (h << 4) + *p++; - if ((g = h & 0xf0000000) != 0) - h ^= g >> 24; - h &= ~g; + h ^= (h >> 24) & 0xf0; } - return (h); + return (h & 0x0fffffff); } /* diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 807437af5ff3..2db73c767502 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -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_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 Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); void lockdflt_init(void); |