prctl: Add force disable speculation
authorThomas Gleixner <tglx@linutronix.de>
Thu, 3 May 2018 20:09:15 +0000 (22:09 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 4 May 2018 22:51:43 +0000 (00:51 +0200)
For certain use cases it is desired to enforce mitigations so they cannot
be undone afterwards. That's important for loader stubs which want to
prevent a child from disabling the mitigation again. Will also be used for
seccomp(). The extra state preserving of the prctl state for SSB is a
preparatory step for EBPF dymanic speculation control.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Documentation/userspace-api/spec_ctrl.rst
arch/x86/kernel/cpu/bugs.c
fs/proc/array.c
include/linux/sched.h
include/uapi/linux/prctl.h

index ddbebcd012084a7109bbad2826f4d763dc457af9..1b3690d309438bc9bef5199316a3d67af7fb9f1c 100644 (file)
@@ -25,19 +25,21 @@ PR_GET_SPECULATION_CTRL
 -----------------------
 
 PR_GET_SPECULATION_CTRL returns the state of the speculation misfeature
-which is selected with arg2 of prctl(2). The return value uses bits 0-2 with
+which is selected with arg2 of prctl(2). The return value uses bits 0-3 with
 the following meaning:
 
-==== ================ ===================================================
-Bit  Define           Description
-==== ================ ===================================================
-0    PR_SPEC_PRCTL    Mitigation can be controlled per task by
-                      PR_SET_SPECULATION_CTRL
-1    PR_SPEC_ENABLE   The speculation feature is enabled, mitigation is
-                      disabled
-2    PR_SPEC_DISABLE  The speculation feature is disabled, mitigation is
-                      enabled
-==== ================ ===================================================
+==== ===================== ===================================================
+Bit  Define                Description
+==== ===================== ===================================================
+0    PR_SPEC_PRCTL         Mitigation can be controlled per task by
+                           PR_SET_SPECULATION_CTRL
+1    PR_SPEC_ENABLE        The speculation feature is enabled, mitigation is
+                           disabled
+2    PR_SPEC_DISABLE       The speculation feature is disabled, mitigation is
+                           enabled
+3    PR_SPEC_FORCE_DISABLE Same as PR_SPEC_DISABLE, but cannot be undone. A
+                           subsequent prctl(..., PR_SPEC_ENABLE) will fail.
+==== ===================== ===================================================
 
 If all bits are 0 the CPU is not affected by the speculation misfeature.
 
@@ -47,9 +49,11 @@ misfeature will fail.
 
 PR_SET_SPECULATION_CTRL
 -----------------------
+
 PR_SET_SPECULATION_CTRL allows to control the speculation misfeature, which
 is selected by arg2 of :manpage:`prctl(2)` per task. arg3 is used to hand
-in the control value, i.e. either PR_SPEC_ENABLE or PR_SPEC_DISABLE.
+in the control value, i.e. either PR_SPEC_ENABLE or PR_SPEC_DISABLE or
+PR_SPEC_FORCE_DISABLE.
 
 Common error codes
 ------------------
@@ -70,10 +74,13 @@ Value   Meaning
 0       Success
 
 ERANGE  arg3 is incorrect, i.e. it's neither PR_SPEC_ENABLE nor
-        PR_SPEC_DISABLE
+        PR_SPEC_DISABLE nor PR_SPEC_FORCE_DISABLE
 
 ENXIO   Control of the selected speculation misfeature is not possible.
         See PR_GET_SPECULATION_CTRL.
+
+EPERM   Speculation was disabled with PR_SPEC_FORCE_DISABLE and caller
+        tried to enable it again.
 ======= =================================================================
 
 Speculation misfeature controls
@@ -84,3 +91,4 @@ Speculation misfeature controls
    * prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_STORE_BYPASS, 0, 0, 0);
    * 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);
index f8d9be0e86b129fbc92580a961bcf15de061bc60..7e0f28160e5e5239a672415193ed3d899b0da8c7 100644 (file)
@@ -533,21 +533,37 @@ static void ssb_select_mitigation()
 
 static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl)
 {
-       bool rds = !!test_tsk_thread_flag(task, TIF_RDS);
+       bool update;
 
        if (ssb_mode != SPEC_STORE_BYPASS_PRCTL)
                return -ENXIO;
 
-       if (ctrl == PR_SPEC_ENABLE)
-               clear_tsk_thread_flag(task, TIF_RDS);
-       else
-               set_tsk_thread_flag(task, TIF_RDS);
+       switch (ctrl) {
+       case PR_SPEC_ENABLE:
+               /* If speculation is force disabled, enable is not allowed */
+               if (task_spec_ssb_force_disable(task))
+                       return -EPERM;
+               task_clear_spec_ssb_disable(task);
+               update = test_and_clear_tsk_thread_flag(task, TIF_RDS);
+               break;
+       case PR_SPEC_DISABLE:
+               task_set_spec_ssb_disable(task);
+               update = !test_and_set_tsk_thread_flag(task, TIF_RDS);
+               break;
+       case PR_SPEC_FORCE_DISABLE:
+               task_set_spec_ssb_disable(task);
+               task_set_spec_ssb_force_disable(task);
+               update = !test_and_set_tsk_thread_flag(task, TIF_RDS);
+               break;
+       default:
+               return -ERANGE;
+       }
 
        /*
         * If being set on non-current task, delay setting the CPU
         * mitigation until it is next scheduled.
         */
-       if (task == current && rds != !!test_tsk_thread_flag(task, TIF_RDS))
+       if (task == current && update)
                speculative_store_bypass_update();
 
        return 0;
@@ -559,7 +575,9 @@ static int ssb_prctl_get(struct task_struct *task)
        case SPEC_STORE_BYPASS_DISABLE:
                return PR_SPEC_DISABLE;
        case SPEC_STORE_BYPASS_PRCTL:
-               if (test_tsk_thread_flag(task, TIF_RDS))
+               if (task_spec_ssb_force_disable(task))
+                       return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE;
+               if (task_spec_ssb_disable(task))
                        return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
                return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
        default:
@@ -572,9 +590,6 @@ static int ssb_prctl_get(struct task_struct *task)
 int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which,
                             unsigned long ctrl)
 {
-       if (ctrl != PR_SPEC_ENABLE && ctrl != PR_SPEC_DISABLE)
-               return -ERANGE;
-
        switch (which) {
        case PR_SPEC_STORE_BYPASS:
                return ssb_prctl_set(task, ctrl);
index 303c155f9b04eaf3ac1e783c56269c9a842893fc..d178a0236514b430c910115525aced76dcb2fc89 100644 (file)
@@ -344,6 +344,9 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
        case PR_SPEC_NOT_AFFECTED:
                seq_printf(m, "not vulnerable");
                break;
+       case PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE:
+               seq_printf(m, "thread force mitigated");
+               break;
        case PR_SPEC_PRCTL | PR_SPEC_DISABLE:
                seq_printf(m, "thread mitigated");
                break;
index b3d697f3b5731b4c45986fb10e8afb2d00ecf6e4..e4218d4deba07f9bb42fd57ebf50a0eaed81528d 100644 (file)
@@ -1393,7 +1393,8 @@ static inline bool is_percpu_thread(void)
 #define PFA_NO_NEW_PRIVS               0       /* May not gain new privileges. */
 #define PFA_SPREAD_PAGE                        1       /* Spread page cache over cpuset */
 #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 TASK_PFA_TEST(name, func)                                      \
        static inline bool task_##func(struct task_struct *p)           \
@@ -1418,6 +1419,13 @@ TASK_PFA_TEST(SPREAD_SLAB, spread_slab)
 TASK_PFA_SET(SPREAD_SLAB, spread_slab)
 TASK_PFA_CLEAR(SPREAD_SLAB, spread_slab)
 
+TASK_PFA_TEST(SPEC_SSB_DISABLE, spec_ssb_disable)
+TASK_PFA_SET(SPEC_SSB_DISABLE, spec_ssb_disable)
+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)
+
 static inline void
 current_restore_flags(unsigned long orig_flags, unsigned long flags)
 {
index ebf057ac1346ddddbf8e44d2ced8edfa8c785a11..db9f15f5db047e643780ce23a0d33f9cc599ddf4 100644 (file)
@@ -217,5 +217,6 @@ struct prctl_mm_map {
 # define PR_SPEC_PRCTL                 (1UL << 0)
 # define PR_SPEC_ENABLE                        (1UL << 1)
 # define PR_SPEC_DISABLE               (1UL << 2)
+# define PR_SPEC_FORCE_DISABLE         (1UL << 3)
 
 #endif /* _LINUX_PRCTL_H */