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 BIT(PTRACE_CFI_LP_EN_BIT)
+#define PTRACE_CFI_LP_LOCK_STATE BIT(PTRACE_CFI_LP_LOCK_BIT)
+#define PTRACE_CFI_ELP_STATE BIT(PTRACE_CFI_ELP_BIT)
+#define PTRACE_CFI_SS_EN_STATE BIT(PTRACE_CFI_SS_EN_BIT)
+#define PTRACE_CFI_SS_LOCK_STATE BIT(PTRACE_CFI_SS_LOCK_BIT)
+#define PTRACE_CFI_SS_PTR_STATE BIT(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 /* __ASSEMBLER__ */
#endif /* _UAPI_ASM_RISCV_PTRACE_H */
#include <linux/regset.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
+#include <asm/usercfi.h>
enum riscv_regset {
REGSET_X,
#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,
}
#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 allow enable / disable of cfi via ptrace?
+ * We don't allow enable / disable / locking control via ptrace for now.
+ * Setting the 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 struct user_regset riscv_user_regset[] __ro_after_init = {
[REGSET_X] = {
USER_REGSET_NOTE_TYPE(PRSTATUS),
.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 = {