@@ -7,11 +7,12 @@
#include <linux/regset.h>
-extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active;
+extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active,
+ cetregs_active;
extern user_regset_get2_fn fpregs_get, xfpregs_get, fpregs_soft_get,
- xstateregs_get;
+ xstateregs_get, cetregs_get;
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
- xstateregs_set;
+ xstateregs_set, cetregs_set;
/*
* xstateregs_active == regset_fpregs_active. Please refer to the comment
@@ -432,6 +432,11 @@
#define CET_RESERVED (BIT_ULL(6) | BIT_ULL(7) | BIT_ULL(8) | BIT_ULL(9))
#define CET_SUPPRESS BIT_ULL(10)
#define CET_WAIT_ENDBR BIT_ULL(11)
+#define CET_EG_LEG_BITMAP_BASE_MASK GENMASK_ULL(63, 13)
+
+#define CET_U_IBT_MASK (CET_ENDBR_EN | CET_LEG_IW_EN | CET_NO_TRACK_EN | \
+ CET_NO_TRACK_EN | CET_SUPPRESS_DISABLE | CET_SUPPRESS | \
+ CET_WAIT_ENDBR | CET_EG_LEG_BITMAP_BASE_MASK)
#define MSR_IA32_PL0_SSP 0x000006a4 /* ring-0 shadow stack pointer */
#define MSR_IA32_PL1_SSP 0x000006a5 /* ring-1 shadow stack pointer */
@@ -174,6 +174,101 @@ int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}
+int cetregs_active(struct task_struct *target, const struct user_regset *regset)
+{
+#ifdef CONFIG_X86_SHADOW_STACK
+ if (target->thread.shstk.size)
+ return regset->n;
+#endif
+ return 0;
+}
+
+int cetregs_get(struct task_struct *target, const struct user_regset *regset,
+ struct membuf to)
+{
+ struct fpu *fpu = &target->thread.fpu;
+ struct cet_user_state *cetregs;
+
+ if (!boot_cpu_has(X86_FEATURE_SHSTK))
+ return -ENODEV;
+
+ sync_fpstate(fpu);
+ cetregs = get_xsave_addr(&fpu->fpstate->regs.xsave, XFEATURE_CET_USER);
+ if (!cetregs) {
+ /*
+ * The registers are the in the init state. The init values for
+ * these regs are zero, so just zero the output buffer.
+ */
+ membuf_zero(&to, sizeof(struct cet_user_state));
+ return 0;
+ }
+
+ return membuf_write(&to, cetregs, sizeof(struct cet_user_state));
+}
+
+int cetregs_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct fpu *fpu = &target->thread.fpu;
+ struct xregs_state *xsave = &fpu->fpstate->regs.xsave;
+ struct cet_user_state *cetregs, tmp;
+ bool ia32;
+ int r;
+
+ if (!boot_cpu_has(X86_FEATURE_SHSTK) ||
+ !cetregs_active(target, regset))
+ return -ENODEV;
+
+ ia32 = IS_ENABLED(CONFIG_IA32_EMULATION) &&
+ target->thread_info.status & TS_COMPAT;
+
+ r = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tmp, 0, -1);
+ if (r)
+ return r;
+
+ /*
+ * Some kernel instructions (IRET, etc) can cause exceptions in the case
+ * of disallowed CET register values. Just prevent invalid values.
+ */
+ if ((tmp.user_ssp >= TASK_SIZE_MAX) ||
+ (ia32 && !IS_ALIGNED(tmp.user_ssp, 4)) ||
+ (!ia32 && !IS_ALIGNED(tmp.user_ssp, 8)))
+ return -EINVAL;
+
+ /*
+ * Don't allow any IBT bits to be set because it is not supported by
+ * the kernel yet. Also don't allow reserved bits.
+ */
+ if ((tmp.user_cet & CET_RESERVED) || (tmp.user_cet & CET_U_IBT_MASK))
+ return -EINVAL;
+
+ fpu_force_restore(fpu);
+
+ /*
+ * Don't want to init the xfeature until the kernel will definetely
+ * overwrite it, otherwise if it inits and then fails out, it would
+ * end up initing it to random data.
+ */
+ if (!xfeature_saved(xsave, XFEATURE_CET_USER) &&
+ WARN_ON(init_xfeature(xsave, XFEATURE_CET_USER)))
+ return -ENODEV;
+
+ cetregs = get_xsave_addr(xsave, XFEATURE_CET_USER);
+ if (WARN_ON(!cetregs)) {
+ /*
+ * This shouldn't ever be NULL because it was successfully
+ * inited above if needed. The only scenario would be if an
+ * xfeature was somehow saved in a buffer, but not enabled in
+ * xsave.
+ */
+ return -ENODEV;
+ }
+
+ memmove(cetregs, &tmp, sizeof(tmp));
+ return 0;
+}
+
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
/*
@@ -51,6 +51,7 @@ enum x86_regset_32 {
REGSET_XSTATE32,
REGSET_TLS32,
REGSET_IOPERM32,
+ REGSET_CET32,
};
enum x86_regset_64 {
@@ -58,6 +59,7 @@ enum x86_regset_64 {
REGSET_FP64,
REGSET_IOPERM64,
REGSET_XSTATE64,
+ REGSET_CET64,
};
#define REGSET_GENERAL \
@@ -1267,6 +1269,15 @@ static struct user_regset x86_64_regsets[] __ro_after_init = {
.active = ioperm_active,
.regset_get = ioperm_get
},
+ [REGSET_CET64] = {
+ .core_note_type = NT_X86_CET,
+ .n = sizeof(struct cet_user_state) / sizeof(u64),
+ .size = sizeof(u64),
+ .align = sizeof(u64),
+ .active = cetregs_active,
+ .regset_get = cetregs_get,
+ .set = cetregs_set
+ },
};
static const struct user_regset_view user_x86_64_view = {
@@ -1336,6 +1347,15 @@ static struct user_regset x86_32_regsets[] __ro_after_init = {
.active = ioperm_active,
.regset_get = ioperm_get
},
+ [REGSET_CET32] = {
+ .core_note_type = NT_X86_CET,
+ .n = sizeof(struct cet_user_state) / sizeof(u64),
+ .size = sizeof(u64),
+ .align = sizeof(u64),
+ .active = cetregs_active,
+ .regset_get = cetregs_get,
+ .set = cetregs_set
+ },
};
static const struct user_regset_view user_x86_32_view = {
@@ -406,6 +406,7 @@ typedef struct elf64_shdr {
#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
+#define NT_X86_CET 0x203 /* x86 CET state */
#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */
#define NT_S390_TIMER 0x301 /* s390 timer register */
#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */