[patch 22/24] x86/speculation: Create PRCTL interface to restrict indirect branch speculation

From: Thomas Gleixner
Date: Wed Nov 21 2018 - 15:18:36 EST


From: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>

Add the PR_SPEC_INDIR_BRANCH option for the PR_GET_SPECULATION_CTRL and
PR_SET_SPECULATION_CTRL prctls to allow fine grained per task control of
indirect branch speculation via STIBP.

Invocations:
Check indirect branch speculation status with
- prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, 0, 0, 0);

Enable indirect branch speculation with
- prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, PR_SPEC_ENABLE, 0, 0);

Disable indirect branch speculation with
- prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, PR_SPEC_DISABLE, 0, 0);

Force disable indirect branch speculation with
- prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);

See Documentation/userspace-api/spec_ctrl.rst.

Signed-off-by: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>
Signed-off-by: Thomas Gleixner <tglx@xxxxxxxxxxxxx>

---
Documentation/userspace-api/spec_ctrl.rst | 9 +++
arch/x86/include/asm/nospec-branch.h | 1
arch/x86/kernel/cpu/bugs.c | 71 ++++++++++++++++++++++++++++++
include/linux/sched.h | 9 +++
include/uapi/linux/prctl.h | 1
tools/include/uapi/linux/prctl.h | 1
6 files changed, 92 insertions(+)

--- a/Documentation/userspace-api/spec_ctrl.rst
+++ b/Documentation/userspace-api/spec_ctrl.rst
@@ -92,3 +92,12 @@ Speculation misfeature controls
* prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_ENABLE, 0, 0);
* prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_DISABLE, 0, 0);
* prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, PR_SPEC_FORCE_DISABLE, 0, 0);
+
+- PR_SPEC_INDIR_BRANCH: Indirect Branch Speculation in User Processes
+ (Mitigate Spectre V2 style attacks against user processes)
+
+ Invocations:
+ * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, 0, 0, 0);
+ * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, PR_SPEC_ENABLE, 0, 0);
+ * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, PR_SPEC_DISABLE, 0, 0);
+ * prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIR_BRANCH, PR_SPEC_FORCE_DISABLE, 0, 0);
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -232,6 +232,7 @@ enum spectre_v2_mitigation {
enum spectre_v2_app2app_mitigation {
SPECTRE_V2_APP2APP_NONE,
SPECTRE_V2_APP2APP_STRICT,
+ SPECTRE_V2_APP2APP_PRCTL,
};

/* The Speculative Store Bypass disable variants */
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -567,6 +567,8 @@ void arch_smt_update(void)
case SPECTRE_V2_APP2APP_STRICT:
update_stibp_strict();
break;
+ case SPECTRE_V2_APP2APP_PRCTL:
+ break;
}

mutex_unlock(&spec_ctrl_mutex);
@@ -753,12 +755,56 @@ static int ssb_prctl_set(struct task_str
return 0;
}

+static int indir_branch_prctl_set(struct task_struct *task, unsigned long ctrl)
+{
+ switch (ctrl) {
+ case PR_SPEC_ENABLE:
+ if (spectre_v2_app2app == SPECTRE_V2_APP2APP_NONE)
+ return 0;
+ /*
+ * Indirect branch speculation is always disabled in strict
+ * mode.
+ */
+ if (spectre_v2_app2app == SPECTRE_V2_APP2APP_STRICT)
+ return -EPERM;
+ task_clear_spec_indir_branch_disable(task);
+ task_update_spec_tif(task, TIF_SPEC_IB, false);
+ break;
+ case PR_SPEC_DISABLE:
+ /*
+ * Indirect branch speculation is always allowed when
+ * mitigation is force disabled.
+ */
+ if (spectre_v2_app2app == SPECTRE_V2_APP2APP_NONE)
+ return -EPERM;
+ if (spectre_v2_app2app == SPECTRE_V2_APP2APP_STRICT)
+ return 0;
+ task_set_spec_indir_branch_disable(task);
+ task_update_spec_tif(task, TIF_SPEC_IB, true);
+ break;
+ case PR_SPEC_FORCE_DISABLE:
+ if (spectre_v2_app2app == SPECTRE_V2_APP2APP_NONE)
+ return -EPERM;
+ if (spectre_v2_app2app == SPECTRE_V2_APP2APP_STRICT)
+ return 0;
+ task_set_spec_indir_branch_disable(task);
+ task_set_spec_indir_branch_force_disable(task);
+ task_update_spec_tif(task, TIF_SPEC_IB, true);
+ break;
+ default:
+ return -ERANGE;
+ }
+ return 0;
+}
+
int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
unsigned long ctrl)
{
switch (which) {
case PR_SPEC_STORE_BYPASS:
return ssb_prctl_set(task, ctrl);
+ case PR_SPEC_INDIR_BRANCH:
+ return indir_branch_prctl_set(task, ctrl);
default:
return -ENODEV;
}
@@ -791,11 +837,34 @@ static int ssb_prctl_get(struct task_str
}
}

+static int indir_branch_prctl_get(struct task_struct *task)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ return PR_SPEC_NOT_AFFECTED;
+
+ switch (spectre_v2_app2app) {
+ case SPECTRE_V2_APP2APP_NONE:
+ return PR_SPEC_ENABLE;
+ case SPECTRE_V2_APP2APP_PRCTL:
+ if (task_spec_indir_branch_force_disable(task))
+ return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
+ if (test_tsk_thread_flag(task, TIF_SPEC_IB))
+ return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
+ return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
+ case SPECTRE_V2_APP2APP_STRICT:
+ return PR_SPEC_DISABLE;
+ default:
+ return PR_SPEC_NOT_AFFECTED;
+ }
+}
+
int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which)
{
switch (which) {
case PR_SPEC_STORE_BYPASS:
return ssb_prctl_get(task);
+ case PR_SPEC_INDIR_BRANCH:
+ return indir_branch_prctl_get(task);
default:
return -ENODEV;
}
@@ -975,6 +1044,8 @@ static char *stibp_state(void)
return ", STIBP: disabled";
case SPECTRE_V2_APP2APP_STRICT:
return ", STIBP: forced";
+ case SPECTRE_V2_APP2APP_PRCTL:
+ return "";
}
return "";
}
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1453,6 +1453,8 @@ static inline bool is_percpu_thread(void
#define PFA_SPREAD_SLAB 2 /* Spread some slab caches over cpuset */
#define PFA_SPEC_SSB_DISABLE 3 /* Speculative Store Bypass disabled */
#define PFA_SPEC_SSB_FORCE_DISABLE 4 /* Speculative Store Bypass force disabled*/
+#define PFA_SPEC_INDIR_BRANCH_DISABLE 5 /* Indirect branch speculation restricted */
+#define PFA_SPEC_INDIR_BRANCH_FORCE_DISABLE 6 /* Indirect branch speculation permanentely restricted */

#define TASK_PFA_TEST(name, func) \
static inline bool task_##func(struct task_struct *p) \
@@ -1484,6 +1486,13 @@ TASK_PFA_CLEAR(SPEC_SSB_DISABLE, spec_ss
TASK_PFA_TEST(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)
TASK_PFA_SET(SPEC_SSB_FORCE_DISABLE, spec_ssb_force_disable)

+TASK_PFA_TEST(SPEC_INDIR_BRANCH_DISABLE, spec_indir_branch_disable)
+TASK_PFA_SET(SPEC_INDIR_BRANCH_DISABLE, spec_indir_branch_disable)
+TASK_PFA_CLEAR(SPEC_INDIR_BRANCH_DISABLE, spec_indir_branch_disable)
+
+TASK_PFA_TEST(SPEC_INDIR_BRANCH_FORCE_DISABLE, spec_indir_branch_force_disable)
+TASK_PFA_SET(SPEC_INDIR_BRANCH_FORCE_DISABLE, spec_indir_branch_force_disable)
+
static inline void
current_restore_flags(unsigned long orig_flags, unsigned long flags)
{
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -212,6 +212,7 @@ struct prctl_mm_map {
#define PR_SET_SPECULATION_CTRL 53
/* Speculation control variants */
# define PR_SPEC_STORE_BYPASS 0
+# define PR_SPEC_INDIR_BRANCH 1
/* Return and control values for PR_SET/GET_SPECULATION_CTRL */
# define PR_SPEC_NOT_AFFECTED 0
# define PR_SPEC_PRCTL (1UL << 0)
--- a/tools/include/uapi/linux/prctl.h
+++ b/tools/include/uapi/linux/prctl.h
@@ -212,6 +212,7 @@ struct prctl_mm_map {
#define PR_SET_SPECULATION_CTRL 53
/* Speculation control variants */
# define PR_SPEC_STORE_BYPASS 0
+# define PR_SPEC_INDIR_BRANCH 1
/* Return and control values for PR_SET/GET_SPECULATION_CTRL */
# define PR_SPEC_NOT_AFFECTED 0
# define PR_SPEC_PRCTL (1UL << 0)