Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp2211432imm; Tue, 10 Jul 2018 15:33:22 -0700 (PDT) X-Google-Smtp-Source: AAOMgpeJLew0LOgjSYP8Ul02aTQiHvvAjz8WuwUMpZyvDAJHSVFiGaIUru4PlZNdAR3NSKuGvHL8 X-Received: by 2002:a62:bd4:: with SMTP id 81-v6mr20856854pfl.67.1531262002627; Tue, 10 Jul 2018 15:33:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1531262002; cv=none; d=google.com; s=arc-20160816; b=g9aLhXy4r/ey3VTLWcexkoALdb71ipYWvz3QwhqZWWs2pWQ3fVl5l6dZwiCFPIDF89 sTshUg43SB7nvkpAgogN+hu9v6hueJdG7NFGM94mHCpu49/0PJMZ3pQKs7Tv50zt3qCn 1L0GxNVwpVR4mCxWyh+qs88q2jwjG8a1PuQseg2npPPTgeGDTfwSRr+wVB0iJA4VasT2 EUmsuRYvSolcOW+/yTgwHoNAJfVON5xWmBroPdb5TGF3WAFXcZsuHAyBP95NfXAOyWPR YjutWip+sj1viDpG91duAhFQQtUAqYIhsIcJ08v+ANN/riwawmQ3G9HtqIpQD6mdWcVS tECw== 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:arc-authentication-results; bh=zqkcz78OctYIT5r2DdO4F9JFWHurMroQqrzAzwvSsuo=; b=i/tkNqcjw6DT/QIoSGnBmQ6chf7+70MBSehESWe7m2yHKgH9Fjrrn0fq2JTZ8iOoI7 7vDCcIPd+GrSo3SCMh2CZrBT0dzACMhK/cUc/4gyM9uQTL5a2M2YRROl4NwxjmQ47XFT 3uS4jZzL95P5OD/kOQ53g6Gn97J69QP6dfhE1RpXlznZyoTAbyJJsrJQNo4oJfozuyev udVTSt6rQvWu1U6t7ndnryNP0REcmOX6MmCij3BKjLdVLwqcdwSBK3MJ3POP0RdcN4Hj 4h81I099u255pQRkFDtliZaXyTnRay5/yWh5QqnhEz8pzkIxV6HLnwmRkTqxaRfFp4WS 7a9Q== ARC-Authentication-Results: i=1; mx.google.com; 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=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id w20-v6si3037793ply.137.2018.07.10.15.33.07; Tue, 10 Jul 2018 15:33:22 -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; 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=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1732635AbeGJWdP (ORCPT + 99 others); Tue, 10 Jul 2018 18:33:15 -0400 Received: from mga11.intel.com ([192.55.52.93]:41890 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732489AbeGJWc1 (ORCPT ); Tue, 10 Jul 2018 18:32:27 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga004.fm.intel.com ([10.253.24.48]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 10 Jul 2018 15:31:16 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.51,335,1526367600"; d="scan'208";a="70305440" Received: from 2b52.sc.intel.com ([143.183.136.52]) by fmsmga004.fm.intel.com with ESMTP; 10 Jul 2018 15:31:15 -0700 From: Yu-cheng Yu To: x86@kernel.org, "H. Peter Anvin" , Thomas Gleixner , Ingo Molnar , linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-api@vger.kernel.org, Arnd Bergmann , Andy Lutomirski , Balbir Singh , Cyrill Gorcunov , Dave Hansen , Florian Weimer , "H.J. Lu" , Jann Horn , Jonathan Corbet , Kees Cook , Mike Kravetz , Nadav Amit , Oleg Nesterov , Pavel Machek , Peter Zijlstra , "Ravi V. Shankar" , Vedvyas Shanbhogue Cc: Yu-cheng Yu Subject: [RFC PATCH v2 20/27] x86/cet/shstk: ELF header parsing of CET Date: Tue, 10 Jul 2018 15:26:32 -0700 Message-Id: <20180710222639.8241-21-yu-cheng.yu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180710222639.8241-1-yu-cheng.yu@intel.com> References: <20180710222639.8241-1-yu-cheng.yu@intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Look in .note.gnu.property of an ELF file and check if shadow stack needs to be enabled for the task. Signed-off-by: H.J. Lu Signed-off-by: Yu-cheng Yu --- arch/x86/Kconfig | 4 + arch/x86/include/asm/elf.h | 5 + arch/x86/include/uapi/asm/elf_property.h | 16 ++ arch/x86/kernel/Makefile | 2 + arch/x86/kernel/elf.c | 262 +++++++++++++++++++++++ fs/binfmt_elf.c | 16 ++ include/uapi/linux/elf.h | 1 + 7 files changed, 306 insertions(+) create mode 100644 arch/x86/include/uapi/asm/elf_property.h create mode 100644 arch/x86/kernel/elf.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 44af5e1aaa4a..768343768643 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1923,12 +1923,16 @@ config X86_INTEL_CET config ARCH_HAS_SHSTK def_bool n +config ARCH_HAS_PROGRAM_PROPERTIES + def_bool n + config X86_INTEL_SHADOW_STACK_USER prompt "Intel Shadow Stack for user-mode" def_bool n depends on CPU_SUP_INTEL && X86_64 select X86_INTEL_CET select ARCH_HAS_SHSTK + select ARCH_HAS_PROGRAM_PROPERTIES ---help--- Shadow stack provides hardware protection against program stack corruption. Only when all the following are true will an application diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index 0d157d2a1e2a..5b5f169c5c07 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -382,4 +382,9 @@ struct va_alignment { extern struct va_alignment va_align; extern unsigned long align_vdso_addr(unsigned long); + +#ifdef CONFIG_ARCH_HAS_PROGRAM_PROPERTIES +extern int arch_setup_features(void *ehdr, void *phdr, struct file *file, + bool interp); +#endif #endif /* _ASM_X86_ELF_H */ diff --git a/arch/x86/include/uapi/asm/elf_property.h b/arch/x86/include/uapi/asm/elf_property.h new file mode 100644 index 000000000000..343a871b8fc1 --- /dev/null +++ b/arch/x86/include/uapi/asm/elf_property.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _UAPI_ASM_X86_ELF_PROPERTY_H +#define _UAPI_ASM_X86_ELF_PROPERTY_H + +/* + * pr_type + */ +#define GNU_PROPERTY_X86_FEATURE_1_AND (0xc0000002) + +/* + * Bits for GNU_PROPERTY_X86_FEATURE_1_AND + */ +#define GNU_PROPERTY_X86_FEATURE_1_SHSTK (0x00000002) +#define GNU_PROPERTY_X86_FEATURE_1_IBT (0x00000001) + +#endif /* _UAPI_ASM_X86_ELF_PROPERTY_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index fbb2d91fb756..36b14ef410c8 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -141,6 +141,8 @@ obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o obj-$(CONFIG_X86_INTEL_CET) += cet.o +obj-$(CONFIG_ARCH_HAS_PROGRAM_PROPERTIES) += elf.o + ### # 64 bit specific files ifeq ($(CONFIG_X86_64),y) diff --git a/arch/x86/kernel/elf.c b/arch/x86/kernel/elf.c new file mode 100644 index 000000000000..233f6dad9c1f --- /dev/null +++ b/arch/x86/kernel/elf.c @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Look at an ELF file's .note.gnu.property and determine if the file + * supports shadow stack and/or indirect branch tracking. + * The path from the ELF header to the note section is the following: + * elfhdr->elf_phdr->elf_note->property[]. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The .note.gnu.property layout: + * + * struct elf_note { + * u32 n_namesz; --> sizeof(n_name[]); always (4) + * u32 n_ndescsz;--> sizeof(property[]) + * u32 n_type; --> always NT_GNU_PROPERTY_TYPE_0 + * }; + * + * char n_name[4]; --> always 'GNU\0' + * + * struct { + * u32 pr_type; + * u32 pr_datasz;--> sizeof(pr_data[]) + * u8 pr_data[pr_datasz]; + * } property[]; + */ + +#define ELF_NOTE_DESC_OFFSET(n, align) \ + round_up(sizeof(*n) + n->n_namesz, (align)) + +#define ELF_NOTE_NEXT_OFFSET(n, align) \ + round_up(ELF_NOTE_DESC_OFFSET(n, align) + n->n_descsz, (align)) + +#define NOTE_PROPERTY_TYPE_0(n) \ + ((n->n_namesz == 4) && (memcmp(n + 1, "GNU", 4) == 0) && \ + (n->n_type == NT_GNU_PROPERTY_TYPE_0)) + +#define NOTE_SIZE_BAD(n, align, max) \ + ((n->n_descsz < 8) || ((n->n_descsz % align) != 0) || \ + (((u8 *)(n + 1) + 4 + n->n_descsz) > (max))) + +/* + * Go through the property array and look for the one + * with pr_type of GNU_PROPERTY_X86_FEATURE_1_AND. + */ +static u32 find_x86_feature_1(u8 *buf, u32 size, u32 align) +{ + u8 *end = buf + size; + u8 *ptr = buf; + + while (1) { + u32 pr_type, pr_datasz; + + if ((ptr + 4) >= end) + break; + + pr_type = *(u32 *)ptr; + pr_datasz = *(u32 *)(ptr + 4); + ptr += 8; + + if ((ptr + pr_datasz) >= end) + break; + + if (pr_type == GNU_PROPERTY_X86_FEATURE_1_AND && + pr_datasz == 4) + return *(u32 *)ptr; + + ptr += pr_datasz; + } + return 0; +} + +static int find_cet(u8 *buf, u32 size, u32 align, int *shstk, int *ibt) +{ + struct elf_note *note = (struct elf_note *)buf; + *shstk = 0; + *ibt = 0; + + /* + * Go through the note section and find the note + * with n_type of NT_GNU_PROPERTY_TYPE_0. + */ + while ((unsigned long)(note + 1) - (unsigned long)buf < size) { + if (NOTE_PROPERTY_TYPE_0(note)) { + u32 p; + + if (NOTE_SIZE_BAD(note, align, buf + size)) + return 0; + + /* + * Found the note; look at its property array. + */ + p = find_x86_feature_1((u8 *)(note + 1) + 4, + note->n_descsz, align); + + if (p & GNU_PROPERTY_X86_FEATURE_1_SHSTK) + *shstk = 1; + if (p & GNU_PROPERTY_X86_FEATURE_1_IBT) + *ibt = 1; + return 1; + } + + /* + * Note sections like .note.ABI-tag and .note.gnu.build-id + * are aligned to 4 bytes in 64-bit ELF objects. So always + * use phdr->p_align. + */ + note = (void *)note + ELF_NOTE_NEXT_OFFSET(note, align); + } + + return 0; +} + +static int check_pt_note_segment(struct file *file, + unsigned long note_size, loff_t *pos, + u32 align, int *shstk, int *ibt) +{ + int retval; + char *note_buf; + + /* + * PT_NOTE segment is small. Read at most + * PAGE_SIZE. + */ + if (note_size > PAGE_SIZE) + note_size = PAGE_SIZE; + + /* + * Try to read in the whole PT_NOTE segment. + */ + note_buf = kmalloc(note_size, GFP_KERNEL); + if (!note_buf) + return -ENOMEM; + retval = kernel_read(file, note_buf, note_size, pos); + if (retval != note_size) { + kfree(note_buf); + return (retval < 0) ? retval : -EIO; + } + + retval = find_cet(note_buf, note_size, align, shstk, ibt); + kfree(note_buf); + return retval; +} + +#ifdef CONFIG_COMPAT +static int check_pt_note_32(struct file *file, struct elf32_phdr *phdr, + int phnum, int *shstk, int *ibt) +{ + int i; + int found = 0; + + /* + * Go through all PT_NOTE segments and find NT_GNU_PROPERTY_TYPE_0. + */ + for (i = 0; i < phnum; i++, phdr++) { + loff_t pos; + + /* + * NT_GNU_PROPERTY_TYPE_0 note is aligned to 4 bytes + * in 32-bit binaries. + */ + if ((phdr->p_type != PT_NOTE) || (phdr->p_align != 4)) + continue; + + pos = phdr->p_offset; + found = check_pt_note_segment(file, phdr->p_filesz, + &pos, phdr->p_align, + shstk, ibt); + if (found) + break; + } + return found; +} +#endif + +#ifdef CONFIG_X86_64 +static int check_pt_note_64(struct file *file, struct elf64_phdr *phdr, + int phnum, int *shstk, int *ibt) +{ + int found = 0; + + /* + * Go through all PT_NOTE segments. + */ + for (; phnum > 0; phnum--, phdr++) { + loff_t pos; + + /* + * NT_GNU_PROPERTY_TYPE_0 note is aligned to 8 bytes + * in 64-bit binaries. + */ + if ((phdr->p_type != PT_NOTE) || (phdr->p_align != 8)) + continue; + + pos = phdr->p_offset; + found = check_pt_note_segment(file, phdr->p_filesz, + &pos, phdr->p_align, + shstk, ibt); + + if (found) + break; + } + return found; +} +#endif + +int arch_setup_features(void *ehdr_p, void *phdr_p, + struct file *file, bool interp) +{ + int err = 0; + int shstk = 0; + int ibt = 0; + + struct elf64_hdr *ehdr64 = ehdr_p; + + if (!cpu_feature_enabled(X86_FEATURE_SHSTK)) + return 0; + + if (ehdr64->e_ident[EI_CLASS] == ELFCLASS64) { + struct elf64_phdr *phdr64 = phdr_p; + + err = check_pt_note_64(file, phdr64, ehdr64->e_phnum, + &shstk, &ibt); + if (err < 0) + goto out; + } else { +#ifdef CONFIG_COMPAT + struct elf32_hdr *ehdr32 = ehdr_p; + + if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) { + struct elf32_phdr *phdr32 = phdr_p; + + err = check_pt_note_32(file, phdr32, ehdr32->e_phnum, + &shstk, &ibt); + if (err < 0) + goto out; + } +#endif + } + + current->thread.cet.shstk_enabled = 0; + current->thread.cet.shstk_base = 0; + current->thread.cet.shstk_size = 0; + if (cpu_feature_enabled(X86_FEATURE_SHSTK)) { + if (shstk) { + err = cet_setup_shstk(); + if (err < 0) + goto out; + } + } +out: + return err; +} diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 0ac456b52bdd..3395f6a631d5 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1081,6 +1081,22 @@ static int load_elf_binary(struct linux_binprm *bprm) goto out_free_dentry; } +#ifdef CONFIG_ARCH_HAS_PROGRAM_PROPERTIES + + if (interpreter) { + retval = arch_setup_features(&loc->interp_elf_ex, + interp_elf_phdata, + interpreter, true); + } else { + retval = arch_setup_features(&loc->elf_ex, + elf_phdata, + bprm->file, false); + } + + if (retval < 0) + goto out_free_dentry; +#endif + if (elf_interpreter) { unsigned long interp_map_addr = 0; diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index 4e12c423b9fe..dc93982b9664 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -372,6 +372,7 @@ typedef struct elf64_shdr { #define NT_PRFPREG 2 #define NT_PRPSINFO 3 #define NT_TASKSTRUCT 4 +#define NT_GNU_PROPERTY_TYPE_0 5 #define NT_AUXV 6 /* * Note to userspace developers: size of NT_SIGINFO note may increase -- 2.17.1