git: 26cc23c84e0e - stable/13 - rtld: fix SysV hash function overflow

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Thu, 27 Apr 2023 18:20:11 UTC
The branch stable/13 has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=26cc23c84e0e8bfc6d32b7d802f3cb9598d6ef79

commit 26cc23c84e0e8bfc6d32b7d802f3cb9598d6ef79
Author:     Ed Maste <emaste@FreeBSD.org>
AuthorDate: 2023-04-12 15:07:26 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2023-04-27 17:05:22 +0000

    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
    
    (cherry picked from commit 29e3a06510823edbb91667d21f530d3ec778116d)
---
 libexec/rtld-elf/rtld.c | 15 ++++++---------
 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 49685508d4dc..dc8353ba6628 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -1837,23 +1837,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 52ff8de911e2..e8e997753c6f 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -385,7 +385,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);