From owner-freebsd-hackers@FreeBSD.ORG Wed Jun 13 19:12:15 2012 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 90956106564A for ; Wed, 13 Jun 2012 19:12:15 +0000 (UTC) (envelope-from kostikbel@gmail.com) Received: from mail.zoral.com.ua (mx0.zoral.com.ua [91.193.166.200]) by mx1.freebsd.org (Postfix) with ESMTP id CFA808FC0A for ; Wed, 13 Jun 2012 19:12:14 +0000 (UTC) Received: from skuns.kiev.zoral.com.ua (localhost [127.0.0.1]) by mail.zoral.com.ua (8.14.2/8.14.2) with ESMTP id q5DJC6dj017806; Wed, 13 Jun 2012 22:12:06 +0300 (EEST) (envelope-from kostikbel@gmail.com) Received: from deviant.kiev.zoral.com.ua (kostik@localhost [127.0.0.1]) by deviant.kiev.zoral.com.ua (8.14.5/8.14.5) with ESMTP id q5DJC5eE042720; Wed, 13 Jun 2012 22:12:05 +0300 (EEST) (envelope-from kostikbel@gmail.com) Received: (from kostik@localhost) by deviant.kiev.zoral.com.ua (8.14.5/8.14.5/Submit) id q5DJC5CX042719; Wed, 13 Jun 2012 22:12:05 +0300 (EEST) (envelope-from kostikbel@gmail.com) X-Authentication-Warning: deviant.kiev.zoral.com.ua: kostik set sender to kostikbel@gmail.com using -f Date: Wed, 13 Jun 2012 22:12:05 +0300 From: Konstantin Belousov To: Ian Lepore Message-ID: <20120613191205.GU2337@deviant.kiev.zoral.com.ua> References: <1339259223.36051.328.camel@revolution.hippie.lan> <20120609165217.GO85127@deviant.kiev.zoral.com.ua> <1339512694.36051.362.camel@revolution.hippie.lan> <20120612204508.GP2337@deviant.kiev.zoral.com.ua> <1339593249.73426.5.camel@revolution.hippie.lan> Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="Ivd6Nf4GAZ12BQqh" Content-Disposition: inline In-Reply-To: <1339593249.73426.5.camel@revolution.hippie.lan> User-Agent: Mutt/1.4.2.3i X-Virus-Scanned: clamav-milter 0.95.2 at skuns.kiev.zoral.com.ua X-Virus-Status: Clean X-Spam-Status: No, score=-4.0 required=5.0 tests=ALL_TRUSTED,AWL,BAYES_00 autolearn=ham version=3.2.5 X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on skuns.kiev.zoral.com.ua Cc: Wojciech Puchar , freebsd-hackers@freebsd.org Subject: Rtld object tasting [Was: Re: wired memory - again!] X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 13 Jun 2012 19:12:15 -0000 --Ivd6Nf4GAZ12BQqh Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Jun 13, 2012 at 07:14:09AM -0600, Ian Lepore wrote: > http://lists.freebsd.org/pipermail/freebsd-arm/2012-January/003288.html The map_object.c patch is step in the almost right direction, I wanted to remove the static page-sized buffer from get_elf_header for long time. It works because rtld always holds bind_lock exclusively while loading an object. There is no need to copy the first page after it is mapped. commit 0f6f8629af1345acded7c0c685d3ff7b4d9180d6 Author: Konstantin Belousov Date: Wed Jun 13 22:04:18 2012 +0300 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. =20 Fix several cases were the whole mapping of the object leaked on error. =20 Potentially, this leaves one-page gap between succeeding dlopen(3), but there are other mmap(2) consumers as well. diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index d74ef15..7068433 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" =20 -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 */ =20 @@ -121,7 +121,7 @@ map_object(int fd, const char *path, const struct stat = *sb) if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) !=3D 0) { _rtld_error("%s: PT_LOAD segment %d not page-aligned", path, nsegs); - return NULL; + goto error; } break; =20 @@ -161,12 +161,12 @@ map_object(int fd, const char *path, const struct sta= t *sb) } if (phdyn =3D=3D NULL) { _rtld_error("%s: object is not dynamically-linked", path); - return NULL; + goto error; } =20 if (nsegs < 0) { _rtld_error("%s: too few PT_LOAD segments", path); - return NULL; + goto error; } =20 /* @@ -183,13 +183,12 @@ map_object(int fd, const char *path, const struct sta= t *sb) if (mapbase =3D=3D (caddr_t) -1) { _rtld_error("%s: mmap of entire address space failed: %s", path, rtld_strerror(errno)); - return NULL; + goto error; } if (base_addr !=3D NULL && mapbase !=3D base_addr) { _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", path, base_addr, mapbase); - munmap(mapbase, mapsize); - return NULL; + goto error1; } =20 for (i =3D 0; i <=3D nsegs; i++) { @@ -204,7 +203,7 @@ map_object(int fd, const char *path, const struct stat = *sb) data_flags, fd, data_offset) =3D=3D (caddr_t) -1) { _rtld_error("%s: mmap of data failed: %s", path, rtld_strerror(errno)); - return NULL; + goto error1; } =20 /* 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; } =20 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) =3D=3D (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 =3D=3D 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 =3D true; @@ -293,63 +292,72 @@ map_object(int fd, const char *path, const struct sta= t *sb) obj->relro_page =3D obj->relocbase + trunc_page(relro_page); obj->relro_size =3D round_page(relro_size); =20 - return obj; + munmap(hdr, PAGE_SIZE); + return (obj); + +error1: + munmap(mapbase, mapsize); +error: + munmap(hdr, PAGE_SIZE); + return (NULL); } =20 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 =3D pread(fd, u.buf, PAGE_SIZE, 0)) =3D=3D -1) { - _rtld_error("%s: read error: %s", path, rtld_strerror(errno)); - return NULL; - } + Elf_Ehdr *hdr; =20 - /* 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] !=3D ELF_TARG_CLASS - || u.hdr.e_ident[EI_DATA] !=3D ELF_TARG_DATA) { - _rtld_error("%s: unsupported file layout", path); - return NULL; - } - if (u.hdr.e_ident[EI_VERSION] !=3D EV_CURRENT - || u.hdr.e_version !=3D EV_CURRENT) { - _rtld_error("%s: unsupported file version", path); - return NULL; - } - if (u.hdr.e_type !=3D ET_EXEC && u.hdr.e_type !=3D ET_DYN) { - _rtld_error("%s: unsupported file type", path); - return NULL; - } - if (u.hdr.e_machine !=3D ELF_TARG_MACH) { - _rtld_error("%s: unsupported machine", path); - return NULL; - } + hdr =3D mmap(NULL, PAGE_SIZE, PROT_READ, 0, fd, 0); + if (hdr =3D=3D (Elf_Ehdr *)MAP_FAILED) { + _rtld_error("%s: read error: %s", path, rtld_strerror(errno)); + return NULL; + } =20 - /* - * 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 !=3D sizeof(Elf_Phdr)) { - _rtld_error( - "%s: invalid shared object: e_phentsize !=3D 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] !=3D ELF_TARG_CLASS || + hdr->e_ident[EI_DATA] !=3D ELF_TARG_DATA) { + _rtld_error("%s: unsupported file layout", path); + goto error; + } + if (hdr->e_ident[EI_VERSION] !=3D EV_CURRENT || + hdr->e_version !=3D EV_CURRENT) { + _rtld_error("%s: unsupported file version", path); + goto error; + } + if (hdr->e_type !=3D ET_EXEC && hdr->e_type !=3D ET_DYN) { + _rtld_error("%s: unsupported file type", path); + goto error; + } + if (hdr->e_machine !=3D 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 (hdr->e_phentsize !=3D sizeof(Elf_Phdr)) { + _rtld_error( + "%s: invalid shared object: e_phentsize !=3D 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); =20 - return (&u.hdr); +error: + munmap(hdr, PAGE_SIZE); + return (NULL); } =20 void --Ivd6Nf4GAZ12BQqh Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (FreeBSD) iEYEARECAAYFAk/Y5gUACgkQC3+MBN1Mb4hoagCeOXwjxBuWFDTlywn8fJcLlpUq apwAoPduJnKqW0X9U9KjZiMMIP1LbhzP =OcdG -----END PGP SIGNATURE----- --Ivd6Nf4GAZ12BQqh--