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:77909f5978("Initial elf nlist support [...]") Fixes:644b4646c7("OpenSSH: Update to 10.1p1") Reviewed by: kib, emaste Differential Revision: https://reviews.freebsd.org/D57034
This commit is contained in:
@@ -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
|
||||
|
||||
+83
-69
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user