summaryrefslogtreecommitdiffstats
path: root/libexec
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2012-06-14 11:20:22 +0000
committerkib <kib@FreeBSD.org>2012-06-14 11:20:22 +0000
commit0e0ae1797174c083bfec06fb3c064ddc5998ee4d (patch)
tree03bfaa9fc1a867b0b9876e8f9377b0178a768155 /libexec
parent181a5cf0b01c09910480cec151eddd788b21a108 (diff)
downloadFreeBSD-src-0e0ae1797174c083bfec06fb3c064ddc5998ee4d.zip
FreeBSD-src-0e0ae1797174c083bfec06fb3c064ddc5998ee4d.tar.gz
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
Diffstat (limited to 'libexec')
-rw-r--r--libexec/rtld-elf/map_object.c132
1 files changed, 70 insertions, 62 deletions
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
index d74ef15..509a64f 100644
--- a/libexec/rtld-elf/map_object.c
+++ b/libexec/rtld-elf/map_object.c
@@ -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;
-
- if ((nbytes = pread(fd, u.buf, PAGE_SIZE, 0)) == -1) {
- _rtld_error("%s: read error: %s", path, rtld_strerror(errno));
- return NULL;
- }
+ Elf_Ehdr *hdr;
- /* 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;
- }
+ 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);
+ }
- /*
- * 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;
- }
+ /* 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;
+ }
- return (&u.hdr);
+ /*
+ * 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);
+
+error:
+ munmap(hdr, PAGE_SIZE);
+ return (NULL);
}
void
OpenPOWER on IntegriCloud