git: 32364f99e15e - stable/14 - rtld-elf: support either byte-order of hints file

From: Stefan Eßer <se_at_FreeBSD.org>
Date: Fri, 29 Mar 2024 06:39:54 UTC
The branch stable/14 has been updated by se:

URL: https://cgit.FreeBSD.org/src/commit/?id=32364f99e15ee067d42e04e5e5721c0b21bd4925

commit 32364f99e15ee067d42e04e5e5721c0b21bd4925
Author:     Stefan Eßer <se@FreeBSD.org>
AuthorDate: 2024-02-26 22:18:12 +0000
Commit:     Stefan Eßer <se@FreeBSD.org>
CommitDate: 2024-03-29 06:34:44 +0000

    rtld-elf: support either byte-order of hints file
    
    Accept either little-endian or big-endian representation of the ELF
    hints magic number in the header of a hints file and convert the
    parameters to the native byte-order of the repsective system.
    
    This is a pre-requisite for a planned change to always write the byte
    order in little-endian format on all architectures. The only relvant
    architecture that uses big-endian data is powerpc64, and it is not
    likely that new architectures will choose that representation of data
    in memory.
    
    When all supported architectures use little-endian data in the hints
    file, the byte swap logic can be enabled for big-endian CPUs at
    compile time. Up to that point, there is a very small run-time penalty
    that is paid on all systems to check the byte-order of the hints file
    and to provide the option to byte-swap the parameters read from the
    hints file header.
    
    This commit contains the changes from review D44080 (which had been
    split off from this patch for easier review),
    
    Reviewed by:    kib
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D44053
    
    (cherry picked from commit 7b77d37a561b47db093a2528b8032dbfe5791698)
    
    rtld-elf: add some debug print statements
    
    The byte-order independent code has been reported to fail on powerpc64.
    Add some more debug statements to help identify the parametrs used and
    to verify the correct operation of the byte-swap macros used..
    
    (cherry picked from commit 173953182af060dcab43990e179ee91e9f2d1e54)
    
    rtld: fix check for endianess of elf hints file
    
    Don't check if the elf hints file is in host byte order, but check
    if it is in little endian by looking at the magic number.
    This fixes rtld on big endian platforms.
    
    Reviewed by:    se, kib (prior version of the patch)
    Fixes:          7b77d37a561b ("rtld-elf: support either byte-order of hints")
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D44472
    
    (cherry picked from commit da2d6e2815d7694e3ccbd561508074c547b02dd6)
    
    rtld: reduce debug messages after fix on big-endian hosts
    
    Remove a debug message that had been added to support the debugging
    of a mis-detection of the hint files endianness on powerpc64.
    
    MFC after:      3 days
    
    (cherry picked from commit c44bf7d2e9d2292867f2e23f291266af26762354)
---
 libexec/rtld-elf/rtld.c | 79 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 63 insertions(+), 16 deletions(-)

diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index f11fe8afcc32..07068dff0b62 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -2040,6 +2040,9 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj,
     return (def);
 }
 
+/* Convert between native byte order and forced little resp. big endian. */
+#define COND_SWAP(n) (is_le ? le32toh(n) : be32toh(n))
+
 /*
  * Return the search path from the ldconfig hints file, reading it if
  * necessary.  If nostdlib is true, then the default search paths are
@@ -2063,6 +2066,12 @@ gethints(bool nostdlib)
 	int fd;
 	size_t flen;
 	uint32_t dl;
+	uint32_t magic;		/* Magic number */
+	uint32_t version;	/* File version (1) */
+	uint32_t strtab;	/* Offset of string table in file */
+	uint32_t dirlist;	/* Offset of directory list in string table */
+	uint32_t dirlistlen;	/* strlen(dirlist) */
+	bool is_le;		/* Does the hints file use little endian */
 	bool skip;
 
 	/* First call, read the hints file */
@@ -2070,8 +2079,10 @@ gethints(bool nostdlib)
 		/* Keep from trying again in case the hints file is bad. */
 		hints = "";
 
-		if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) == -1)
+		if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) == -1) {
+			dbg("failed to open hints file \"%s\"", ld_elf_hints_path);
 			return (NULL);
+		}
 
 		/*
 		 * Check of hdr.dirlistlen value against type limit
@@ -2079,29 +2090,65 @@ gethints(bool nostdlib)
 		 * paranoia leads to checks that dirlist is fully
 		 * contained in the file range.
 		 */
-		if (read(fd, &hdr, sizeof hdr) != sizeof hdr ||
-		    hdr.magic != ELFHINTS_MAGIC ||
-		    hdr.version != 1 || hdr.dirlistlen > UINT_MAX / 2 ||
-		    fstat(fd, &hint_stat) == -1) {
+		if (read(fd, &hdr, sizeof hdr) != sizeof hdr) {
+			dbg("failed to read %lu bytes from hints file \"%s\"",
+			    (u_long)sizeof hdr, ld_elf_hints_path);
 cleanup1:
 			close(fd);
 			hdr.dirlistlen = 0;
 			return (NULL);
 		}
-		dl = hdr.strtab;
-		if (dl + hdr.dirlist < dl)
+		dbg("host byte-order: %s-endian", le32toh(1) == 1 ? "little" : "big");
+		dbg("hints file byte-order: %s-endian",
+		    hdr.magic == htole32(ELFHINTS_MAGIC) ? "little" : "big");
+		is_le = /*htole32(1) == 1 || */ hdr.magic == htole32(ELFHINTS_MAGIC);
+		magic = COND_SWAP(hdr.magic);
+		version = COND_SWAP(hdr.version);
+		strtab = COND_SWAP(hdr.strtab);
+		dirlist = COND_SWAP(hdr.dirlist);
+		dirlistlen = COND_SWAP(hdr.dirlistlen);
+		if (magic != ELFHINTS_MAGIC) {
+			dbg("invalid magic number %#08x (expected: %#08x)",
+			    magic, ELFHINTS_MAGIC);
 			goto cleanup1;
-		dl += hdr.dirlist;
-		if (dl + hdr.dirlistlen < dl)
+		}
+		if (version != 1) {
+			dbg("hints file version %d (expected: 1)", version);
 			goto cleanup1;
-		dl += hdr.dirlistlen;
-		if (dl > hint_stat.st_size)
+		}
+		if (dirlistlen > UINT_MAX / 2) {
+			dbg("directory list is to long: %d > %d",
+			    dirlistlen, UINT_MAX / 2);
 			goto cleanup1;
-		p = xmalloc(hdr.dirlistlen + 1);
-		if (pread(fd, p, hdr.dirlistlen + 1,
-		    hdr.strtab + hdr.dirlist) != (ssize_t)hdr.dirlistlen + 1 ||
-		    p[hdr.dirlistlen] != '\0') {
+		}
+		if (fstat(fd, &hint_stat) == -1) {
+			dbg("failed to find length of hints file \"%s\"",
+			    ld_elf_hints_path);
+			goto cleanup1;
+		}
+		dl = strtab;
+		if (dl + dirlist < dl) {
+			dbg("invalid string table position %d", dl);
+			goto cleanup1;
+		}
+		dl += dirlist;
+		if (dl + dirlistlen < dl) {
+			dbg("invalid directory list offset %d", dirlist);
+			goto cleanup1;
+		}
+		dl += dirlistlen;
+		if (dl > hint_stat.st_size) {
+			dbg("hints file \"%s\" is truncated (%d vs. %jd bytes)",
+			    ld_elf_hints_path, dl, (uintmax_t)hint_stat.st_size);
+			goto cleanup1;
+		}
+		p = xmalloc(dirlistlen + 1);
+		if (pread(fd, p, dirlistlen + 1,
+		    strtab + dirlist) != (ssize_t)dirlistlen + 1 ||
+		    p[dirlistlen] != '\0') {
 			free(p);
+			dbg("failed to read %d bytes starting at %d from hints file \"%s\"",
+			    dirlistlen + 1, strtab + dirlist, ld_elf_hints_path);
 			goto cleanup1;
 		}
 		hints = p;
@@ -2164,7 +2211,7 @@ cleanup1:
 	 */
 	fndx = 0;
 	fcount = 0;
-	filtered_path = xmalloc(hdr.dirlistlen + 1);
+	filtered_path = xmalloc(dirlistlen + 1);
 	hintpath = &hintinfo->dls_serpath[0];
 	for (hintndx = 0; hintndx < hmeta.dls_cnt; hintndx++, hintpath++) {
 		skip = false;