From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C0164C32750 for ; Tue, 13 Aug 2019 21:03:38 +0000 (UTC) Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.kernel.org (Postfix) with ESMTP id 71DCA2070D for ; Tue, 13 Aug 2019 21:03:38 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 71DCA2070D Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=owner-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix) id 4A07F6B0279; Tue, 13 Aug 2019 17:03:06 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 451EA6B027A; Tue, 13 Aug 2019 17:03:06 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 2C9896B027C; Tue, 13 Aug 2019 17:03:06 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0028.hostedemail.com [216.40.44.28]) by kanga.kvack.org (Postfix) with ESMTP id 03EEC6B0279 for ; Tue, 13 Aug 2019 17:03:05 -0400 (EDT) Received: from smtpin22.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay04.hostedemail.com (Postfix) with SMTP id A548A6117 for ; Tue, 13 Aug 2019 21:03:05 +0000 (UTC) X-FDA: 75818629530.22.bone80_1aed653997b1b X-HE-Tag: bone80_1aed653997b1b X-Filterd-Recvd-Size: 11142 Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) by imf22.hostedemail.com (Postfix) with ESMTP for ; Tue, 13 Aug 2019 21:03:04 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 13 Aug 2019 14:03:04 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,382,1559545200"; d="scan'208";a="187901511" Received: from yyu32-desk1.sc.intel.com ([10.144.153.205]) by orsmga002.jf.intel.com with ESMTP; 13 Aug 2019 14:03:03 -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 , Borislav Petkov , Cyrill Gorcunov , Dave Hansen , Eugene Syromiatnikov , Florian Weimer , "H.J. Lu" , Jann Horn , Jonathan Corbet , Kees Cook , Mike Kravetz , Nadav Amit , Oleg Nesterov , Pavel Machek , Peter Zijlstra , Randy Dunlap , "Ravi V. Shankar" , Vedvyas Shanbhogue , Dave Martin Cc: Yu-cheng Yu Subject: [PATCH v8 22/27] binfmt_elf: Extract .note.gnu.property from an ELF file Date: Tue, 13 Aug 2019 13:52:20 -0700 Message-Id: <20190813205225.12032-23-yu-cheng.yu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190813205225.12032-1-yu-cheng.yu@intel.com> References: <20190813205225.12032-1-yu-cheng.yu@intel.com> X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: An ELF file's .note.gnu.property indicates features the executable file can support. For example, the property GNU_PROPERTY_X86_FEATURE_1_AND indicates the file supports GNU_PROPERTY_X86_FEATURE_1_IBT and/or GNU_PROPERTY_X86_FEATURE_1_SHSTK. With this patch, if an arch needs to setup features from ELF properties, it needs CONFIG_ARCH_USE_GNU_PROPERTY to be set, and specific arch_parse_property() and arch_setup_property(). For example, for X86_64: int arch_setup_property(void *ehdr, void *phdr, struct file *f, bool inter) { int r; uint32_t property; r = get_gnu_property(ehdr, phdr, f, GNU_PROPERTY_X86_FEATURE_1_AND, &property); ... } This patch is derived from code provided by H.J. Lu . Signed-off-by: Yu-cheng Yu --- fs/Kconfig.binfmt | 3 + fs/Makefile | 1 + fs/binfmt_elf.c | 20 +++++ fs/gnu_property.c | 178 +++++++++++++++++++++++++++++++++++++++ include/linux/elf.h | 11 +++ include/uapi/linux/elf.h | 14 +++ 6 files changed, 227 insertions(+) create mode 100644 fs/gnu_property.c diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index 62dc4f577ba1..d2cfe0729a73 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -36,6 +36,9 @@ config COMPAT_BINFMT_ELF config ARCH_BINFMT_ELF_STATE bool +config ARCH_USE_GNU_PROPERTY + bool + config BINFMT_ELF_FDPIC bool "Kernel support for FDPIC ELF binaries" default y if !BINFMT_ELF diff --git a/fs/Makefile b/fs/Makefile index d60089fd689b..939b1eb7e8cc 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o obj-$(CONFIG_COMPAT_BINFMT_ELF) += compat_binfmt_elf.o obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o +obj-$(CONFIG_ARCH_USE_GNU_PROPERTY) += gnu_property.o obj-$(CONFIG_FS_MBCACHE) += mbcache.o obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index d4e11b2e04f6..a4e87fcb10a8 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -852,6 +852,21 @@ static int load_elf_binary(struct linux_binprm *bprm) } } + if (interpreter) { + retval = arch_parse_property(&loc->interp_elf_ex, + interp_elf_phdata, + interpreter, true, + &arch_state); + } else { + retval = arch_parse_property(&loc->elf_ex, + elf_phdata, + bprm->file, false, + &arch_state); + } + + if (retval) + goto out_free_dentry; + /* * Allow arch code to reject the ELF at this point, whilst it's * still possible to return an error to the code that invoked @@ -1080,6 +1095,11 @@ static int load_elf_binary(struct linux_binprm *bprm) goto out_free_dentry; } + retval = arch_setup_property(&arch_state); + + if (retval < 0) + goto out_free_dentry; + if (interpreter) { unsigned long interp_map_addr = 0; diff --git a/fs/gnu_property.c b/fs/gnu_property.c new file mode 100644 index 000000000000..b22b43f4d6a0 --- /dev/null +++ b/fs/gnu_property.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Extract an ELF file's .note.gnu.property. + * + * The path from the ELF header to .note.gnu.property is: + * elfhdr->elf_phdr->elf_note. + * + * .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 (5) + * }; + * char n_name[4]; --> always 'GNU\0' + * + * struct { + * struct gnu_property { + * u32 pr_type; + * u32 pr_datasz; + * }; + * u8 pr_data[pr_datasz]; + * }[]; + */ + +#include +#include +#include +#include +#include + +/* + * Search a note's payload for 'pr_type'. + */ +static int check_note_payload(void *buf, unsigned long len, u32 pr_type, + u32 *property) +{ + u32 pr_type_max = 0; + + *property = 0; + + while (len > 0) { + struct gnu_property *pr = buf; + unsigned long pr_len; + + if (sizeof(*pr) > len) + return 0; + + pr_len = sizeof(*pr) + pr->pr_datasz; + + if (pr_len > len) + return -ENOEXEC; + + /* property types are in ascending order */ + if ((pr_type_max != 0) && (pr->pr_type > pr_type_max)) + return 0; + + if (pr->pr_type > pr_type) + return 0; + + if ((pr->pr_type == pr_type) && + (pr->pr_datasz >= sizeof(u32))) { + *property = *(u32 *)(buf + sizeof(*pr)); + return 0; + } + + if (pr->pr_type > pr_type_max) + pr_type_max = pr->pr_type; + + buf += pr_len; + len -= pr_len; + } + + return 0; +} + +/* + * Look at an ELF file's NT_GNU_PROPERTY for the property of pr_type. + * + * Input: + * buf: the buffer containing the whole note. + * len: size of buf. + * align: alignment of the note's payload. + * pr_type: the property type. + * + * Output: + * The property found. + * + * Return: + * Zero or error. + */ +static int check_note(void *buf, unsigned long len, int align, + u32 pr_type, u32 *property) +{ + struct elf_note *n = buf; + char *note_name = buf + sizeof(*n); + unsigned long payload_offset; + unsigned long payload_len; + + if (len < sizeof(*n) + 4) + return -ENOEXEC; + + if ((n->n_namesz != 4) || strncmp("GNU", note_name, 3)) + return -ENOEXEC; + + payload_offset = round_up(sizeof(*n) + n->n_namesz, align); + payload_len = n->n_descsz; + + if (payload_offset + payload_len > len) + return -ENOEXEC; + + buf += payload_offset; + len -= payload_offset; + + return check_note_payload(buf, len, pr_type, property); +} + +#define find_note(phdr, nr_phdrs, align, pos, len) { \ + int cnt; \ + \ + for (cnt = 0; cnt < nr_phdrs; cnt++) { \ + if ((phdr)[cnt].p_align != align) \ + continue; \ + if ((phdr)[cnt].p_type == PT_GNU_PROPERTY) { \ + pos = (phdr)[cnt].p_offset; \ + len = (phdr)[cnt].p_filesz; \ + } \ + } \ +} + +int get_gnu_property(void *ehdr, void *phdr, struct file *file, + u32 pr_type, u32 *property) +{ + Elf64_Ehdr *ehdr64 = ehdr; + Elf32_Ehdr *ehdr32 = ehdr; + void *buf; + int align; + loff_t pos = 0; + unsigned long len = 0; + int err = 0; + + /* + * Find PT_GNU_PROPERTY from ELF program headers. + */ + if (ehdr64->e_ident[EI_CLASS] == ELFCLASS64) { + align = 8; + find_note((Elf64_Phdr *)phdr, ehdr64->e_phnum, align, pos, len); + } else if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) { + align = 4; + find_note((Elf32_Phdr *)phdr, ehdr32->e_phnum, align, pos, len); + } + + /* + * Read in the whole note. PT_GNU_PROPERTY + * is not expected to be larger than a page. + */ + if (len == 0) + return 0; + + if (len > PAGE_SIZE) + return -ENOEXEC; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = kernel_read(file, buf, len, &pos); + if (err < len) { + if (err >= 0) + err = -EIO; + goto out; + } + + err = check_note(buf, len, align, pr_type, property); +out: + kfree(buf); + return err; +} diff --git a/include/linux/elf.h b/include/linux/elf.h index e3649b3e970e..c86cbfd17382 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -56,4 +56,15 @@ static inline int elf_coredump_extra_notes_write(struct coredump_params *cprm) { extern int elf_coredump_extra_notes_size(void); extern int elf_coredump_extra_notes_write(struct coredump_params *cprm); #endif + +#ifdef CONFIG_ARCH_USE_GNU_PROPERTY +extern int arch_parse_property(void *ehdr, void *phdr, struct file *f, + bool inter, struct arch_elf_state *state); +extern int arch_setup_property(struct arch_elf_state *state); +extern int get_gnu_property(void *ehdr_p, void *phdr_p, struct file *f, + u32 pr_type, u32 *feature); +#else +#define arch_parse_property(ehdr, phdr, file, inter, state) (0) +#define arch_setup_property(state) (0) +#endif #endif /* _LINUX_ELF_H */ diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index 34c02e4290fe..530ce08467c2 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -36,6 +36,7 @@ typedef __s64 Elf64_Sxword; #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7fffffff #define PT_GNU_EH_FRAME 0x6474e550 +#define PT_GNU_PROPERTY 0x6474e553 #define PT_GNU_STACK (PT_LOOS + 0x474e551) @@ -443,4 +444,17 @@ typedef struct elf64_note { Elf64_Word n_type; /* Content type */ } Elf64_Nhdr; +/* NT_GNU_PROPERTY_TYPE_0 header */ +struct gnu_property { + __u32 pr_type; + __u32 pr_datasz; +}; + +/* .note.gnu.property types */ +#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002 + +/* Bits of GNU_PROPERTY_X86_FEATURE_1_AND */ +#define GNU_PROPERTY_X86_FEATURE_1_IBT 0x00000001 +#define GNU_PROPERTY_X86_FEATURE_1_SHSTK 0x00000002 + #endif /* _UAPI_LINUX_ELF_H */ -- 2.17.1