From owner-svn-src-all@FreeBSD.ORG Thu Jun 14 11:20:23 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (unknown [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 8248A106566C; Thu, 14 Jun 2012 11:20:23 +0000 (UTC) (envelope-from kib@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [69.147.83.44]) by mx1.freebsd.org (Postfix) with ESMTP id 6E1F48FC08; Thu, 14 Jun 2012 11:20:23 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q5EBKN7M090891; Thu, 14 Jun 2012 11:20:23 GMT (envelope-from kib@svn.freebsd.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q5EBKNs0090889; Thu, 14 Jun 2012 11:20:23 GMT (envelope-from kib@svn.freebsd.org) Message-Id: <201206141120.q5EBKNs0090889@svn.freebsd.org> From: Konstantin Belousov Date: Thu, 14 Jun 2012 11:20:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r237058 - head/libexec/rtld-elf X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Jun 2012 11:20:23 -0000 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 [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