Expose a new register type NT_RISCV_USER_CFI for risc-v cfi status and
state. Intentionally both landing pad and shadow stack status and state
are rolled into cfi state. Creating two different NT_RISCV_USER_XXX would
not be useful and wastage of a note type. Enabling or disabling of feature
is not allowed via ptrace set interface. However setting `elp` state or
setting shadow stack pointer are allowed via ptrace set interface. It is
expected `gdb` might have use to fixup `elp` state or `shadow stack`
pointer.

Signed-off-by: Deepak Gupta <de...@rivosinc.com>
---
 arch/riscv/include/uapi/asm/ptrace.h | 18 ++++++++
 arch/riscv/kernel/ptrace.c           | 83 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/elf.h             |  1 +
 3 files changed, 102 insertions(+)

diff --git a/arch/riscv/include/uapi/asm/ptrace.h 
b/arch/riscv/include/uapi/asm/ptrace.h
index 659ea3af5680..e6571fba8a8a 100644
--- a/arch/riscv/include/uapi/asm/ptrace.h
+++ b/arch/riscv/include/uapi/asm/ptrace.h
@@ -131,6 +131,24 @@ struct __sc_riscv_cfi_state {
        unsigned long ss_ptr;   /* shadow stack pointer */
 };
 
+struct __cfi_status {
+       /* indirect branch tracking state */
+       __u64 lp_en : 1;
+       __u64 lp_lock : 1;
+       __u64 elp_state : 1;
+
+       /* shadow stack status */
+       __u64 shstk_en : 1;
+       __u64 shstk_lock : 1;
+
+       __u64 rsvd : sizeof(__u64) - 5;
+};
+
+struct user_cfi_state {
+       struct __cfi_status     cfi_status;
+       __u64 shstk_ptr;
+};
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _UAPI_ASM_RISCV_PTRACE_H */
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index ea67e9fb7a58..1c546c01715e 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -19,6 +19,7 @@
 #include <linux/regset.h>
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
+#include <asm/usercfi.h>
 
 enum riscv_regset {
        REGSET_X,
@@ -31,6 +32,9 @@ enum riscv_regset {
 #ifdef CONFIG_RISCV_ISA_SUPM
        REGSET_TAGGED_ADDR_CTRL,
 #endif
+#ifdef CONFIG_RISCV_USER_CFI
+       REGSET_CFI,
+#endif
 };
 
 static int riscv_gpr_get(struct task_struct *target,
@@ -184,6 +188,75 @@ static int tagged_addr_ctrl_set(struct task_struct *target,
 }
 #endif
 
+#ifdef CONFIG_RISCV_USER_CFI
+static int riscv_cfi_get(struct task_struct *target,
+                       const struct user_regset *regset,
+                       struct membuf to)
+{
+       struct user_cfi_state user_cfi;
+       struct pt_regs *regs;
+
+       regs = task_pt_regs(target);
+
+       user_cfi.cfi_status.lp_en = is_indir_lp_enabled(target);
+       user_cfi.cfi_status.lp_lock = is_indir_lp_locked(target);
+       user_cfi.cfi_status.elp_state = (regs->status & SR_ELP);
+
+       user_cfi.cfi_status.shstk_en = is_shstk_enabled(target);
+       user_cfi.cfi_status.shstk_lock = is_shstk_locked(target);
+       user_cfi.shstk_ptr = get_active_shstk(target);
+
+       return membuf_write(&to, &user_cfi, sizeof(user_cfi));
+}
+
+/*
+ * Does it make sense to allowing enable / disable of cfi via ptrace?
+ * Not allowing enable / disable / locking control via ptrace for now.
+ * Setting shadow stack pointer is allowed. GDB might use it to unwind or
+ * some other fixup. Similarly gdb might want to suppress elp and may want
+ * to reset elp state.
+ */
+static int riscv_cfi_set(struct task_struct *target,
+                       const struct user_regset *regset,
+                       unsigned int pos, unsigned int count,
+                       const void *kbuf, const void __user *ubuf)
+{
+       int ret;
+       struct user_cfi_state user_cfi;
+       struct pt_regs *regs;
+
+       regs = task_pt_regs(target);
+
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &user_cfi, 0, -1);
+       if (ret)
+               return ret;
+
+       /*
+        * Not allowing enabling or locking shadow stack or landing pad
+        * There is no disabling of shadow stack or landing pad via ptrace
+        * rsvd field should be set to zero so that if those fields are needed 
in future
+        */
+       if (user_cfi.cfi_status.lp_en || user_cfi.cfi_status.lp_lock ||
+               user_cfi.cfi_status.shstk_en || user_cfi.cfi_status.shstk_lock 
||
+               !user_cfi.cfi_status.rsvd)
+               return -EINVAL;
+
+       /* If lpad is enabled on target and ptrace requests to set / clear elp, 
do that */
+       if (is_indir_lp_enabled(target)) {
+               if (user_cfi.cfi_status.elp_state) /* set elp state */
+                       regs->status |= SR_ELP;
+               else
+                       regs->status &= ~SR_ELP; /* clear elp state */
+       }
+
+       /* If shadow stack enabled on target, set new shadow stack pointer */
+       if (is_shstk_enabled(target))
+               set_active_shstk(target, user_cfi.shstk_ptr);
+
+       return 0;
+}
+#endif
+
 static const struct user_regset riscv_user_regset[] = {
        [REGSET_X] = {
                .core_note_type = NT_PRSTATUS,
@@ -224,6 +297,16 @@ static const struct user_regset riscv_user_regset[] = {
                .set = tagged_addr_ctrl_set,
        },
 #endif
+#ifdef CONFIG_RISCV_USER_CFI
+       [REGSET_CFI] = {
+               .core_note_type = NT_RISCV_USER_CFI,
+               .align = sizeof(__u64),
+               .n = sizeof(struct user_cfi_state) / sizeof(__u64),
+               .size = sizeof(__u64),
+               .regset_get = riscv_cfi_get,
+               .set = riscv_cfi_set,
+       },
+#endif
 };
 
 static const struct user_regset_view riscv_user_native_view = {
diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
index a920cf8934dc..83d16bc61709 100644
--- a/include/uapi/linux/elf.h
+++ b/include/uapi/linux/elf.h
@@ -451,6 +451,7 @@ typedef struct elf64_shdr {
 #define NT_RISCV_CSR   0x900           /* RISC-V Control and Status Registers 
*/
 #define NT_RISCV_VECTOR        0x901           /* RISC-V vector registers */
 #define NT_RISCV_TAGGED_ADDR_CTRL 0x902        /* RISC-V tagged address 
control (prctl()) */
+#define NT_RISCV_USER_CFI      0x903           /* RISC-V shadow stack state */
 #define NT_LOONGARCH_CPUCFG    0xa00   /* LoongArch CPU config registers */
 #define NT_LOONGARCH_CSR       0xa01   /* LoongArch control and status 
registers */
 #define NT_LOONGARCH_LSX       0xa02   /* LoongArch Loongson SIMD Extension 
registers */

-- 
2.45.0


Reply via email to