Fix issues found by Coverity in the rtld-elf.c:gethints().

Check that the dirlist path string specification does not cause
overflow and is fully contained in the hints file.
Check that the dirlist string is nul-terminated.
Make 'hdr' static variable, so that hdr.dirlistlen is available when
hints cached value is used on next function calls.  Reset hdr.dirlistlen
to zero if error was detected, so that allocations use reasonable size.
Use 'hints', and not 'p' in the body, since p is only initialized on the
first call.

Reported and reviewed by:	truckman (previous version)
Sponsored by:	The FreeBSD Foundation
CIDs:	1006503, 1006504, 1006676, 1008488, 1007263
MFC after:	2 weeks
This commit is contained in:
kib 2016-05-25 18:10:44 +00:00
parent 03a8f3fe54
commit 95a437124b

View File

@ -1667,14 +1667,16 @@ static const char *
gethints(bool nostdlib)
{
static char *hints, *filtered_path;
struct elfhints_hdr hdr;
static struct elfhints_hdr hdr;
struct fill_search_info_args sargs, hargs;
struct dl_serinfo smeta, hmeta, *SLPinfo, *hintinfo;
struct dl_serpath *SLPpath, *hintpath;
char *p;
struct stat hint_stat;
unsigned int SLPndx, hintndx, fndx, fcount;
int fd;
size_t flen;
uint32_t dl;
bool skip;
/* First call, read the hints file */
@ -1684,19 +1686,38 @@ gethints(bool nostdlib)
if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) == -1)
return (NULL);
/*
* Check of hdr.dirlistlen value against type limit
* intends to pacify static analyzers. Further
* 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.version != 1 || hdr.dirlistlen > UINT_MAX / 2 ||
fstat(fd, &hint_stat) == -1) {
cleanup1:
close(fd);
hdr.dirlistlen = 0;
return (NULL);
}
dl = hdr.strtab;
if (dl + hdr.dirlist < dl)
goto cleanup1;
dl += hdr.dirlist;
if (dl + hdr.dirlistlen < dl)
goto cleanup1;
dl += hdr.dirlistlen;
if (dl > hint_stat.st_size)
goto cleanup1;
p = xmalloc(hdr.dirlistlen + 1);
if (lseek(fd, hdr.strtab + hdr.dirlist, SEEK_SET) == -1 ||
read(fd, p, hdr.dirlistlen + 1) !=
(ssize_t)hdr.dirlistlen + 1) {
(ssize_t)hdr.dirlistlen + 1 || p[hdr.dirlistlen] != '\0') {
free(p);
close(fd);
return (NULL);
goto cleanup1;
}
hints = p;
close(fd);
@ -1729,7 +1750,7 @@ gethints(bool nostdlib)
hargs.serinfo = &hmeta;
path_enumerate(ld_standard_library_path, fill_search_info, &sargs);
path_enumerate(p, fill_search_info, &hargs);
path_enumerate(hints, fill_search_info, &hargs);
SLPinfo = xmalloc(smeta.dls_size);
hintinfo = xmalloc(hmeta.dls_size);
@ -1748,7 +1769,7 @@ gethints(bool nostdlib)
hargs.strspace = (char *)&hintinfo->dls_serpath[hmeta.dls_cnt];
path_enumerate(ld_standard_library_path, fill_search_info, &sargs);
path_enumerate(p, fill_search_info, &hargs);
path_enumerate(hints, fill_search_info, &hargs);
/*
* Now calculate the difference between two sets, by excluding