Eliminate the static buffer used to read the first page of the mapped
object, and eliminate the pread(2) call as well [1]. Mmap the first page of the object temporaly, and unmap it on error or last use. Potentially, this leaves one-page gap between succeeding dlopen(3), but there are other mmap(2) consumers as well. Fix several cases were the whole mapping of the object leaked on error. Use MAP_PREFAULT_READ for mmap(2) calls which map real object pages [2]. Insipired by the patch by: Ian Lepore <freebsd damnhippie dyndns org> [1] Suggested by: alc [2] MFC after: 2 weeks
This commit is contained in:
parent
d4746e107f
commit
e474e51e07
@ -38,7 +38,7 @@
|
||||
#include "debug.h"
|
||||
#include "rtld.h"
|
||||
|
||||
static Elf_Ehdr *get_elf_header (int, const char *);
|
||||
static Elf_Ehdr *get_elf_header(int, const char *);
|
||||
static int convert_prot(int); /* Elf flags -> mmap protection */
|
||||
static int convert_flags(int); /* Elf flags -> mmap flags */
|
||||
|
||||
@ -121,7 +121,7 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) {
|
||||
_rtld_error("%s: PT_LOAD segment %d not page-aligned",
|
||||
path, nsegs);
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -161,12 +161,12 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
}
|
||||
if (phdyn == NULL) {
|
||||
_rtld_error("%s: object is not dynamically-linked", path);
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (nsegs < 0) {
|
||||
_rtld_error("%s: too few PT_LOAD segments", path);
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -183,13 +183,12 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
if (mapbase == (caddr_t) -1) {
|
||||
_rtld_error("%s: mmap of entire address space failed: %s",
|
||||
path, rtld_strerror(errno));
|
||||
return NULL;
|
||||
goto error;
|
||||
}
|
||||
if (base_addr != NULL && mapbase != base_addr) {
|
||||
_rtld_error("%s: mmap returned wrong address: wanted %p, got %p",
|
||||
path, base_addr, mapbase);
|
||||
munmap(mapbase, mapsize);
|
||||
return NULL;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
for (i = 0; i <= nsegs; i++) {
|
||||
@ -201,10 +200,10 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
data_prot = convert_prot(segs[i]->p_flags);
|
||||
data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED;
|
||||
if (mmap(data_addr, data_vlimit - data_vaddr, data_prot,
|
||||
data_flags, fd, data_offset) == (caddr_t) -1) {
|
||||
data_flags | MAP_PREFAULT_READ, fd, data_offset) == (caddr_t) -1) {
|
||||
_rtld_error("%s: mmap of data failed: %s", path,
|
||||
rtld_strerror(errno));
|
||||
return NULL;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
/* Do BSS setup */
|
||||
@ -221,7 +220,7 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) {
|
||||
_rtld_error("%s: mprotect failed: %s", path,
|
||||
rtld_strerror(errno));
|
||||
return NULL;
|
||||
goto error1;
|
||||
}
|
||||
|
||||
memset(clear_addr, 0, nclear);
|
||||
@ -240,7 +239,7 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
data_flags | MAP_ANON, -1, 0) == (caddr_t)-1) {
|
||||
_rtld_error("%s: mmap of bss failed: %s", path,
|
||||
rtld_strerror(errno));
|
||||
return NULL;
|
||||
goto error1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,7 +272,7 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
if (obj->phdr == NULL) {
|
||||
obj_free(obj);
|
||||
_rtld_error("%s: cannot allocate program header", path);
|
||||
return NULL;
|
||||
goto error1;
|
||||
}
|
||||
memcpy((char *)obj->phdr, (char *)hdr + hdr->e_phoff, phsize);
|
||||
obj->phdr_alloc = true;
|
||||
@ -293,63 +292,72 @@ map_object(int fd, const char *path, const struct stat *sb)
|
||||
obj->relro_page = obj->relocbase + trunc_page(relro_page);
|
||||
obj->relro_size = round_page(relro_size);
|
||||
|
||||
return obj;
|
||||
munmap(hdr, PAGE_SIZE);
|
||||
return (obj);
|
||||
|
||||
error1:
|
||||
munmap(mapbase, mapsize);
|
||||
error:
|
||||
munmap(hdr, PAGE_SIZE);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static Elf_Ehdr *
|
||||
get_elf_header (int fd, const char *path)
|
||||
get_elf_header(int fd, const char *path)
|
||||
{
|
||||
static union {
|
||||
Elf_Ehdr hdr;
|
||||
char buf[PAGE_SIZE];
|
||||
} u;
|
||||
ssize_t nbytes;
|
||||
Elf_Ehdr *hdr;
|
||||
|
||||
if ((nbytes = pread(fd, u.buf, PAGE_SIZE, 0)) == -1) {
|
||||
_rtld_error("%s: read error: %s", path, rtld_strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ,
|
||||
fd, 0);
|
||||
if (hdr == (Elf_Ehdr *)MAP_FAILED) {
|
||||
_rtld_error("%s: read error: %s", path, rtld_strerror(errno));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Make sure the file is valid */
|
||||
if (nbytes < (ssize_t)sizeof(Elf_Ehdr) || !IS_ELF(u.hdr)) {
|
||||
_rtld_error("%s: invalid file format", path);
|
||||
return NULL;
|
||||
}
|
||||
if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS
|
||||
|| u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) {
|
||||
_rtld_error("%s: unsupported file layout", path);
|
||||
return NULL;
|
||||
}
|
||||
if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT
|
||||
|| u.hdr.e_version != EV_CURRENT) {
|
||||
_rtld_error("%s: unsupported file version", path);
|
||||
return NULL;
|
||||
}
|
||||
if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) {
|
||||
_rtld_error("%s: unsupported file type", path);
|
||||
return NULL;
|
||||
}
|
||||
if (u.hdr.e_machine != ELF_TARG_MACH) {
|
||||
_rtld_error("%s: unsupported machine", path);
|
||||
return NULL;
|
||||
}
|
||||
/* Make sure the file is valid */
|
||||
if (!IS_ELF(*hdr)) {
|
||||
_rtld_error("%s: invalid file format", path);
|
||||
goto error;
|
||||
}
|
||||
if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
|
||||
hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
|
||||
_rtld_error("%s: unsupported file layout", path);
|
||||
goto error;
|
||||
}
|
||||
if (hdr->e_ident[EI_VERSION] != EV_CURRENT ||
|
||||
hdr->e_version != EV_CURRENT) {
|
||||
_rtld_error("%s: unsupported file version", path);
|
||||
goto error;
|
||||
}
|
||||
if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
|
||||
_rtld_error("%s: unsupported file type", path);
|
||||
goto error;
|
||||
}
|
||||
if (hdr->e_machine != ELF_TARG_MACH) {
|
||||
_rtld_error("%s: unsupported machine", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* We rely on the program header being in the first page. This is
|
||||
* not strictly required by the ABI specification, but it seems to
|
||||
* always true in practice. And, it simplifies things considerably.
|
||||
*/
|
||||
if (u.hdr.e_phentsize != sizeof(Elf_Phdr)) {
|
||||
_rtld_error(
|
||||
"%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path);
|
||||
return NULL;
|
||||
}
|
||||
if (u.hdr.e_phoff + u.hdr.e_phnum * sizeof(Elf_Phdr) > (size_t)nbytes) {
|
||||
_rtld_error("%s: program header too large", path);
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* We rely on the program header being in the first page. This is
|
||||
* not strictly required by the ABI specification, but it seems to
|
||||
* always true in practice. And, it simplifies things considerably.
|
||||
*/
|
||||
if (hdr->e_phentsize != sizeof(Elf_Phdr)) {
|
||||
_rtld_error(
|
||||
"%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path);
|
||||
goto error;
|
||||
}
|
||||
if (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) >
|
||||
(size_t)PAGE_SIZE) {
|
||||
_rtld_error("%s: program header too large", path);
|
||||
goto error;
|
||||
}
|
||||
return (hdr);
|
||||
|
||||
return (&u.hdr);
|
||||
error:
|
||||
munmap(hdr, PAGE_SIZE);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
x
Reference in New Issue
Block a user