[v1,RFC,Zisslpcfi,08/20] riscv: ELF header parsing in GNU property for riscv zisslpcfi

Message ID 20230213045351.3945824-9-debug@rivosinc.com
State New
Headers
Series riscv control-flow integrity for U mode |

Commit Message

Deepak Gupta Feb. 13, 2023, 4:53 a.m. UTC
  Binaries enabled for Zisslpcfi will have new instructions that may fault
on risc-v cpus which dont implement Zimops or Zicfi. This change adds

 - support for parsing new backward and forward cfi flags in
   PT_GNU_PROPERTY
 - setting cfi state on recognizing cfi flags in ELF
 - enable back cfi and forward cfi in sstatus

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
---
 arch/riscv/include/asm/elf.h | 54 +++++++++++++++++++++++++++++
 arch/riscv/kernel/process.c  | 67 ++++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+)
  

Patch

diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h
index e7acffdf21d2..60ac2d2390ee 100644
--- a/arch/riscv/include/asm/elf.h
+++ b/arch/riscv/include/asm/elf.h
@@ -14,6 +14,7 @@ 
 #include <asm/auxvec.h>
 #include <asm/byteorder.h>
 #include <asm/cacheinfo.h>
+#include <linux/processor.h>
 
 /*
  * These are used to set parameters in the core dumps.
@@ -140,4 +141,57 @@  extern int compat_arch_setup_additional_pages(struct linux_binprm *bprm,
 				compat_arch_setup_additional_pages
 
 #endif /* CONFIG_COMPAT */
+
+#define RISCV_ELF_FCFI (1 << 0)
+#define RISCV_ELF_BCFI (1 << 1)
+
+#ifdef CONFIG_ARCH_BINFMT_ELF_STATE
+struct arch_elf_state {
+	int flags;
+};
+
+#define INIT_ARCH_ELF_STATE {                  \
+	.flags = 0,                             \
+}
+#endif
+
+#ifdef CONFIG_ARCH_USE_GNU_PROPERTY
+static inline int arch_parse_elf_property(u32 type, const void *data,
+					  size_t datasz, bool compat,
+					  struct arch_elf_state *arch)
+{
+	/*
+	 * TODO: Do we want to support in 32bit/compat?
+	 * may be return 0 for now.
+	 */
+	if (IS_ENABLED(CONFIG_COMPAT) && compat)
+		return 0;
+	if ((type & GNU_PROPERTY_RISCV_FEATURE_1_AND) == GNU_PROPERTY_RISCV_FEATURE_1_AND) {
+		const u32 *p = data;
+
+		if (datasz != sizeof(*p))
+			return -ENOEXEC;
+		if (arch_supports_indirect_br_lp_instr() &&
+		   (*p & GNU_PROPERTY_RISCV_FEATURE_1_FCFI))
+			arch->flags |= RISCV_ELF_FCFI;
+		if (arch_supports_shadow_stack() && (*p & GNU_PROPERTY_RISCV_FEATURE_1_BCFI))
+			arch->flags |= RISCV_ELF_BCFI;
+	}
+	return 0;
+}
+
+static inline int arch_elf_pt_proc(void *ehdr, void *phdr,
+				   struct file *f, bool is_interp,
+				   struct arch_elf_state *state)
+{
+	return 0;
+}
+
+static inline int arch_check_elf(void *ehdr, bool has_interp,
+				 void *interp_ehdr,
+				 struct arch_elf_state *state)
+{
+	return 0;
+}
+#endif
 #endif /* _ASM_RISCV_ELF_H */
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index 8955f2432c2d..db676262e61e 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -24,6 +24,7 @@ 
 #include <asm/switch_to.h>
 #include <asm/thread_info.h>
 #include <asm/cpuidle.h>
+#include <linux/mman.h>
 
 register unsigned long gp_in_global __asm__("gp");
 
@@ -135,6 +136,14 @@  void start_thread(struct pt_regs *regs, unsigned long pc,
 	else
 		regs->status |= SR_UXL_64;
 #endif
+#ifdef CONFIG_USER_SHADOW_STACK
+	if (current_thread_info()->user_cfi_state.ufcfi_en)
+		regs->status |= SR_UFCFIEN;
+#endif
+#ifdef CONFIG_USER_INDIRECT_BR_LP
+	if (current_thread_info()->user_cfi_state.ubcfi_en)
+		regs->status |= SR_UBCFIEN;
+#endif
 }
 
 void flush_thread(void)
@@ -189,3 +198,61 @@  int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
 	p->thread.sp = (unsigned long)childregs; /* kernel sp */
 	return 0;
 }
+
+
+int allocate_shadow_stack(unsigned long *shadow_stack_base, unsigned long *shdw_size)
+{
+	int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+	struct mm_struct *mm = current->mm;
+	unsigned long addr, populate, size;
+	*shadow_stack = 0;
+
+	if (!shdw_size)
+		return -EINVAL;
+
+	size = *shdw_size;
+
+	/* If size is 0, then try to calculate yourself */
+	if (size == 0)
+		size = round_up(min_t(unsigned long long, rlimit(RLIMIT_STACK), SZ_4G), PAGE_SIZE);
+	mmap_write_lock(mm);
+	addr = do_mmap(NULL, 0, size, PROT_SHADOWSTACK, flags, 0,
+		       &populate, NULL);
+	mmap_write_unlock(mm);
+	if (IS_ERR_VALUE(addr))
+		return PTR_ERR((void *)addr);
+	*shadow_stack_base = addr;
+	*shdw_size = size;
+	return 0;
+}
+
+#if defined(CONFIG_USER_SHADOW_STACK) || defined(CONFIG_USER_INDIRECT_BR_LP)
+/* gets called from load_elf_binary(). This'll setup shadow stack and forward cfi enable */
+int arch_elf_setup_cfi_state(const struct arch_elf_state *state)
+{
+	int ret = 0;
+	unsigned long shadow_stack_base = 0;
+	unsigned long shadow_stk_size = 0;
+	struct thread_info *info = NULL;
+
+	info = current_thread_info();
+	/* setup back cfi state */
+	/* setup cfi state only if implementation supports it */
+	if (arch_supports_shadow_stack() && (state->flags & RISCV_ELF_BCFI)) {
+		info->user_cfi_state.ubcfi_en = 1;
+		ret = allocate_shadow_stack(&shadow_stack_base, &shadow_stk_size);
+		if (ret)
+			return ret;
+
+		info->user_cfi_state.user_shdw_stk = (shadow_stack_base + shadow_stk_size);
+		info->user_cfi_state.shdw_stk_base = shadow_stack_base;
+	}
+	/* setup forward cfi state */
+	if (arch_supports_indirect_br_lp_instr() && (state->flags & RISCV_ELF_FCFI)) {
+		info->user_cfi_state.ufcfi_en = 1;
+		info->user_cfi_state.lp_label = 0;
+	}
+
+	return ret;
+}
+#endif
\ No newline at end of file