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, disabling and locking
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 | 30 ++++++++++++
 arch/riscv/kernel/ptrace.c           | 95 ++++++++++++++++++++++++++++++++++++
 include/uapi/linux/elf.h             |  2 +
 3 files changed, 127 insertions(+)

diff --git a/arch/riscv/include/uapi/asm/ptrace.h 
b/arch/riscv/include/uapi/asm/ptrace.h
index 659ea3af5680..42c3fc8bd513 100644
--- a/arch/riscv/include/uapi/asm/ptrace.h
+++ b/arch/riscv/include/uapi/asm/ptrace.h
@@ -131,6 +131,36 @@ struct __sc_riscv_cfi_state {
        unsigned long ss_ptr;   /* shadow stack pointer */
 };
 
+#define PTRACE_CFI_LP_EN_BIT   0
+#define PTRACE_CFI_LP_LOCK_BIT 1
+#define PTRACE_CFI_ELP_BIT     2
+#define PTRACE_CFI_SS_EN_BIT   3
+#define PTRACE_CFI_SS_LOCK_BIT 4
+#define PTRACE_CFI_SS_PTR_BIT  5
+
+#define PTRACE_CFI_LP_EN_STATE         (1 << PTRACE_CFI_LP_EN_BIT)
+#define PTRACE_CFI_LP_LOCK_STATE       (1 << PTRACE_CFI_LP_LOCK_BIT)
+#define PTRACE_CFI_ELP_STATE           (1 << PTRACE_CFI_ELP_BIT)
+#define PTRACE_CFI_SS_EN_STATE         (1 << PTRACE_CFI_SS_EN_BIT)
+#define PTRACE_CFI_SS_LOCK_STATE       (1 << PTRACE_CFI_SS_LOCK_BIT)
+#define PTRACE_CFI_SS_PTR_STATE                (1 << PTRACE_CFI_SS_PTR_BIT)
+
+#define PRACE_CFI_STATE_INVALID_MASK   ~(PTRACE_CFI_LP_EN_STATE | \
+                                         PTRACE_CFI_LP_LOCK_STATE | \
+                                         PTRACE_CFI_ELP_STATE | \
+                                         PTRACE_CFI_SS_EN_STATE | \
+                                         PTRACE_CFI_SS_LOCK_STATE | \
+                                         PTRACE_CFI_SS_PTR_STATE)
+
+struct __cfi_status {
+       __u64 cfi_state;
+};
+
+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..933a3d26d33c 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,87 @@ 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;
+
+       memset(&user_cfi, 0, sizeof(user_cfi));
+       regs = task_pt_regs(target);
+
+       if (is_indir_lp_enabled(target)) {
+               user_cfi.cfi_status.cfi_state |= PTRACE_CFI_LP_EN_STATE;
+               user_cfi.cfi_status.cfi_state |= is_indir_lp_locked(target) ?
+                                                PTRACE_CFI_LP_LOCK_STATE : 0;
+               user_cfi.cfi_status.cfi_state |= (regs->status & SR_ELP) ?
+                                               PTRACE_CFI_ELP_STATE : 0;
+       }
+
+       if (is_shstk_enabled(target)) {
+               user_cfi.cfi_status.cfi_state |= (PTRACE_CFI_SS_EN_STATE |
+                                                 PTRACE_CFI_SS_PTR_STATE);
+               user_cfi.cfi_status.cfi_state |= is_shstk_locked(target) ?
+                                                PTRACE_CFI_SS_LOCK_STATE : 0;
+               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.cfi_state &
+            (PTRACE_CFI_LP_EN_STATE | PTRACE_CFI_LP_LOCK_STATE |
+             PTRACE_CFI_SS_EN_STATE | PTRACE_CFI_SS_LOCK_STATE)) ||
+            (user_cfi.cfi_status.cfi_state & PRACE_CFI_STATE_INVALID_MASK))
+               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.cfi_state &
+                   PTRACE_CFI_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) &&
+           (user_cfi.cfi_status.cfi_state & PTRACE_CFI_SS_PTR_STATE))
+               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 +309,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 819ded2d39de..ee30dcd80901 100644
--- a/include/uapi/linux/elf.h
+++ b/include/uapi/linux/elf.h
@@ -545,6 +545,8 @@ typedef struct elf64_shdr {
 #define NT_RISCV_VECTOR        0x901           /* RISC-V vector registers */
 #define NN_RISCV_TAGGED_ADDR_CTRL "LINUX"
 #define NT_RISCV_TAGGED_ADDR_CTRL 0x902        /* RISC-V tagged address 
control (prctl()) */
+#define NN_RISCV_USER_CFI      "LINUX"
+#define NT_RISCV_USER_CFI      0x903           /* RISC-V shadow stack state */
 #define NN_LOONGARCH_CPUCFG    "LINUX"
 #define NT_LOONGARCH_CPUCFG    0xa00   /* LoongArch CPU config registers */
 #define NN_LOONGARCH_CSR       "LINUX"

-- 
2.43.0


Reply via email to