Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 28 Jun 2012 14:13:45 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r237712 - stable/9/libexec/rtld-elf
Message-ID:  <201206281413.q5SEDjXI080538@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Thu Jun 28 14:13:45 2012
New Revision: 237712
URL: http://svn.freebsd.org/changeset/base/237712

Log:
  MFC r237058:
  Eliminate the static buffer used to read the first page of the mapped
  object, and eliminate the pread(2) call as well. 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

Modified:
  stable/9/libexec/rtld-elf/map_object.c
Directory Properties:
  stable/9/libexec/rtld-elf/   (props changed)

Modified: stable/9/libexec/rtld-elf/map_object.c
==============================================================================
--- stable/9/libexec/rtld-elf/map_object.c	Thu Jun 28 13:47:07 2012	(r237711)
+++ stable/9/libexec/rtld-elf/map_object.c	Thu Jun 28 14:13:45 2012	(r237712)
@@ -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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201206281413.q5SEDjXI080538>