The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=4617a6cb82a673b02257257c1f5f8a3c8d2bb943

commit 4617a6cb82a673b02257257c1f5f8a3c8d2bb943
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2026-05-19 06:52:53 +0000
Commit:     Dag-Erling Smørgrav <[email protected]>
CommitDate: 2026-05-19 06:53:35 +0000

    nlist: Handle multiple symbol tables
    
    * Instead of looking for and stopping at the first SHT_SYMTAB section,
      iterate over all SHT_DYNSYM and SHT_SYMTAB sections until we've either
      found all our symbols or run out.
    
    * Perform bounds checks on section and string table offsets and sizes
      before attempting to mmap() the string table.
    
    * Perform bounds checks on individual symbol table entries before
      attempting to access the corresponding strings.
    
    * Stop treating _Foo and Foo as the same symbol.
    
    This unbreaks OpenSSH which uses nlist(3) to verify PKCS#11 providers.
    
    PR:             295336
    MFC after:      1 week
    Fixes:          77909f597881 ("Initial elf nlist support [...]")
    Fixes:          644b4646c7ac ("OpenSSH: Update to 10.1p1")
    Reviewed by:    kib, emaste
    Differential Revision:  https://reviews.freebsd.org/D57034
---
 lib/libc/gen/nlist.3 |   8 +--
 lib/libc/gen/nlist.c | 150 ++++++++++++++++++++++++++++-----------------------
 2 files changed, 87 insertions(+), 71 deletions(-)

diff --git a/lib/libc/gen/nlist.3 b/lib/libc/gen/nlist.3
index 6aefd95e3b51..4912180e9bcd 100644
--- a/lib/libc/gen/nlist.3
+++ b/lib/libc/gen/nlist.3
@@ -25,7 +25,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 18, 2026
+.Dd May 19, 2026
 .Dt NLIST 3
 .Os
 .Sh NAME
@@ -45,9 +45,11 @@ Its use is discouraged.
 The
 .Fn nlist
 function
-retrieves name list entries from the
+retrieves name list entries from
 .Xr elf 5
-section with type
+sections with type
+.Dv SHT_DYNSYM
+or
 .Dv SHT_SYMTAB
 in an ELF object (for example, an executable file or shared library).
 The argument
diff --git a/lib/libc/gen/nlist.c b/lib/libc/gen/nlist.c
index ebf4ae92641a..06034dc8ee1a 100644
--- a/lib/libc/gen/nlist.c
+++ b/lib/libc/gen/nlist.c
@@ -36,8 +36,9 @@
 #include <sys/file.h>
 #include <arpa/inet.h>
 
-#include <errno.h>
 #include <a.out.h>
+#include <errno.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -85,6 +86,8 @@ __fdnlist(int fd, struct nlist *list)
 
 #define        ISLAST(p)       (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
 
+static int elf_scan_symtab(Elf_Shdr *, int, int, off_t, size_t, char *, size_t,
+    struct nlist *, int);
 static void elf_sym_to_nlist(struct nlist *, Elf_Sym *, Elf_Shdr *, int);
 
 /*
@@ -121,22 +124,19 @@ int
 __elf_fdnlist(int fd, struct nlist *list)
 {
        struct nlist *p;
-       Elf_Off symoff = 0, symstroff = 0;
-       Elf_Size symsize = 0, symstrsize = 0;
-       Elf_Ssize cc, i;
+       Elf_Off symoff = 0, stroff = 0;
+       Elf_Size symsize = 0, strsize = 0;
+       Elf_Ssize i;
        int nent = -1;
        int errsave;
-       Elf_Sym sbuf[1024];
-       Elf_Sym *s;
        Elf_Ehdr ehdr;
-       char *strtab = NULL;
-       Elf_Shdr *shdr = NULL;
+       Elf_Shdr *shdr;
        Elf_Size shdr_size;
        void *base;
        struct stat st;
 
        /* Make sure obj is OK */
-       if (lseek(fd, (off_t)0, SEEK_SET) == -1 ||
+       if (lseek(fd, 0, SEEK_SET) == -1 ||
            _read(fd, &ehdr, sizeof(Elf_Ehdr)) != sizeof(Elf_Ehdr) ||
            !__elf_is_okay__(&ehdr) ||
            _fstat(fd, &st) < 0)
@@ -158,39 +158,6 @@ __elf_fdnlist(int fd, struct nlist *list)
                return (-1);
        shdr = (Elf_Shdr *)base;
 
-       /*
-        * Find the symbol table entry and it's corresponding
-        * string table entry.  Version 1.1 of the ABI states
-        * that there is only one symbol table but that this
-        * could change in the future.
-        */
-       for (i = 0; i < ehdr.e_shnum; i++) {
-               if (shdr[i].sh_type == SHT_SYMTAB) {
-                       symoff = shdr[i].sh_offset;
-                       symsize = shdr[i].sh_size;
-                       symstroff = shdr[shdr[i].sh_link].sh_offset;
-                       symstrsize = shdr[shdr[i].sh_link].sh_size;
-                       break;
-               }
-       }
-
-       /* Check for files too large to mmap. */
-       if (symstrsize > SIZE_T_MAX) {
-               errno = EFBIG;
-               goto done;
-       }
-       /*
-        * Map string table into our address space.  This gives us
-        * an easy way to randomly access all the strings, without
-        * making the memory allocation permanent as with malloc/free
-        * (i.e., munmap will return it to the system).
-        */
-       base = mmap(NULL, (size_t)symstrsize, PROT_READ, MAP_PRIVATE, fd,
-           (off_t)symstroff);
-       if (base == MAP_FAILED)
-               goto done;
-       strtab = (char *)base;
-
        /*
         * clean out any left-over information for all valid entries.
         * Type and value defined to be 0 if not found; historical
@@ -210,46 +177,93 @@ __elf_fdnlist(int fd, struct nlist *list)
                ++nent;
        }
 
-       /* Don't process any further if object is stripped. */
-       if (symoff == 0)
-               goto done;
-               
-       if (lseek(fd, (off_t) symoff, SEEK_SET) == -1) {
-               nent = -1;
-               goto done;
+       /*
+        * Find the symbol table entry and it's corresponding
+        * string table entry.  Version 1.1 of the ABI states
+        * that there is only one symbol table but that this
+        * could change in the future.
+        */
+       for (i = 0; nent > 0 && i < ehdr.e_shnum; i++) {
+               if (shdr[i].sh_type != SHT_SYMTAB &&
+                   shdr[i].sh_type != SHT_DYNSYM)
+                       continue;
+               symoff = shdr[i].sh_offset;
+               symsize = shdr[i].sh_size;
+               stroff = shdr[shdr[i].sh_link].sh_offset;
+               strsize = shdr[shdr[i].sh_link].sh_size;
+
+               /*
+                * Skip this section if it or its string table is empty or
+                * extends beyond the end of the file, or if the string
+                * table is too large to map into memory.
+                */
+               if (symoff == 0 || symsize == 0 ||
+                   symsize > SIZE_MAX - symoff ||
+                   symoff + symsize > st.st_size ||
+                   stroff == 0 || strsize == 0 ||
+                   strsize > SIZE_MAX - stroff ||
+                   stroff + strsize > st.st_size) {
+                       errno = ENOENT;
+                       continue;
+               }
+
+               /*
+                * Map string table into our address space.  This gives us
+                * an easy way to randomly access all the strings, without
+                * making the memory allocation permanent as with
+                * malloc/free (i.e., munmap will return it to the
+                * system).
+                */
+               base = mmap(NULL, (size_t)strsize, PROT_READ,
+                   MAP_PRIVATE, fd, (off_t)stroff);
+               if (base == MAP_FAILED)
+                       continue;
+
+               nent = elf_scan_symtab(shdr, ehdr.e_shnum, fd, symoff, symsize,
+                   base, strsize, list, nent);
+
+               errsave = errno;
+               munmap(base, strsize);
+               errno = errsave;
        }
+       errsave = errno;
+       munmap(shdr, shdr_size);
+       errno = errsave;
+       return (nent);
+}
+
+static int
+elf_scan_symtab(Elf_Shdr *shdr, int shnum, int fd, off_t symoff, size_t 
symsize,
+    char *strtab, size_t strsize, struct nlist *list, int nent)
+{
+       Elf_Sym sbuf[1024];
+       Elf_Sym *s;
+       char *name;
+       struct nlist *p;
+       Elf_Ssize cc;
+       size_t slen;
 
+       if (lseek(fd, symoff, SEEK_SET) == -1)
+               return (-1);
        while (symsize > 0 && nent > 0) {
                cc = MIN(symsize, sizeof(sbuf));
                if (_read(fd, sbuf, cc) != cc)
                        break;
                symsize -= cc;
                for (s = sbuf; cc > 0 && nent > 0; ++s, cc -= sizeof(*s)) {
-                       char *name;
-                       struct nlist *p;
-
+                       if (s->st_name >= strsize)
+                               continue;
                        name = strtab + s->st_name;
                        if (name[0] == '\0')
                                continue;
-                       for (p = list; !ISLAST(p); p++) {
-                               if ((p->n_un.n_name[0] == '_' &&
-                                   strcmp(name, p->n_un.n_name+1) == 0)
-                                   || strcmp(name, p->n_un.n_name) == 0) {
-                                       elf_sym_to_nlist(p, s, shdr,
-                                           ehdr.e_shnum);
-                                       if (--nent <= 0)
-                                               break;
-                               }
+                       slen = strnlen(name, strsize - s->st_name);
+                       for (p = list; nent > 0 && !ISLAST(p); p++) {
+                               if (strncmp(name, p->n_un.n_name, slen) == 0 &&
+                                   p->n_un.n_name[slen] == '\0')
+                                       elf_sym_to_nlist(p, s, shdr, shnum);
                        }
                }
        }
-  done:
-       errsave = errno;
-       if (strtab != NULL)
-               munmap(strtab, symstrsize);
-       if (shdr != NULL)
-               munmap(shdr, shdr_size);
-       errno = errsave;
        return (nent);
 }
 

Reply via email to