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 <hjl.tools@xxxxxxxxx>. Signed-off-by: Yu-cheng Yu <yu-cheng.yu@xxxxxxxxx> --- 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 <linux/elf.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/compat.h> + +/* + * 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