Author: kib
Date: Thu Jun 14 11:20:22 2012
New Revision: 237058
URL: http://svn.freebsd.org/changeset/base/237058

Log:
  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

Modified:
  head/libexec/rtld-elf/map_object.c

Modified: head/libexec/rtld-elf/map_object.c
==============================================================================
--- head/libexec/rtld-elf/map_object.c  Thu Jun 14 11:17:54 2012        
(r237057)
+++ head/libexec/rtld-elf/map_object.c  Thu Jun 14 11:20:22 2012        
(r237058)
@@ -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, con
            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, con
     }
     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, con
     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, con
        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, con
                     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, con
                    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, con
        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, con
     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;
-    }
-
-    /* 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;
-    }
+       Elf_Ehdr *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 (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;
-    }
+       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 (!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
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to