git: 9e4ae81971f7 - stable/13 - link_elf: fix SysV hash function overflow

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

URL: https://cgit.FreeBSD.org/src/commit/?id=9e4ae81971f7b85982c73c1b29424149b56f38dd

commit 9e4ae81971f7b85982c73c1b29424149b56f38dd
Author:     Ed Maste <emaste@FreeBSD.org>
AuthorDate: 2023-04-12 14:04:27 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2023-04-27 17:05:28 +0000

    link_elf: 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 2ef2c26f3f132af33f6f12cd7b27d4dbbd7fa435)
---
 sys/kern/link_elf.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
index 245c8697cf79..3c06c8c79108 100644
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -1481,23 +1481,20 @@ relocate_file(elf_file_t ef)
 }
 
 /*
- * 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 specified by the
+ * System V ABI.
  */
-static unsigned long
+static Elf32_Word
 elf_hash(const char *name)
 {
-	const unsigned char *p = (const unsigned char *) name;
-	unsigned long h = 0;
-	unsigned long g;
+	const unsigned char *p = (const unsigned char *)name;
+	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);
 }
 
 static int
@@ -1508,7 +1505,7 @@ link_elf_lookup_symbol1(linker_file_t lf, const char *name, c_linker_sym_t *sym,
 	unsigned long symnum;
 	const Elf_Sym* symp;
 	const char *strp;
-	unsigned long hash;
+	Elf32_Word hash;
 
 	/* If we don't have a hash, bail. */
 	if (ef->buckets == NULL || ef->nbuckets == 0) {