From owner-svn-src-all@freebsd.org Fri Oct 18 13:53:15 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id A20421516D0; Fri, 18 Oct 2019 13:53:15 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46vnYR3rmJz4bJp; Fri, 18 Oct 2019 13:53:15 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 676529E61; Fri, 18 Oct 2019 13:53:15 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x9IDrFdd047001; Fri, 18 Oct 2019 13:53:15 GMT (envelope-from markj@FreeBSD.org) Received: (from markj@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x9IDrFu7047000; Fri, 18 Oct 2019 13:53:15 GMT (envelope-from markj@FreeBSD.org) Message-Id: <201910181353.x9IDrFu7047000@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: markj set sender to markj@FreeBSD.org using -f From: Mark Johnston Date: Fri, 18 Oct 2019 13:53:15 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r353729 - head/sys/kern X-SVN-Group: head X-SVN-Commit-Author: markj X-SVN-Commit-Paths: head/sys/kern X-SVN-Commit-Revision: 353729 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 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: Fri, 18 Oct 2019 13:53:15 -0000 Author: markj Date: Fri Oct 18 13:53:14 2019 New Revision: 353729 URL: https://svnweb.freebsd.org/changeset/base/353729 Log: Apply mapping protections to .o kernel modules. Use the section flags to derive mapping protections. When multiple sections overlap within a page, the union of their protections must be applied. With r353701 the .text and .rodata sections are padded to ensure that this does not happen on amd64. Reviewed by: kib MFC after: 1 month Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D21896 Modified: head/sys/kern/link_elf_obj.c Modified: head/sys/kern/link_elf_obj.c ============================================================================== --- head/sys/kern/link_elf_obj.c Fri Oct 18 13:41:08 2019 (r353728) +++ head/sys/kern/link_elf_obj.c Fri Oct 18 13:53:14 2019 (r353729) @@ -70,8 +70,8 @@ __FBSDID("$FreeBSD$"); typedef struct { void *addr; Elf_Off size; - int flags; - int sec; /* Original section */ + int flags; /* Section flags. */ + int sec; /* Original section number. */ char *name; } Elf_progent; @@ -196,6 +196,119 @@ link_elf_init(void *arg) SYSINIT(link_elf_obj, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, NULL); +static void +link_elf_protect_range(elf_file_t ef, vm_offset_t start, vm_offset_t end, + vm_prot_t prot) +{ + int error __unused; + + KASSERT(start <= end && start >= (vm_offset_t)ef->address && + end <= round_page((vm_offset_t)ef->address + ef->lf.size), + ("link_elf_protect_range: invalid range %#jx-%#jx", + (uintmax_t)start, (uintmax_t)end)); + + if (start == end) + return; + error = vm_map_protect(kernel_map, start, end, prot, FALSE); + KASSERT(error == KERN_SUCCESS, + ("link_elf_protect_range: vm_map_protect() returned %d", error)); +} + +/* + * Restrict permissions on linker file memory based on section flags. + * Sections need not be page-aligned, so overlap within a page is possible. + */ +static void +link_elf_protect(elf_file_t ef) +{ + vm_offset_t end, segend, segstart, start; + vm_prot_t gapprot, prot, segprot; + int i; + + /* + * If the file was preloaded, the last page may contain other preloaded + * data which may need to be writeable. ELF files are always + * page-aligned, but other preloaded data, such as entropy or CPU + * microcode may be loaded with a smaller alignment. + */ + gapprot = ef->preloaded ? VM_PROT_RW : VM_PROT_READ; + + start = end = (vm_offset_t)ef->address; + prot = VM_PROT_READ; + for (i = 0; i < ef->nprogtab; i++) { + /* + * VNET and DPCPU sections have their memory allocated by their + * respective subsystems. + */ + if (ef->progtab[i].name != NULL && ( +#ifdef VIMAGE + strcmp(ef->progtab[i].name, VNET_SETNAME) == 0 || +#endif + strcmp(ef->progtab[i].name, DPCPU_SETNAME) == 0)) + continue; + + segstart = trunc_page((vm_offset_t)ef->progtab[i].addr); + segend = round_page((vm_offset_t)ef->progtab[i].addr + + ef->progtab[i].size); + segprot = VM_PROT_READ; + if ((ef->progtab[i].flags & SHF_WRITE) != 0) + segprot |= VM_PROT_WRITE; + if ((ef->progtab[i].flags & SHF_EXECINSTR) != 0) + segprot |= VM_PROT_EXECUTE; + + if (end <= segstart) { + /* + * Case 1: there is no overlap between the previous + * segment and this one. Apply protections to the + * previous segment, and protect the gap between the + * previous and current segments, if any. + */ + link_elf_protect_range(ef, start, end, prot); + link_elf_protect_range(ef, end, segstart, gapprot); + + start = segstart; + end = segend; + prot = segprot; + } else if (start < segstart && end == segend) { + /* + * Case 2: the current segment is a subrange of the + * previous segment. Apply protections to the + * non-overlapping portion of the previous segment. + */ + link_elf_protect_range(ef, start, segstart, prot); + + start = segstart; + prot |= segprot; + } else if (end < segend) { + /* + * Case 3: there is partial overlap between the previous + * and current segments. Apply protections to the + * non-overlapping portion of the previous segment, and + * then the overlap, which must use the union of the two + * segments' protections. + */ + link_elf_protect_range(ef, start, segstart, prot); + link_elf_protect_range(ef, segstart, end, + prot | segprot); + start = end; + end = segend; + prot = segprot; + } else { + /* + * Case 4: the two segments reside in the same page. + */ + prot |= segprot; + } + } + + /* + * Fix up the last unprotected segment and trailing data. + */ + link_elf_protect_range(ef, start, end, prot); + link_elf_protect_range(ef, end, + round_page((vm_offset_t)ef->address + ef->lf.size), gapprot); +} + static int link_elf_link_preload(linker_class_t cls, const char *filename, linker_file_t *result) @@ -358,6 +471,7 @@ link_elf_link_preload(linker_class_t cls, const char * else ef->progtab[pb].name = "<>"; ef->progtab[pb].size = shdr[i].sh_size; + ef->progtab[pb].flags = shdr[i].sh_flags; ef->progtab[pb].sec = i; if (ef->shstrtab && shdr[i].sh_name != 0) ef->progtab[pb].name = @@ -783,7 +897,9 @@ link_elf_load_file(linker_class_t cls, const char *fil /* * In order to satisfy amd64's architectural requirements on the * location of code and data in the kernel's address space, request a - * mapping that is above the kernel. + * mapping that is above the kernel. + * + * Protections will be restricted once relocations are applied. */ #ifdef __amd64__ mapbase = KERNBASE; @@ -884,6 +1000,7 @@ link_elf_load_file(linker_class_t cls, const char *fil goto out; } ef->progtab[pb].size = shdr[i].sh_size; + ef->progtab[pb].flags = shdr[i].sh_flags; ef->progtab[pb].sec = i; if (shdr[i].sh_type == SHT_PROGBITS #ifdef __amd64__ @@ -1023,9 +1140,8 @@ link_elf_load_file(linker_class_t cls, const char *fil goto out; #endif - /* Invoke .ctors */ + link_elf_protect(ef); link_elf_invoke_ctors(lf->ctors_addr, lf->ctors_size); - *result = lf; out: