Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp3856173imm; Tue, 29 May 2018 15:22:14 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJbdtyJEXz2Lbem99iqZ7rnQwbjOgaHk/CW3TLEIN24D12GfF6aZEOQNzT1B77ZdmYLUzz7 X-Received: by 2002:a17:902:7209:: with SMTP id ba9-v6mr234364plb.119.1527632534555; Tue, 29 May 2018 15:22:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527632534; cv=none; d=google.com; s=arc-20160816; b=I4x7xE7PMjw/gMfhirCb4/ZUGhV0VijT8Vpy8IiXWq0CJqCG8KnGleK278tm0aPio5 qRYFxiYGPfV62DL9cDjRA5U92hM7mM0/jQi9YL3W5SBahNxrCRZ3fUQoegcCkv9XrgsT +Vg+yRu1ze86zvDULC+A5vlJlq2S+Ky4oiB5iNgjLyIbkHIp7OLbYxNiL3VFNnybsqIH Vk+dq5XVrw3hNYly5a3fhLsLlQqSk9yp66qrvppzY/Z857ezr0B7WnPI1gjWuxWBJaaw gOcCrI86Xlcd48vr9XXizl1qyqCTt4p1LuhiMQ3SaIp+AWACaQ1/TXMqi7kE8Q8KfG8Y KtHw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=aUAPrGEGg4q+Om3Srgt6ZwzdnES0XBgz9YrkgpRDb2E=; b=FKVyExp0UY8MX1eGUJiBDoFwFIWnySINgClgcNccPBC14jpmm7f1yHhsX8k9cWXgdn fURB7ullPjA0usaFNE53ZbdXc7QumoU3ab/fhoZGwowTRm9Ia0cbuvJWftwBHOEHHgUw 0cBDuyXLAVaWUaAneV/CeRBd/2EtP7x5K4ZOl1SETWuxvtJ/G4MDG8VieDy80WhVXx3s lcE315Rb3rEDzSdaV48Li7eWusOS3+WJqM6ZNDR1lqEFt9gtwPVjiBwNwk/Pu9A2UGnH h7wOSesR4ki16Ai/w+kejwKpU2W3PVYAsJXPkk9nqTqHFY56GHv9VI8uBYMtFL+SScPP nrGQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=dhyYZR5d; 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 d190-v6si34498360pfg.142.2018.05.29.15.22.00; Tue, 29 May 2018 15:22:14 -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=dhyYZR5d; 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 S968122AbeE2WT5 (ORCPT + 99 others); Tue, 29 May 2018 18:19:57 -0400 Received: from mail-pg0-f66.google.com ([74.125.83.66]:42820 "EHLO mail-pg0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S968192AbeE2WSN (ORCPT ); Tue, 29 May 2018 18:18:13 -0400 Received: by mail-pg0-f66.google.com with SMTP id p9-v6so7181177pgc.9 for ; Tue, 29 May 2018 15:18:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=aUAPrGEGg4q+Om3Srgt6ZwzdnES0XBgz9YrkgpRDb2E=; b=dhyYZR5dRhThEVYq2jAFPzLKxTFUb/1kyKJU3vrtjj0xSCgh4oj6/T5/nnFDbBhYX9 2ibphsJEwaUvtnhKh5pnz2AhftOS5Uh8cfysUlUEEXiuq2DX6CT6pf2e6y0hP14l2c0U vVAoGiSAMCihh/8Tbt3KKC2mvFoBMnKl8EqlgfkiXWeEqW0044a4+a2m7KvIZQU//62+ sOgyRXEdoG04I+IZUDQj3ac9V19TyXHv7oVzY0+XwfcTIW8XveswWCnpNQy8T+R1cem6 GY6x/YXdr9oqbtdz4+qhAO6RQuvR+yBAgIlFaJPCtdLxha3gXSqNhvTOPPtSe3tYljx8 X89g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=aUAPrGEGg4q+Om3Srgt6ZwzdnES0XBgz9YrkgpRDb2E=; b=l0KyT1vEzQvqMQgHA0LcR0GsnG06Umi6EldLBIhzFu/X438JwVHZyDudjzpO9I++xX 0KM9uSm032KDIg2rk7VX36WvmAwn0Q+/JFTbcEL+zJw5+Fnug905/0qCW1d/0J/jK0Y7 v3N/4F2sdbtXJhtH1BIQoZ9Qm1uTJdzXBiPLGqlbN8Jehm7Y3zLVepNsgitaPAPsVzkO IkOVlui4Su9z5Zn0pkMee9G7P2TJlVcVanh8QWS00TDZtwCGZcLdtOC22c1DPW/jBE8V wrtVnNeidzSktObGWag7ZGtBju4NxoIKXwUVCnmigxlWcMnfzsJRPdCdpCnqRrwg73lU 3lnw== X-Gm-Message-State: ALKqPwcWJ3wv93VK8N5o90JoJJfXfTnC7FTdoD17J3neHSfz5u7Wir02 0E04ntkmDjtk9iBWr8EKY3fsoA== X-Received: by 2002:a65:4a87:: with SMTP id b7-v6mr159890pgu.271.1527632291950; Tue, 29 May 2018 15:18:11 -0700 (PDT) Received: from skynet.sea.corp.google.com ([2620:15c:17:4:29de:3bb1:1270:e679]) by smtp.gmail.com with ESMTPSA id o84-v6sm78767935pfi.27.2018.05.29.15.18.11 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 29 May 2018 15:18:11 -0700 (PDT) From: Thomas Garnier To: kernel-hardening@lists.openwall.com Cc: Thomas Garnier , Thomas Gleixner , Ingo Molnar , "H. Peter Anvin" , x86@kernel.org, Josh Poimboeuf , Greg Kroah-Hartman , Philippe Ombredanne , Kate Stewart , Francis Deslauriers , "H.J. Lu" , linux-kernel@vger.kernel.org Subject: [PATCH v4 23/27] x86/modules: Adapt module loading for PIE support Date: Tue, 29 May 2018 15:15:24 -0700 Message-Id: <20180529221625.33541-24-thgarnie@google.com> X-Mailer: git-send-email 2.17.0.921.gf22659ad46-goog In-Reply-To: <20180529221625.33541-1-thgarnie@google.com> References: <20180529221625.33541-1-thgarnie@google.com> 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 277ffc57ae13..20bb6cbd8938 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 da3d98bb2bcb..89b3a95c8d11 100644 --- a/arch/x86/include/asm/sections.h +++ b/arch/x86/include/asm/sections.h @@ -17,4 +17,8 @@ extern char __entry_trampoline_start[], __entry_trampoline_end[]; extern char __start_got[], __end_got[]; #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.17.0.921.gf22659ad46-goog