libelf: Fix out-of-bounds read in elf_getarsym for unterminated symbol table
elf_getarsym parses the archive symbol table (the "/" or "/SYM64/"
member). After validating only the size of the index, it walks the
declared number of symbols and, for each one, computes a hash with
_dl_elf_hash and advances to the next name with rawmemchr (or an
open-coded loop on platforms without rawmemchr). Both of these scan
for a terminating NUL byte without any bound.
Nothing guarantees that the string area following the offset table
actually contains that many NUL-terminated names, or any NUL at all.
A crafted archive whose symbol-table string area is not NUL terminated
makes the name scan run past the end of the heap buffer holding the
string data (or past the mapped file) -- an out-of-bounds read.
Remember the end of the string area and use a bounded memchr to locate
each name terminator. If a name is not terminated within the string
area the archive is malformed, so fail with ELF_E_INVALID_ARCHIVE
instead of reading out of bounds.
Reproduced under AddressSanitizer with a crafted archive whose string
area contains no NUL byte:
==ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 1
#0 _dl_elf_hash dl-hash.h:45
#1 elf_getarsym elf_getarsym.c:294
0 bytes after 116-byte region ... allocated by elf_getarsym.c:215
The function is reachable from eu-nm -s and eu-readelf, which call
elf_getarsym on untrusted .a files.