Received: by 2002:ac0:a5a6:0:0:0:0:0 with SMTP id m35-v6csp38716imm; Thu, 30 Aug 2018 07:50:17 -0700 (PDT) X-Google-Smtp-Source: ANB0VdadqZzTLKueZm8oJDmT9kt+JY5QQw3j3s7cuQvgun1+xIfN3LHogltZawlD4b+u0NdP2Qk6 X-Received: by 2002:a63:c14a:: with SMTP id p10-v6mr10058678pgi.305.1535640617621; Thu, 30 Aug 2018 07:50:17 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1535640617; cv=none; d=google.com; s=arc-20160816; b=Fo/w02YimdrI9aoClbFpMjGKxh+waf5GNf0bXTrBfbLQgIEpl20HX8uJxrZOlxMwfM 46iHF8tC6jidxWpHjR6y7c4XUbhvYrVQHZY1BT71a+AUL1wqXlMfVNYFnqI06bYutr15 6UYwtUDUfEsXQ/a3Rd8auG/714iMKAtu0T+Sv+uxV5Otv9scJwBW5qs8XU0Btd9K64aa RM9T3Cg6QYgfeBiFJnWD98zbQcybTRDuKlrPiAl/LK78P848S0nQTxLk6x3Qg8kaahD4 3yC09dSpN48duATP7Xe8xaprTCIQCQAgTt+F9KQJcp7rHo1298sb6RBIE8N9tIluAwKR 3Q1A== 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=ZhdA6V38hlV1nNmtuavCJWD7maHUnpgc48DAv1CMvY4=; b=dO3lL8GBz2wDJlwWDfiQP2l/dtoV9rWGHLuQectWLt4jD4j4nu3DCtyLt03risZIAe k8tOsKsWqR3oIAWPi/du3kNnUFWImJdLm+Rg/+o5DA6JUFH55o34tdeNOJIr1LItEzwH iwd5k1jpdbxUFpN0bEVVLnXcWG6vd6E+ee09U422ajhCEhrAIsvU6RKfOAFQp13XgBR7 t2weSqLGe9seTkMQa9iAxHqhs1LeGdAnRokLIr3QtFaXDHwYAome8zlXOn2NZNvvn2We 5zZfl1DeRVC3ejpWMOGWOWeuZWPWJyv7NKzBgqn1VHbuTRTMWNulOlsUNzadIgKQ+B0Q Rgog== 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 x25-v6si7064548pfi.138.2018.08.30.07.50.02; Thu, 30 Aug 2018 07:50:17 -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 S1729486AbeH3SqP (ORCPT + 99 others); Thu, 30 Aug 2018 14:46:15 -0400 Received: from mga17.intel.com ([192.55.52.151]:62771 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729172AbeH3SqP (ORCPT ); Thu, 30 Aug 2018 14:46:15 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 30 Aug 2018 07:43:44 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.53,307,1531810800"; d="scan'208";a="67186730" Received: from 2b52.sc.intel.com ([143.183.136.52]) by fmsmga008.fm.intel.com with ESMTP; 30 Aug 2018 07:43:42 -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 v3 21/24] x86/cet/shstk: ELF header parsing of Shadow Stack Date: Thu, 30 Aug 2018 07:39:01 -0700 Message-Id: <20180830143904.3168-22-yu-cheng.yu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180830143904.3168-1-yu-cheng.yu@intel.com> References: <20180830143904.3168-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 | 15 + arch/x86/kernel/Makefile | 2 + arch/x86/kernel/elf.c | 338 +++++++++++++++++++++++ fs/binfmt_elf.c | 15 + include/uapi/linux/elf.h | 1 + 7 files changed, 380 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 017b3ba70807..2cfe11e1cf7f 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1919,12 +1919,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..af361207718c --- /dev/null +++ b/arch/x86/include/uapi/asm/elf_property.h @@ -0,0 +1,15 @@ +/* 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) + +#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..a2c41bf39c58 --- /dev/null +++ b/arch/x86/kernel/elf.c @@ -0,0 +1,338 @@ +/* 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 +#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 { + * struct property_x86 { + * u32 pr_type; + * u32 pr_datasz; + * }; + * u8 pr_data[pr_datasz]; + * }[]; + */ + +#define BUF_SIZE (PAGE_SIZE / 4) + +struct property_x86 { + u32 pr_type; + u32 pr_datasz; +}; + +typedef bool (test_fn)(void *buf, u32 *arg); +typedef void *(next_fn)(void *buf, u32 *arg); + +static inline bool test_note_type_0(void *buf, u32 *arg) +{ + struct elf_note *n = buf; + + return ((n->n_namesz == 4) && (memcmp(n + 1, "GNU", 4) == 0) && + (n->n_type == NT_GNU_PROPERTY_TYPE_0)); +} + +static inline void *next_note(void *buf, u32 *arg) +{ + struct elf_note *n = buf; + u32 align = *arg; + int size; + + size = round_up(sizeof(*n) + n->n_namesz, align); + size = round_up(size + n->n_descsz, align); + + if (buf + size < buf) + return NULL; + else + return (buf + size); +} + +static inline bool test_property_x86(void *buf, u32 *arg) +{ + struct property_x86 *pr = buf; + u32 max_type = *arg; + + if (pr->pr_type > max_type) + *arg = pr->pr_type; + + return (pr->pr_type == GNU_PROPERTY_X86_FEATURE_1_AND); +} + +static inline void *next_property(void *buf, u32 *arg) +{ + struct property_x86 *pr = buf; + u32 max_type = *arg; + + if ((buf + sizeof(*pr) + pr->pr_datasz < buf) || + (pr->pr_type > GNU_PROPERTY_X86_FEATURE_1_AND) || + (pr->pr_type > max_type)) + return NULL; + else + return (buf + sizeof(*pr) + pr->pr_datasz); +} + +/* + * Scan 'buf' for a pattern; return true if found. + * *pos is the distance from the beginning of buf to where + * the searched item or the next item is located. + */ +static int scan(u8 *buf, u32 buf_size, int item_size, + test_fn test, next_fn next, u32 *arg, u32 *pos) +{ + int found = 0; + u8 *p, *max; + + max = buf + buf_size; + if (max < buf) + return 0; + + p = buf; + + while ((p + item_size < max) && (p + item_size > buf)) { + if (test(p, arg)) { + found = 1; + break; + } + + p = next(p, arg); + } + + *pos = (p + item_size <= buf) ? 0 : (u32)(p - buf); + return found; +} + +/* + * Search a NT_GNU_PROPERTY_TYPE_0 for GNU_PROPERTY_X86_FEATURE_1_AND. + */ +static int find_feature_x86(struct file *file, unsigned long desc_size, + loff_t file_offset, u8 *buf, u32 *feature) +{ + u32 buf_pos; + unsigned long read_size; + unsigned long done; + int found = 0; + int ret = 0; + u32 last_pr = 0; + + *feature = 0; + buf_pos = 0; + + for (done = 0; done < desc_size; done += buf_pos) { + read_size = desc_size - done; + if (read_size > BUF_SIZE) + read_size = BUF_SIZE; + + ret = kernel_read(file, buf, read_size, &file_offset); + + if (ret != read_size) + return (ret < 0) ? ret : -EIO; + + found = scan(buf, read_size, sizeof(struct property_x86), + test_property_x86, next_property, + &last_pr, &buf_pos); + + if ((!buf_pos) || found) + break; + + file_offset += buf_pos - read_size; + } + + if (found) { + struct property_x86 *pr = + (struct property_x86 *)(buf + buf_pos); + + if (pr->pr_datasz == 4) { + u32 *max = (u32 *)(buf + read_size); + u32 *data = (u32 *)((u8 *)pr + sizeof(*pr)); + + if (data + 1 <= max) { + *feature = *data; + } else { + file_offset += buf_pos - read_size; + file_offset += sizeof(*pr); + ret = kernel_read(file, feature, 4, + &file_offset); + } + } + } + + return ret; +} + +/* + * Search a PT_NOTE segment for the first NT_GNU_PROPERTY_TYPE_0. + */ +static int find_note_type_0(struct file *file, unsigned long note_size, + loff_t file_offset, u32 align, u32 *feature) +{ + u8 *buf; + u32 buf_pos; + unsigned long read_size; + unsigned long done; + int found = 0; + int ret = 0; + + buf = kmalloc(BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + *feature = 0; + buf_pos = 0; + + for (done = 0; done < note_size; done += buf_pos) { + read_size = note_size - done; + if (read_size > BUF_SIZE) + read_size = BUF_SIZE; + + ret = kernel_read(file, buf, read_size, &file_offset); + + if (ret != read_size) { + ret = (ret < 0) ? ret : -EIO; + kfree(buf); + return ret; + } + + /* + * item_size = sizeof(struct elf_note) + elf_note.n_namesz. + * n_namesz is 4 for the note type we look for. + */ + found += scan(buf, read_size, sizeof(struct elf_note) + 4, + test_note_type_0, next_note, + &align, &buf_pos); + + file_offset += buf_pos - read_size; + + if (found == 1) { + struct elf_note *n = + (struct elf_note *)(buf + buf_pos); + u32 start = round_up(sizeof(*n) + n->n_namesz, align); + u32 total = round_up(start + n->n_descsz, align); + + ret = find_feature_x86(file, n->n_descsz, + file_offset + start, + buf, feature); + file_offset += total; + buf_pos += total; + } else if (!buf_pos) { + *feature = 0; + break; + } + } + + kfree(buf); + return ret; +} + +#ifdef CONFIG_COMPAT +static int check_notes_32(struct file *file, struct elf32_phdr *phdr, + int phnum, u32 *feature) +{ + int i; + int err = 0; + + for (i = 0; i < phnum; i++, phdr++) { + if ((phdr->p_type != PT_NOTE) || (phdr->p_align != 4)) + continue; + + err = find_note_type_0(file, phdr->p_filesz, phdr->p_offset, + phdr->p_align, feature); + if (err) + return err; + } + + return 0; +} +#endif + +#ifdef CONFIG_X86_64 +static int check_notes_64(struct file *file, struct elf64_phdr *phdr, + int phnum, u32 *feature) +{ + int i; + int err = 0; + + for (i = 0; i < phnum; i++, phdr++) { + if ((phdr->p_type != PT_NOTE) || (phdr->p_align != 8)) + continue; + + err = find_note_type_0(file, phdr->p_filesz, phdr->p_offset, + phdr->p_align, feature); + if (err) + return err; + } + + return 0; +} +#endif + +int arch_setup_features(void *ehdr_p, void *phdr_p, + struct file *file, bool interp) +{ + int err = 0; + u32 feature = 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_notes_64(file, phdr64, ehdr64->e_phnum, + &feature); + 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_notes_32(file, phdr32, ehdr32->e_phnum, + &feature); + if (err < 0) + goto out; + } +#endif + } + + memset(¤t->thread.cet, 0, sizeof(struct cet_status)); + + if (cpu_feature_enabled(X86_FEATURE_SHSTK)) { + if (feature & GNU_PROPERTY_X86_FEATURE_1_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 efae2fb0930a..b891aa292b46 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1081,6 +1081,21 @@ 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 c5358e0ae7c5..5ef25a565e88 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