Create PRCTL interface to restrict an application's indirect branch
speculation.  This will protect the application against spectre v2 attack
from another application.

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>
---
 Documentation/userspace-api/spec_ctrl.rst |  9 ++++
 arch/x86/kernel/cpu/bugs.c                | 80 +++++++++++++++++++++++++++++++
 include/linux/sched.h                     |  9 ++++
 include/uapi/linux/prctl.h                |  1 +
 tools/include/uapi/linux/prctl.h          |  1 +
 5 files changed, 100 insertions(+)

diff --git a/Documentation/userspace-api/spec_ctrl.rst 
b/Documentation/userspace-api/spec_ctrl.rst
index 32f3d55..8a4e268 100644
--- 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);
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index 21caade..8f5187e 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -773,12 +773,69 @@ static int ssb_prctl_set(struct task_struct *task, 
unsigned long ctrl)
        return 0;
 }
 
+static void set_task_stibp(struct task_struct *tsk, bool stibp_on)
+{
+       bool update = false;
+
+       if (stibp_on)
+               update = !test_and_set_tsk_thread_flag(tsk, TIF_STIBP);
+       else
+               update = test_and_clear_tsk_thread_flag(tsk, TIF_STIBP);
+
+       if (tsk == current && update)
+               speculation_ctrl_update_current();
+}
+
+static int indir_branch_prctl_set(struct task_struct *task, unsigned long ctrl)
+{
+       switch (ctrl) {
+       case PR_SPEC_ENABLE:
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_NONE)
+                       return 0;
+               /*
+                * Indirect branch speculation is always disabled in
+                * strict mode.
+                */
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_STRICT)
+                       return -EPERM;
+               task_clear_spec_indir_branch_disable(task);
+               set_task_stibp(task, false);
+               break;
+       case PR_SPEC_DISABLE:
+               /*
+                * Indirect branch speculation is always allowed when
+                * mitigation is force disabled.
+                */
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_NONE)
+                       return -EPERM;
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_STRICT)
+                       return 0;
+               task_set_spec_indir_branch_disable(task);
+               set_task_stibp(task, true);
+               break;
+       case PR_SPEC_FORCE_DISABLE:
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_NONE)
+                       return -EPERM;
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_STRICT)
+                       return 0;
+               task_set_spec_indir_branch_disable(task);
+               task_set_spec_indir_branch_force_disable(task);
+               set_task_stibp(task, 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;
        }
@@ -811,11 +868,34 @@ static int ssb_prctl_get(struct task_struct *task)
        }
 }
 
+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_enabled) {
+       case SPECTRE_V2_APP2APP_NONE:
+               return PR_SPEC_ENABLE;
+       case SPECTRE_V2_APP2APP_LITE:
+               if (task_spec_indir_branch_force_disable(task))
+                       return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
+               if (test_tsk_thread_flag(task, TIF_STIBP))
+                       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;
        }
diff --git a/include/linux/sched.h b/include/linux/sched.h
index a51c13c..e92e4bf 100644
--- 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 in apps */
+#define PFA_SPEC_INDIR_BRANCH_FORCE_DISABLE 6  /* Indirect branch speculation 
restricted in apps forced */
 
 #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_ssb_disable)
 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)
 {
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index c0d7ea0..577f2ca 100644
--- 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)
diff --git a/tools/include/uapi/linux/prctl.h b/tools/include/uapi/linux/prctl.h
index c0d7ea0..577f2ca 100644
--- 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)
-- 
2.9.4

Reply via email to