From: Tim Chen <tim.c.c...@linux.intel.com>

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.c...@linux.intel.com>
Signed-off-by: Thomas Gleixner <t...@linutronix.de>

---
 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)


Reply via email to