To migitgate possible app to app attack from branch target buffer poisoning,
a new prctl is provided to control branch speculation for applications in
user app.  The following interfaces are provided:

prctl(PR_SET_SPECULATION_CTRL, PR_INDIR_BRANCH, PR_SPEC_DISABLE, 0, 0);
- Disable branch target speculation to protect against app to app
style attack using IBPB and STIBP

prctl(PR_SET_SPECULATION_CTRL, PR_INDIR_BRANCH, PR_SPEC_ENABLE, 0, 0);
- Allow branch target speculation, no mitigation for Spectre V2

prctl(PR_GET_SPECULATION_CTRL, PR_INDIR_BRANCH, 0, 0, 0)
- Query the indirect branch speculation restriction on a process

Signed-off-by: Tim Chen <[email protected]>
---
 Documentation/admin-guide/kernel-parameters.txt |  4 +-
 Documentation/userspace-api/spec_ctrl.rst       |  8 +++
 arch/x86/kernel/cpu/bugs.c                      | 82 ++++++++++++++++++++++++-
 arch/x86/mm/tlb.c                               | 30 ++-------
 fs/exec.c                                       | 13 +++-
 include/linux/sched.h                           |  5 ++
 include/linux/sched/coredump.h                  |  2 +-
 include/uapi/linux/prctl.h                      |  1 +
 kernel/cred.c                                   |  2 +-
 kernel/sys.c                                    |  2 +-
 tools/include/uapi/linux/prctl.h                |  1 +
 11 files changed, 117 insertions(+), 33 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt 
b/Documentation/admin-guide/kernel-parameters.txt
index 6243144..640ce9a 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -4190,7 +4190,9 @@
                        [X86] Control app to app mitigation of Spectre variant 2
                        (indirect branch speculation) vulnerability.
 
-                       lite   - only turn on mitigation for non-dumpable 
processes
+                       lite   - turn on mitigation for non-dumpable processes
+                                or processes that has indirect branch 
restricted
+                                via prctl's PR_SET_SPECULATION_CTRL option
                        strict - protect against attacks for all user processes
                        auto   - let kernel decide lite or strict mode
 
diff --git a/Documentation/userspace-api/spec_ctrl.rst 
b/Documentation/userspace-api/spec_ctrl.rst
index 32f3d55..aa71e84 100644
--- a/Documentation/userspace-api/spec_ctrl.rst
+++ b/Documentation/userspace-api/spec_ctrl.rst
@@ -92,3 +92,11 @@ 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_INDIR_BRANCH: Indirect Branch Speculation in Applications
+                   (Mitigate Spectre V2 style user space app to app attack)
+
+  Invocations:
+   * prctl(PR_GET_SPECULATION_CTRL, PR_INDIR_BRANCH, 0, 0, 0);
+   * prctl(PR_SET_SPECULATION_CTRL, PR_INDIR_BRANCH, PR_SPEC_ENABLE, 0, 0);
+   * prctl(PR_SET_SPECULATION_CTRL, PR_INDIR_BRANCH, PR_SPEC_DISABLE, 0, 0);
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index 052f1a5..2ec531f 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/nospec.h>
 #include <linux/prctl.h>
+#include <linux/sched/coredump.h>
 
 #include <asm/spec-ctrl.h>
 #include <asm/cmdline.h>
@@ -150,7 +151,7 @@ static const char *spectre_v2_strings[] = {
 
 static const char *spectre_v2_app2app_strings[] = {
        [SPECTRE_V2_APP2APP_NONE]               = "App-App Vulnerable",
-       [SPECTRE_V2_APP2APP_LITE]               = "App-App Mitigation: Protect 
only non-dumpable process",
+       [SPECTRE_V2_APP2APP_LITE]               = "App-App Mitigation: Protect 
non-dumpable or indir branch restricted process",
        [SPECTRE_V2_APP2APP_STRICT]             = "App-App Mitigation: Full app 
to app attack protection",
 };
 
@@ -728,17 +729,74 @@ static int ssb_prctl_set(struct task_struct *task, 
unsigned long ctrl)
        return 0;
 }
 
+static int indir_branch_prctl_set(struct task_struct *task, unsigned long ctrl)
+{
+       bool update;
+
+       if (spectre_v2_app2app_enabled != SPECTRE_V2_APP2APP_LITE &&
+           spectre_v2_app2app_enabled != SPECTRE_V2_APP2APP_STRICT)
+               return -ENXIO;
+
+       switch (ctrl) {
+       case PR_SPEC_ENABLE:
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_STRICT)
+                       return -ENXIO;
+               if (get_dumpable(task->mm) != SUID_DUMP_USER)
+                       return -ENXIO;
+               task_clear_spec_indir_branch_disable(task);
+               update = test_and_clear_tsk_thread_flag(task, TIF_STIBP);
+               break;
+       case PR_SPEC_DISABLE:
+               if (spectre_v2_app2app_enabled == SPECTRE_V2_APP2APP_STRICT)
+                       return 0;
+               task_set_spec_indir_branch_disable(task);
+               update = !test_and_set_tsk_thread_flag(task, TIF_STIBP);
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       /*
+        * If being set on non-current task, delay setting the CPU
+        * mitigation until it is next scheduled.
+        * Use speculative_store_bypass_update will update SPEC_CTRL MSR
+        */
+       if (task == current && update)
+               speculative_store_bypass_update_current();
+
+       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_INDIR_BRANCH:
+               return indir_branch_prctl_set(task, ctrl);
        default:
                return -ENODEV;
        }
 }
 
+void arch_set_dumpable(struct task_struct *tsk, struct mm_struct *mm, int 
value)
+{
+       if (!static_branch_unlikely(&spectre_v2_app_lite))
+               return;
+       if (!static_cpu_has(X86_FEATURE_STIBP))
+               return;
+
+       if ((unsigned) value != SUID_DUMP_USER) {
+               set_tsk_thread_flag(tsk, TIF_STIBP);
+               return;
+       }
+
+       if (!task_spec_indir_branch_disable(tsk)) {
+               clear_tsk_thread_flag(tsk, TIF_STIBP);
+       }
+}
+
 #ifdef CONFIG_SECCOMP
 void arch_seccomp_spec_mitigate(struct task_struct *task)
 {
@@ -766,11 +824,33 @@ 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_disable(task) ||
+                       get_dumpable(task->mm) != SUID_DUMP_USER)
+                       return PR_SPEC_PRCTL | PR_SPEC_DISABLE;
+               return PR_SPEC_PRCTL | PR_SPEC_ENABLE;
+       case SPECTRE_V2_APP2APP_STRICT:
+               return PR_SPEC_PRCTL | 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_INDIR_BRANCH:
+               return indir_branch_prctl_get(task);
        default:
                return -ENODEV;
        }
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index b3d1daa..65329a7 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -184,8 +184,9 @@ static void sync_current_stack_to_mm(struct mm_struct *mm)
 static bool ibpb_needed(struct task_struct *tsk, u64 last_ctx_id)
 {
        /*
-        * For lite protection mode, we only protect the non-dumpable
-        * processes.
+        * For lite protection mode, we protect processes  
+        * where the user explicitly disable indirect branch
+        * speculation or mark the process as non-dumpable. 
         *
         * Otherwise check if the current (previous) task has access to the 
memory
         * of the @tsk (next) task for strict app to app protection.
@@ -200,30 +201,12 @@ static bool ibpb_needed(struct task_struct *tsk, u64 
last_ctx_id)
                return false;
 
        if (static_branch_unlikely(&spectre_v2_app_lite))
-               return (get_dumpable(tsk->mm) != SUID_DUMP_USER);
+               return (get_dumpable(tsk->mm) != SUID_DUMP_USER ||
+                       task_thread_info(tsk)->flags & _TIF_STIBP);
        else
                return (__ptrace_may_access(tsk, PTRACE_MODE_IBPB));
 }
 
-static void set_stibp(struct task_struct *tsk)
-{
-       /*
-        * For lite protection mode, we set STIBP only 
-        * for non-dumpable processes.
-        */
-
-       if (!static_branch_unlikely(&spectre_v2_app_lite))
-               return;
-
-       if (!tsk || !tsk->mm)
-               return;
-
-       if (get_dumpable(tsk->mm) != SUID_DUMP_USER)
-               set_tsk_thread_flag(tsk, TIF_STIBP);
-       else
-               clear_tsk_thread_flag(tsk, TIF_STIBP);
-}
-
 void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                        struct task_struct *tsk)
 {
@@ -315,9 +298,6 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct 
mm_struct *next,
                                ibpb_needed(tsk, last_ctx_id))
                        indirect_branch_prediction_barrier();
 
-               if (static_cpu_has(X86_FEATURE_STIBP))
-                       set_stibp(tsk); 
-
                if (IS_ENABLED(CONFIG_VMAP_STACK)) {
                        /*
                         * If our current stack is in vmalloc space and isn't
diff --git a/fs/exec.c b/fs/exec.c
index 1ebf6e5..89edadd 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1362,9 +1362,9 @@ void setup_new_exec(struct linux_binprm * bprm)
        if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP ||
            !(uid_eq(current_euid(), current_uid()) &&
              gid_eq(current_egid(), current_gid())))
-               set_dumpable(current->mm, suid_dumpable);
+               set_dumpable(current, current->mm, suid_dumpable);
        else
-               set_dumpable(current->mm, SUID_DUMP_USER);
+               set_dumpable(current, current->mm, SUID_DUMP_USER);
 
        arch_setup_new_exec();
        perf_event_exec();
@@ -1940,10 +1940,15 @@ void set_binfmt(struct linux_binfmt *new)
 }
 EXPORT_SYMBOL(set_binfmt);
 
+void __weak arch_set_dumpable(struct task_struct *tsk, struct mm_struct *mm, 
int value)
+{
+       return;
+}
+
 /*
  * set_dumpable stores three-value SUID_DUMP_* into mm->flags.
  */
-void set_dumpable(struct mm_struct *mm, int value)
+void set_dumpable(struct task_struct *tsk, struct mm_struct *mm, int value)
 {
        unsigned long old, new;
 
@@ -1954,6 +1959,8 @@ void set_dumpable(struct mm_struct *mm, int value)
                old = READ_ONCE(mm->flags);
                new = (old & ~MMF_DUMPABLE_MASK) | value;
        } while (cmpxchg(&mm->flags, old, new) != old);
+
+       arch_set_dumpable(tsk, mm, value);
 }
 
 SYSCALL_DEFINE3(execve,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 977cb57..b0a78fd 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1439,6 +1439,7 @@ 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 TASK_PFA_TEST(name, func)                                      \
        static inline bool task_##func(struct task_struct *p)           \
@@ -1470,6 +1471,10 @@ 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)
+
 static inline void
 current_restore_flags(unsigned long orig_flags, unsigned long flags)
 {
diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h
index ec912d0..4284350 100644
--- a/include/linux/sched/coredump.h
+++ b/include/linux/sched/coredump.h
@@ -14,7 +14,7 @@
 #define MMF_DUMPABLE_BITS 2
 #define MMF_DUMPABLE_MASK ((1 << MMF_DUMPABLE_BITS) - 1)
 
-extern void set_dumpable(struct mm_struct *mm, int value);
+extern void set_dumpable(struct task_struct *tsk, struct mm_struct *mm, int 
value);
 /*
  * This returns the actual value of the suid_dumpable flag. For things
  * that are using this for checking for privilege transitions, it must
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index c0d7ea0..06f71f6 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_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/kernel/cred.c b/kernel/cred.c
index ecf0365..de3f486 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -446,7 +446,7 @@ int commit_creds(struct cred *new)
            !gid_eq(old->fsgid, new->fsgid) ||
            !cred_cap_issubset(old, new)) {
                if (task->mm)
-                       set_dumpable(task->mm, suid_dumpable);
+                       set_dumpable(task, task->mm, suid_dumpable);
                task->pdeath_signal = 0;
                smp_wmb();
        }
diff --git a/kernel/sys.c b/kernel/sys.c
index cf5c675..78af561 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -2292,7 +2292,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, 
unsigned long, arg3,
                        error = -EINVAL;
                        break;
                }
-               set_dumpable(me->mm, arg2);
+               set_dumpable(me, me->mm, arg2);
                break;
 
        case PR_SET_UNALIGN:
diff --git a/tools/include/uapi/linux/prctl.h b/tools/include/uapi/linux/prctl.h
index c0d7ea0..06f71f6 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_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