From patchwork Fri Jan 15 14:59:41 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Roger_Pau_Monn=C3=A9?= X-Patchwork-Id: 8042331 Return-Path: X-Original-To: patchwork-xen-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id F2C289FE73 for ; Fri, 15 Jan 2016 15:02:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 3E4B52044B for ; Fri, 15 Jan 2016 15:02:52 +0000 (UTC) Received: from lists.xen.org (lists.xenproject.org [50.57.142.19]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E054320384 for ; Fri, 15 Jan 2016 15:02:49 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xen.org) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1aK5r3-0000p2-NQ; Fri, 15 Jan 2016 14:59:57 +0000 Received: from mail6.bemta3.messagelabs.com ([195.245.230.39]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1aK5r1-0000o8-OM for xen-devel@lists.xenproject.org; Fri, 15 Jan 2016 14:59:55 +0000 Received: from [85.158.137.68] by server-17.bemta-3.messagelabs.com id D7/97-02940-B6909965; Fri, 15 Jan 2016 14:59:55 +0000 X-Env-Sender: prvs=8150a737c=roger.pau@citrix.com X-Msg-Ref: server-5.tower-31.messagelabs.com!1452869990!16144022!2 X-Originating-IP: [66.165.176.89] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogNjYuMTY1LjE3Ni44OSA9PiAyMDMwMDc=\n, received_headers: No Received headers X-StarScan-Received: X-StarScan-Version: 7.35.1; banners=-,-,- X-VirusChecked: Checked Received: (qmail 17762 invoked from network); 15 Jan 2016 14:59:53 -0000 Received: from smtp.citrix.com (HELO SMTP.CITRIX.COM) (66.165.176.89) by server-5.tower-31.messagelabs.com with RC4-SHA encrypted SMTP; 15 Jan 2016 14:59:53 -0000 X-IronPort-AV: E=Sophos;i="5.22,299,1449532800"; d="scan'208";a="325418774" From: Roger Pau Monne To: Date: Fri, 15 Jan 2016 15:59:41 +0100 Message-ID: <1452869983-75956-3-git-send-email-roger.pau@citrix.com> X-Mailer: git-send-email 1.9.5 (Apple Git-50.3) In-Reply-To: <1452869983-75956-1-git-send-email-roger.pau@citrix.com> References: <1452869983-75956-1-git-send-email-roger.pau@citrix.com> MIME-Version: 1.0 X-DLP: MIA1 Cc: Keir Fraser , Ian Campbell , Ian Jackson , Tim Deegan , Jan Beulich , Roger Pau Monne Subject: [Xen-devel] [PATCH v2 2/4] libelf: rewrite symtab/strtab loading for Dom0 X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Current implementation of elf_load_bsdsyms is broken when loading inside of a HVM guest, because it assumes elf_memcpy_safe is able to write into guest memory space, which it is not. Take the oportunity to do some cleanup and properly document how elf_{parse/load}_bsdsyms works. The new implementation uses elf_load_image when dealing with data that needs to be copied to the guest memory space. Also reduce the number of section headers copied to the minimum necessary. Signed-off-by: Roger Pau Monné --- Cc: Ian Campbell Cc: Ian Jackson Cc: Jan Beulich Cc: Keir Fraser Cc: Tim Deegan --- xen/common/libelf/libelf-loader.c | 213 ++++++++++++++++++++++++++++---------- 1 file changed, 158 insertions(+), 55 deletions(-) diff --git a/xen/common/libelf/libelf-loader.c b/xen/common/libelf/libelf-loader.c index 6f42bea..9552d4c 100644 --- a/xen/common/libelf/libelf-loader.c +++ b/xen/common/libelf/libelf-loader.c @@ -153,7 +153,7 @@ void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) { uint64_t sz; ELF_HANDLE_DECL(elf_shdr) shdr; - unsigned i, type; + unsigned int i; if ( !ELF_HANDLE_VALID(elf->sym_tab) ) return; @@ -164,20 +164,33 @@ void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) sz = sizeof(uint32_t); /* Space for the elf and elf section headers */ - sz += (elf_uval(elf, elf->ehdr, e_ehsize) + - elf_shdr_count(elf) * elf_uval(elf, elf->ehdr, e_shentsize)); + sz += elf_uval(elf, elf->ehdr, e_ehsize) + + 3 * elf_uval(elf, elf->ehdr, e_shentsize); sz = elf_round_up(elf, sz); - /* Space for the symbol and string tables. */ + /* Space for the symbol and string table. */ for ( i = 0; i < elf_shdr_count(elf); i++ ) { shdr = elf_shdr_by_index(elf, i); if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) /* input has an insane section header count field */ break; - type = elf_uval(elf, shdr, sh_type); - if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) ) - sz = elf_round_up(elf, sz + elf_uval(elf, shdr, sh_size)); + + if ( elf_uval(elf, shdr, sh_type) != SHT_SYMTAB ) + continue; + + sz = elf_round_up(elf, sz + elf_uval(elf, shdr, sh_size)); + shdr = elf_shdr_by_index(elf, elf_uval(elf, shdr, sh_link)); + + if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) + /* input has an insane section header count field */ + break; + + if ( elf_uval(elf, shdr, sh_type) != SHT_STRTAB ) + /* Invalid symtab -> strtab link */ + break; + + sz = elf_round_up(elf, sz + elf_uval(elf, shdr, sh_size)); } elf->bsd_symtab_pstart = pstart; @@ -186,13 +199,31 @@ void elf_parse_bsdsyms(struct elf_binary *elf, uint64_t pstart) static void elf_load_bsdsyms(struct elf_binary *elf) { - ELF_HANDLE_DECL(elf_ehdr) sym_ehdr; - unsigned long sz; - elf_ptrval maxva; - elf_ptrval symbase; - elf_ptrval symtab_addr; - ELF_HANDLE_DECL(elf_shdr) shdr; - unsigned i, type; + /* + * Header that is placed at the end of the kernel and allows + * the OS to find where the symtab and strtab have been loaded. + * It mimics a valid ELF file header, although it only contains + * a symtab and a strtab section. + * + * NB: according to the ELF spec there's only ONE symtab per ELF + * file, and accordingly we will only load the corresponding + * strtab, so we only need three section headers in our fake ELF + * header (first section header is always a dummy). + */ + struct __packed { + elf_ehdr header; + elf_shdr section[3]; + } symbol_header; + + ELF_HANDLE_DECL(elf_ehdr) header_handle; + unsigned long shdr_size; + uint32_t symsize; + ELF_HANDLE_DECL(elf_shdr) section_handle; + ELF_HANDLE_DECL(elf_shdr) image_handle; + unsigned int i, link; + elf_ptrval header_base; + elf_ptrval symtab_base; + elf_ptrval strtab_base; if ( !elf->bsd_symtab_pstart ) return; @@ -200,64 +231,136 @@ static void elf_load_bsdsyms(struct elf_binary *elf) #define elf_hdr_elm(_elf, _hdr, _elm, _val) \ do { \ if ( elf_64bit(_elf) ) \ - elf_store_field(_elf, _hdr, e64._elm, _val); \ + (_hdr).e64._elm = _val; \ else \ - elf_store_field(_elf, _hdr, e32._elm, _val); \ + (_hdr).e32._elm = _val; \ } while ( 0 ) - symbase = elf_get_ptr(elf, elf->bsd_symtab_pstart); - symtab_addr = maxva = symbase + sizeof(uint32_t); +#define SYMTAB_INDEX 1 +#define STRTAB_INDEX 2 - /* Set up Elf header. */ - sym_ehdr = ELF_MAKE_HANDLE(elf_ehdr, symtab_addr); - sz = elf_uval(elf, elf->ehdr, e_ehsize); - elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(sym_ehdr), ELF_HANDLE_PTRVAL(elf->ehdr), sz); - maxva += sz; /* no round up */ + /* Allow elf_memcpy_safe to write to symbol_header. */ + elf->caller_xdest_base = &symbol_header; + elf->caller_xdest_size = sizeof(symbol_header); - elf_hdr_elm(elf, sym_ehdr, e_phoff, 0); - elf_hdr_elm(elf, sym_ehdr, e_shoff, elf_uval(elf, elf->ehdr, e_ehsize)); - elf_hdr_elm(elf, sym_ehdr, e_phentsize, 0); - elf_hdr_elm(elf, sym_ehdr, e_phnum, 0); - - /* Copy Elf section headers. */ - shdr = ELF_MAKE_HANDLE(elf_shdr, maxva); - sz = elf_shdr_count(elf) * elf_uval(elf, elf->ehdr, e_shentsize); - elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(shdr), - ELF_IMAGE_BASE(elf) + elf_uval(elf, elf->ehdr, e_shoff), - sz); - maxva = elf_round_up(elf, (unsigned long)maxva + sz); + /* + * Calculate the position of the various elements in GUEST MEMORY SPACE. + * This addresses MUST only be used with elf_load_image. + * + * NB: strtab_base cannot be calculated at this point because we don't + * know the size of the symtab yet, and the strtab will be placed after it. + */ + header_base = elf_get_ptr(elf, elf->bsd_symtab_pstart) + sizeof(uint32_t); + symtab_base = elf_round_up(elf, header_base + sizeof(symbol_header)); + + /* Fill the ELF header, copied from the original ELF header. */ + header_handle = ELF_MAKE_HANDLE(elf_ehdr, + ELF_REALPTR2PTRVAL(&symbol_header.header)); + elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(header_handle), + ELF_HANDLE_PTRVAL(elf->ehdr), + elf_uval(elf, elf->ehdr, e_ehsize)); + + /* Set the offset to the shdr array. */ + elf_hdr_elm(elf, symbol_header.header, e_shoff, + offsetof(typeof(symbol_header), section)); + + /* Set the right number of section headers. */ + elf_hdr_elm(elf, symbol_header.header, e_shnum, 3); + + /* Clear a couple of fields we don't use. */ + elf_hdr_elm(elf, symbol_header.header, e_phoff, 0); + elf_hdr_elm(elf, symbol_header.header, e_phentsize, 0); + elf_hdr_elm(elf, symbol_header.header, e_phnum, 0); + + /* Zero the dummy section. */ + section_handle = ELF_MAKE_HANDLE(elf_shdr, + ELF_REALPTR2PTRVAL(&symbol_header.section[SHN_UNDEF])); + shdr_size = elf_uval(elf, elf->ehdr, e_shentsize); + elf_memset_safe(elf, ELF_HANDLE_PTRVAL(section_handle), 0, shdr_size); + /* + * Find the actual symtab and strtab in the ELF. + * + * The symtab section header is going to reside in section[SYMTAB_INDEX], + * while the corresponding strtab is going to be placed in + * section[STRTAB_INDEX]. sh_offset is mangled so it points to the offset + * where the sections are actually loaded (relative to the ELF header + * location). + */ + section_handle = ELF_MAKE_HANDLE(elf_shdr, + ELF_REALPTR2PTRVAL(&symbol_header.section[SYMTAB_INDEX])); for ( i = 0; i < elf_shdr_count(elf); i++ ) { - elf_ptrval old_shdr_p; - elf_ptrval new_shdr_p; - type = elf_uval(elf, shdr, sh_type); - if ( (type == SHT_STRTAB) || (type == SHT_SYMTAB) ) + image_handle = elf_shdr_by_index(elf, i); + if ( elf_uval(elf, image_handle, sh_type) != SHT_SYMTAB ) + continue; + + elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(section_handle), + ELF_HANDLE_PTRVAL(image_handle), + shdr_size); + + link = elf_uval(elf, section_handle, sh_link); + if ( link == SHN_UNDEF ) { - elf_msg(elf, "%s: shdr %i at 0x%"ELF_PRPTRVAL" -> 0x%"ELF_PRPTRVAL"\n", __func__, i, - elf_section_start(elf, shdr), maxva); - sz = elf_uval(elf, shdr, sh_size); - elf_memcpy_safe(elf, maxva, elf_section_start(elf, shdr), sz); - /* Mangled to be based on ELF header location. */ - elf_hdr_elm(elf, shdr, sh_offset, maxva - symtab_addr); - maxva = elf_round_up(elf, (unsigned long)maxva + sz); + elf_mark_broken(elf, "bad link in symtab"); + break; } - old_shdr_p = ELF_HANDLE_PTRVAL(shdr); - new_shdr_p = old_shdr_p + elf_uval(elf, elf->ehdr, e_shentsize); - if ( new_shdr_p <= old_shdr_p ) /* wrapped or stuck */ + + /* Load symtab into guest memory. */ + elf_load_image(elf, symtab_base, elf_section_start(elf, section_handle), + elf_uval(elf, section_handle, sh_size), + elf_uval(elf, section_handle, sh_size)); + elf_hdr_elm(elf, symbol_header.section[SYMTAB_INDEX], sh_offset, + symtab_base - header_base); + elf_hdr_elm(elf, symbol_header.section[SYMTAB_INDEX], sh_link, + STRTAB_INDEX); + + /* Calculate the guest address where strtab is loaded. */ + strtab_base = elf_round_up(elf, symtab_base + + elf_uval(elf, section_handle, sh_size)); + + /* Load strtab section header. */ + section_handle = ELF_MAKE_HANDLE(elf_shdr, + ELF_REALPTR2PTRVAL(&symbol_header.section[STRTAB_INDEX])); + elf_memcpy_safe(elf, ELF_HANDLE_PTRVAL(section_handle), + ELF_HANDLE_PTRVAL(elf_shdr_by_index(elf, link)), + shdr_size); + + if ( elf_uval(elf, section_handle, sh_type) != SHT_STRTAB ) { - elf_mark_broken(elf, "bad section header length"); + elf_mark_broken(elf, "strtab not found"); break; } - if ( !elf_access_ok(elf, new_shdr_p, 1) ) /* outside image */ - break; - shdr = ELF_MAKE_HANDLE(elf_shdr, new_shdr_p); + + /* Load strtab into guest memory. */ + elf_load_image(elf, strtab_base, elf_section_start(elf, section_handle), + elf_uval(elf, section_handle, sh_size), + elf_uval(elf, section_handle, sh_size)); + elf_hdr_elm(elf, symbol_header.section[STRTAB_INDEX], sh_offset, + strtab_base - header_base); + + /* Store the whole size (including headers and loaded sections). */ + symsize = strtab_base + elf_uval(elf, section_handle, sh_size) - + header_base; + break; } - /* Write down the actual sym size. */ - elf_store_val(elf, uint32_t, symbase, maxva - symtab_addr); + /* Load the total size at symtab_pstart. */ + elf_load_image(elf, elf_get_ptr(elf, elf->bsd_symtab_pstart), + ELF_REALPTR2PTRVAL(&symsize), sizeof(symsize), + sizeof(symsize)); + + /* Load the headers. */ + elf_load_image(elf, header_base, ELF_REALPTR2PTRVAL(&symbol_header), + sizeof(symbol_header), sizeof(symbol_header)); + + /* Remove permissions from elf_memcpy_safe. */ + elf->caller_xdest_base = NULL; + elf->caller_xdest_size = 0; +#undef SYMTAB_INDEX +#undef STRTAB_INDEX #undef elf_ehdr_elm }