An ELF file's .note.gnu.property indicates arch features supported by the
file.  These features are extracted by arch_parse_elf_property() and stored
in 'arch_elf_state'.  Introduce arch_setup_elf_property() for enabling such
features.  The first use-case of this function is shadow stack.

ARM64 is the other arch that has ARCH_USER_GNU_PROPERTY and arch_parse_elf_
property().  Add arch_setup_elf_property() for it.

Signed-off-by: Yu-cheng Yu <yu-cheng...@intel.com>
Cc: Mark Brown <broo...@kernel.org>
Cc: Catalin Marinas <catalin.mari...@arm.com>
Cc: Dave Martin <dave.mar...@arm.com>
---
 arch/arm64/include/asm/elf.h |  5 +++++
 arch/x86/Kconfig             |  2 ++
 arch/x86/include/asm/elf.h   | 13 +++++++++++++
 arch/x86/kernel/process_64.c | 32 ++++++++++++++++++++++++++++++++
 fs/binfmt_elf.c              |  4 ++++
 include/linux/elf.h          |  6 ++++++
 6 files changed, 62 insertions(+)

diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index 8d1c8dcb87fd..d37bc7915935 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -281,6 +281,11 @@ static inline int arch_parse_elf_property(u32 type, const 
void *data,
        return 0;
 }
 
+static inline int arch_setup_elf_property(struct arch_elf_state *arch)
+{
+       return 0;
+}
+
 static inline int arch_elf_pt_proc(void *ehdr, void *phdr,
                                   struct file *f, bool is_interp,
                                   struct arch_elf_state *state)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 7578327226e3..4b28a0ce4594 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1950,6 +1950,8 @@ config X86_SHADOW_STACK_USER
        select X86_CET
        select ARCH_MAYBE_MKWRITE
        select ARCH_HAS_SHADOW_STACK
+       select ARCH_USE_GNU_PROPERTY
+       select ARCH_BINFMT_ELF_STATE
        help
          Shadow Stacks provides protection against program stack
          corruption.  It's a hardware feature.  This only matters
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index b9a5d488f1a5..0e1be2a13359 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -385,6 +385,19 @@ extern int compat_arch_setup_additional_pages(struct 
linux_binprm *bprm,
                                              int uses_interp);
 #define compat_arch_setup_additional_pages compat_arch_setup_additional_pages
 
+#ifdef CONFIG_ARCH_BINFMT_ELF_STATE
+struct arch_elf_state {
+       unsigned int gnu_property;
+};
+
+#define INIT_ARCH_ELF_STATE {  \
+       .gnu_property = 0,      \
+}
+
+#define arch_elf_pt_proc(ehdr, phdr, elf, interp, state) (0)
+#define arch_check_elf(ehdr, interp, interp_ehdr, state) (0)
+#endif
+
 /* Do not change the values. See get_align_mask() */
 enum align_flags {
        ALIGN_VA_32     = BIT(0),
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 9afefe325acb..8725e67bcd44 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -837,3 +837,35 @@ unsigned long KSTK_ESP(struct task_struct *task)
 {
        return task_pt_regs(task)->sp;
 }
+
+#ifdef CONFIG_ARCH_USE_GNU_PROPERTY
+int arch_parse_elf_property(u32 type, const void *data, size_t datasz,
+                            bool compat, struct arch_elf_state *state)
+{
+       if (type != GNU_PROPERTY_X86_FEATURE_1_AND)
+               return 0;
+
+       if (datasz != sizeof(unsigned int))
+               return -ENOEXEC;
+
+       state->gnu_property = *(unsigned int *)data;
+       return 0;
+}
+
+int arch_setup_elf_property(struct arch_elf_state *state)
+{
+       int r = 0;
+
+       if (!IS_ENABLED(CONFIG_X86_CET))
+               return r;
+
+       memset(&current->thread.cet, 0, sizeof(struct cet_status));
+
+       if (static_cpu_has(X86_FEATURE_SHSTK)) {
+               if (state->gnu_property & GNU_PROPERTY_X86_FEATURE_1_SHSTK)
+                       r = cet_setup_shstk();
+       }
+
+       return r;
+}
+#endif
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 13d053982dd7..2b4cfc256895 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1217,6 +1217,10 @@ static int load_elf_binary(struct linux_binprm *bprm)
 
        set_binfmt(&elf_format);
 
+       retval = arch_setup_elf_property(&arch_state);
+       if (retval < 0)
+               goto out;
+
 #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
        retval = arch_setup_additional_pages(bprm, !!interpreter);
        if (retval < 0)
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 5d5b0321da0b..4827695ca415 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -82,9 +82,15 @@ static inline int arch_parse_elf_property(u32 type, const 
void *data,
 {
        return 0;
 }
+
+static inline int arch_setup_elf_property(struct arch_elf_state *arch)
+{
+       return 0;
+}
 #else
 extern int arch_parse_elf_property(u32 type, const void *data, size_t datasz,
                                   bool compat, struct arch_elf_state *arch);
+extern int arch_setup_elf_property(struct arch_elf_state *arch);
 #endif
 
 #ifdef CONFIG_ARCH_HAVE_ELF_PROT
-- 
2.21.0

Reply via email to