Received: by 2002:ac0:a581:0:0:0:0:0 with SMTP id m1-v6csp4424372imm; Mon, 25 Jun 2018 15:44:45 -0700 (PDT) X-Google-Smtp-Source: ADUXVKKn3JFqtSEt78ClA8yuNVky8pJwOySt9mZG/aasZbYKL0deChV/iZ/LMJifBkK80L5DidM8 X-Received: by 2002:a62:2044:: with SMTP id g65-v6mr14811419pfg.40.1529966685932; Mon, 25 Jun 2018 15:44:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529966685; cv=none; d=google.com; s=arc-20160816; b=siuzppJZz1ul277SOrMK7lLu0HGjyhE6K+FLbXakni5wq0kxwEsisRMIRGpvLwblb3 KwVvsye6zv7yyMuAw8DlnknnAzUosfttKqgFebO6w/JaMS5KE5hQ9N36IyF/qvPhrfze 1oCsGYRJKE/Ujvnt3CD7APWsMpaTE4vOXu1EfHp3nDvhSg+pchsn+h6a18HjKt5V3hci mtCbdxEGEioQww908VMloce4UUDT2j/5c9zelll4F3zheEnPMbltpDr668Egrv7HV37+ KDUebPspfXDVFqbCwLgYmgF1ysidCWmU0DcOPaVn3d5vMO6QJ/7dfJjIONU/zOhEONv9 R6ow== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:from:subject:references:message-id :in-reply-to:date:mime-version:dkim-signature :arc-authentication-results; bh=ESq6rfDNik/kXwRZbH6VPHQYFEm3dOylYovQo3are/c=; b=fjqd87CZbD66Zirafrw4u1/dqPTupGNzOi1cmIs6nKjTZM72szNgTpXpj9CSx9MXkz ZfhzmQTLzLZeR+kvG+uv1KatCWC27qvqpim8O3yQ3lUzCIxppCRsqp74A6SSLZEOe4kP nEg0fOv01vNDoDPwTBaD3QtCms9C/x3VJihwzf9ywqOmd/74+/ub9SbEsxQUwiTSssTA 6CraNKbvasCTMsPrzLsUbr/RRDdyLUtpzq54RYiAFD7Eq143gCbUlZLW2TQS6CneOdHG brT95/FEH/iBIC7RqF/6VXH/FrEEnbOpdhbRAWfJQhLt17ACSlaCtBDIwaV3ZwcwJTGL ZSAA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="apXGASg/"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i1-v6si72328plt.183.2018.06.25.15.44.31; Mon, 25 Jun 2018 15:44:45 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="apXGASg/"; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965017AbeFYWmq (ORCPT + 99 others); Mon, 25 Jun 2018 18:42:46 -0400 Received: from mail-ua0-f201.google.com ([209.85.217.201]:41028 "EHLO mail-ua0-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S964955AbeFYWmm (ORCPT ); Mon, 25 Jun 2018 18:42:42 -0400 Received: by mail-ua0-f201.google.com with SMTP id l15-v6so97797uae.8 for ; Mon, 25 Jun 2018 15:42:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:date:in-reply-to:message-id:references:subject:from:to :cc; bh=ESq6rfDNik/kXwRZbH6VPHQYFEm3dOylYovQo3are/c=; b=apXGASg/GZZ7EKt+TqN7gssCs0OUjKQFdfG//DagNhRFBL1cvg8Nq6xVYuLI/9Djc1 qGIVO7TCHsEiE+8RrrpuMW3SX+88d1JBFT5I7zufdfRy6M5WtXuzB/jwzlIlWbyfYuon +ng8ka561FKuTrIjDxeg56ey2NYLoLEiGMurwTTb7VMLRZBIw2N993zD5AzD+k0yYB5B JPkKG7t/wZ65cblDPV0ftVZxabrFo+LJc6Ig/RBdQVSvSQBMJVDLKyPSBS+vs7mgKwxj K6w3aRDRj7EKb8c+nZe/XASN2+OX94VTZtRRxFuXCOtFK8v78MZn9pdJGao2iDwo6rG5 o2UA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:date:in-reply-to:message-id :references:subject:from:to:cc; bh=ESq6rfDNik/kXwRZbH6VPHQYFEm3dOylYovQo3are/c=; b=Ce7GMbhtgW/qc3ZQ7E9oVyZz9sONvpkOG9Fq49R+2xzVrYSig0plp32vEFQZypZCVQ TE1ipPTfZSTDNiWO2hB8hBIlTnhMa6Y4oHWD6x+OEtwSqhs2mjgelRX9o7vtM2F757jE Tzh0FdpcAkM2EKNFAuubEgcaXKvywoD4vyyc1/0jOd6Ro9DBacS9W4CACN9HgfGMFCLi yByErNbOpf8vLVliZCpFAfEA6djMbdpTHchhXtkY+pJQUtenln1YIU/R4qUBqC1QjxzY o+uY7JfwP84YTL5VYYS8TLQEdVlUtcjblFXR1aM6CIwM16cLgnzYEGTz+CboGXhPLM05 0tbQ== X-Gm-Message-State: APt69E0ayvVEk15VMG+cZB52Ql3wt25aSBhp5/Osj3crefkdo+m2jJjr BQn73TAjCHXA1F7wmnvGn+YNGSvt3k1EDg== MIME-Version: 1.0 X-Received: by 2002:a1f:245:: with SMTP id 66-v6mr1609237vkc.35.1529966561458; Mon, 25 Jun 2018 15:42:41 -0700 (PDT) Date: Mon, 25 Jun 2018 15:39:11 -0700 In-Reply-To: <20180625224014.134829-1-thgarnie@google.com> Message-Id: <20180625224014.134829-24-thgarnie@google.com> References: <20180625224014.134829-1-thgarnie@google.com> X-Mailer: git-send-email 2.18.0.rc2.346.g013aa6912e-goog Subject: [PATCH v5 23/27] x86/modules: Adapt module loading for PIE support From: Thomas Garnier To: kernel-hardening@lists.openwall.com Cc: Thomas Garnier , Thomas Gleixner , Ingo Molnar , "H. Peter Anvin" , x86@kernel.org, Josh Poimboeuf , Philippe Ombredanne , Kate Stewart , Greg Kroah-Hartman , Francis Deslauriers , "H.J. Lu" , linux-kernel@vger.kernel.org Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Adapt module loading to support PIE relocations. Generate dynamic GOT if a symbol requires it but no entry exists in the kernel GOT. Position Independent Executable (PIE) support will allow to extend the KASLR randomization range 0xffffffff80000000. Signed-off-by: Thomas Garnier --- arch/x86/Makefile | 4 + arch/x86/include/asm/module.h | 11 ++ arch/x86/include/asm/sections.h | 4 + arch/x86/kernel/module.c | 181 +++++++++++++++++++++++++++++++- arch/x86/kernel/module.lds | 3 + 5 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 arch/x86/kernel/module.lds diff --git a/arch/x86/Makefile b/arch/x86/Makefile index c2c221fd20d7..112ccdc99566 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -134,7 +134,11 @@ else KBUILD_CFLAGS += $(cflags-y) KBUILD_CFLAGS += -mno-red-zone +ifdef CONFIG_X86_PIE + KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/x86/kernel/module.lds +else KBUILD_CFLAGS += -mcmodel=kernel +endif # -funit-at-a-time shrinks the kernel .text considerably # unfortunately it makes reading oopses harder. diff --git a/arch/x86/include/asm/module.h b/arch/x86/include/asm/module.h index 7948a17febb4..68ff05e14288 100644 --- a/arch/x86/include/asm/module.h +++ b/arch/x86/include/asm/module.h @@ -5,12 +5,23 @@ #include #include +#ifdef CONFIG_X86_PIE +struct mod_got_sec { + struct elf64_shdr *got; + int got_num_entries; + int got_max_entries; +}; +#endif + struct mod_arch_specific { #ifdef CONFIG_UNWINDER_ORC unsigned int num_orcs; int *orc_unwind_ip; struct orc_entry *orc_unwind; #endif +#ifdef CONFIG_X86_PIE + struct mod_got_sec core; +#endif }; #ifdef CONFIG_X86_64 diff --git a/arch/x86/include/asm/sections.h b/arch/x86/include/asm/sections.h index 5c019d23d06b..da3d98bb2bcb 100644 --- a/arch/x86/include/asm/sections.h +++ b/arch/x86/include/asm/sections.h @@ -13,4 +13,8 @@ extern char __end_rodata_hpage_align[]; extern char __entry_trampoline_start[], __entry_trampoline_end[]; #endif +#if defined(CONFIG_X86_PIE) +extern char __start_got[], __end_got[]; +#endif + #endif /* _ASM_X86_SECTIONS_H */ diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index f58336af095c..88895f3d474b 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,173 @@ static unsigned long int get_module_load_offset(void) } #endif +#ifdef CONFIG_X86_PIE +static u64 find_got_kernel_entry(Elf64_Sym *sym, const Elf64_Rela *rela) +{ + u64 *pos; + + for (pos = (u64*)__start_got; pos < (u64*)__end_got; pos++) { + if (*pos == sym->st_value) + return (u64)pos + rela->r_addend; + } + + return 0; +} + +static u64 module_emit_got_entry(struct module *mod, void *loc, + const Elf64_Rela *rela, Elf64_Sym *sym) +{ + struct mod_got_sec *gotsec = &mod->arch.core; + u64 *got = (u64*)gotsec->got->sh_addr; + int i = gotsec->got_num_entries; + u64 ret; + + /* Check if we can use the kernel GOT */ + ret = find_got_kernel_entry(sym, rela); + if (ret) + return ret; + + got[i] = sym->st_value; + + /* + * Check if the entry we just created is a duplicate. Given that the + * relocations are sorted, this will be the last entry we allocated. + * (if one exists). + */ + if (i > 0 && got[i] == got[i - 2]) { + ret = (u64)&got[i - 1]; + } else { + gotsec->got_num_entries++; + BUG_ON(gotsec->got_num_entries > gotsec->got_max_entries); + ret = (u64)&got[i]; + } + + return ret + rela->r_addend; +} + +#define cmp_3way(a,b) ((a) < (b) ? -1 : (a) > (b)) + +static int cmp_rela(const void *a, const void *b) +{ + const Elf64_Rela *x = a, *y = b; + int i; + + /* sort by type, symbol index and addend */ + i = cmp_3way(ELF64_R_TYPE(x->r_info), ELF64_R_TYPE(y->r_info)); + if (i == 0) + i = cmp_3way(ELF64_R_SYM(x->r_info), ELF64_R_SYM(y->r_info)); + if (i == 0) + i = cmp_3way(x->r_addend, y->r_addend); + return i; +} + +static bool duplicate_rel(const Elf64_Rela *rela, int num) +{ + /* + * Entries are sorted by type, symbol index and addend. That means + * that, if a duplicate entry exists, it must be in the preceding + * slot. + */ + return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0; +} + +static unsigned int count_gots(Elf64_Sym *syms, Elf64_Rela *rela, int num) +{ + unsigned int ret = 0; + Elf64_Sym *s; + int i; + + for (i = 0; i < num; i++) { + switch (ELF64_R_TYPE(rela[i].r_info)) { + case R_X86_64_GOTPCREL: + s = syms + ELF64_R_SYM(rela[i].r_info); + + /* + * Use the kernel GOT when possible, else reserve a + * custom one for this module. + */ + if (!duplicate_rel(rela, i) && + !find_got_kernel_entry(s, rela + i)) + ret++; + break; + } + } + return ret; +} + +/* + * Generate GOT entries for GOTPCREL relocations that do not exists in the + * kernel GOT. Based on arm64 module-plts implementation. + */ +int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, + char *secstrings, struct module *mod) +{ + unsigned long gots = 0; + Elf_Shdr *symtab = NULL; + Elf64_Sym *syms = NULL; + char *strings, *name; + int i; + + /* + * Find the empty .got section so we can expand it to store the PLT + * entries. Record the symtab address as well. + */ + for (i = 0; i < ehdr->e_shnum; i++) { + if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) { + mod->arch.core.got = sechdrs + i; + } else if (sechdrs[i].sh_type == SHT_SYMTAB) { + symtab = sechdrs + i; + syms = (Elf64_Sym *)symtab->sh_addr; + } + } + + if (!mod->arch.core.got) { + pr_err("%s: module GOT section missing\n", mod->name); + return -ENOEXEC; + } + if (!syms) { + pr_err("%s: module symtab section missing\n", mod->name); + return -ENOEXEC; + } + + for (i = 0; i < ehdr->e_shnum; i++) { + Elf64_Rela *rels = (void *)ehdr + sechdrs[i].sh_offset; + int numrels = sechdrs[i].sh_size / sizeof(Elf64_Rela); + + if (sechdrs[i].sh_type != SHT_RELA) + continue; + + /* sort by type, symbol index and addend */ + sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL); + + gots += count_gots(syms, rels, numrels); + } + + mod->arch.core.got->sh_type = SHT_NOBITS; + mod->arch.core.got->sh_flags = SHF_ALLOC; + mod->arch.core.got->sh_addralign = L1_CACHE_BYTES; + mod->arch.core.got->sh_size = (gots + 1) * sizeof(u64); + mod->arch.core.got_num_entries = 0; + mod->arch.core.got_max_entries = gots; + + /* + * If a _GLOBAL_OFFSET_TABLE_ symbol exists, make it absolute for + * modules to correctly reference it. Similar to s390 implementation. + */ + strings = (void *) ehdr + sechdrs[symtab->sh_link].sh_offset; + for (i = 0; i < symtab->sh_size/sizeof(Elf_Sym); i++) { + if (syms[i].st_shndx != SHN_UNDEF) + continue; + name = strings + syms[i].st_name; + if (!strcmp(name, "_GLOBAL_OFFSET_TABLE_")) { + syms[i].st_shndx = SHN_ABS; + break; + } + } + return 0; +} +#endif + void *module_alloc(unsigned long size) { void *p; @@ -190,16 +358,20 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, if ((s64)val != *(s32 *)loc) goto overflow; break; +#ifdef CONFIG_X86_PIE + case R_X86_64_GOTPCREL: + val = module_emit_got_entry(me, loc, rel + i, sym); + /* fallthrough */ +#endif case R_X86_64_PC32: case R_X86_64_PLT32: if (*(u32 *)loc != 0) goto invalid_relocation; val -= (u64)loc; *(u32 *)loc = val; -#if 0 - if ((s64)val != *(s32 *)loc) + if (IS_ENABLED(CONFIG_X86_PIE) && + (s64)val != *(s32 *)loc) goto overflow; -#endif break; default: pr_err("%s: Unknown rela relocation: %llu\n", @@ -217,8 +389,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, overflow: pr_err("overflow in relocation type %d val %Lx\n", (int)ELF64_R_TYPE(rel[i].r_info), val); - pr_err("`%s' likely not compiled with -mcmodel=kernel\n", - me->name); + pr_err("`%s' likely too far from the kernel\n", me->name); return -ENOEXEC; } #endif diff --git a/arch/x86/kernel/module.lds b/arch/x86/kernel/module.lds new file mode 100644 index 000000000000..fd6e95a4b454 --- /dev/null +++ b/arch/x86/kernel/module.lds @@ -0,0 +1,3 @@ +SECTIONS { + .got (NOLOAD) : { BYTE(0) } +} -- 2.18.0.rc2.346.g013aa6912e-goog