On Sat, 22 Sep 2018, Thomas Gleixner wrote:
> On Sat, 22 Sep 2018, Peter Zijlstra wrote:
> > This has some unfortunate duplication.
> > 
> > Lets go with it for now, but I'll see if I can do something about that
> > later.
> 
> Yes, I know. I tried to make the duplication smaller, but all attempts
> ended up being a convoluted mess. I'll try again after applying more
> coffee.

Lunch and coffee indeed made brain work better. The simple solution was way
too obvious.

Thanks,

        tglx

8<--------------------
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -7,6 +7,7 @@
 #include <linux/export.h>
 #include <linux/cpu.h>
 #include <linux/debugfs.h>
+#include <linux/ptrace.h>
 
 #include <asm/tlbflush.h>
 #include <asm/mmu_context.h>
@@ -180,6 +181,19 @@ static void sync_current_stack_to_mm(str
        }
 }
 
+static bool ibpb_needed(struct task_struct *tsk, u64 last_ctx_id)
+{
+       /*
+        * Check if the current (previous) task has access to the memory
+        * of the @tsk (next) task. If access is denied, make sure to
+        * issue a IBPB to stop user->user Spectre-v2 attacks.
+        *
+        * Note: __ptrace_may_access() returns 0 or -ERRNO.
+        */
+       return (tsk && tsk->mm && tsk->mm->context.ctx_id != last_ctx_id &&
+               ptrace_may_access_sched(tsk, PTRACE_MODE_SPEC_IBPB));
+}
+
 void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                        struct task_struct *tsk)
 {
@@ -262,18 +276,13 @@ void switch_mm_irqs_off(struct mm_struct
                 * one process from doing Spectre-v2 attacks on another.
                 *
                 * As an optimization, flush indirect branches only when
-                * switching into processes that disable dumping. This
-                * protects high value processes like gpg, without having
-                * too high performance overhead. IBPB is *expensive*!
-                *
-                * This will not flush branches when switching into kernel
-                * threads. It will also not flush if we switch to idle
-                * thread and back to the same process. It will flush if we
-                * switch to a different non-dumpable process.
+                * switching into a processes that can't be ptrace by the
+                * current one (as in such case, attacker has much more
+                * convenient way how to tamper with the next process than
+                * branch buffer poisoning).
                 */
-               if (tsk && tsk->mm &&
-                   tsk->mm->context.ctx_id != last_ctx_id &&
-                   get_dumpable(tsk->mm) != SUID_DUMP_USER)
+               if (static_cpu_has(X86_FEATURE_USE_IBPB) &&
+                               ibpb_needed(tsk, last_ctx_id))
                        indirect_branch_prediction_barrier();
 
                if (IS_ENABLED(CONFIG_VMAP_STACK)) {
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -62,14 +62,17 @@ extern void exit_ptrace(struct task_stru
 #define PTRACE_MODE_READ       0x01
 #define PTRACE_MODE_ATTACH     0x02
 #define PTRACE_MODE_NOAUDIT    0x04
-#define PTRACE_MODE_FSCREDS 0x08
-#define PTRACE_MODE_REALCREDS 0x10
+#define PTRACE_MODE_FSCREDS    0x08
+#define PTRACE_MODE_REALCREDS  0x10
+#define PTRACE_MODE_SCHED      0x20
+#define PTRACE_MODE_IBPB       0x40
 
 /* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */
 #define PTRACE_MODE_READ_FSCREDS (PTRACE_MODE_READ | PTRACE_MODE_FSCREDS)
 #define PTRACE_MODE_READ_REALCREDS (PTRACE_MODE_READ | PTRACE_MODE_REALCREDS)
 #define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS)
 #define PTRACE_MODE_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | 
PTRACE_MODE_REALCREDS)
+#define PTRACE_MODE_SPEC_IBPB (PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MOD_IBPB)
 
 /**
  * ptrace_may_access - check whether the caller is permitted to access
@@ -87,6 +90,20 @@ extern void exit_ptrace(struct task_stru
  */
 extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
 
+/**
+ * ptrace_may_access - check whether the caller is permitted to access
+ * a target task.
+ * @task: target task
+ * @mode: selects type of access and caller credentials
+ *
+ * Returns true on success, false on denial.
+ *
+ * Similar to ptrace_may_access(). Only to be called from context switch
+ * code. Does not call into audit and the regular LSM hooks due to locking
+ * constraints.
+ */
+extern bool ptrace_may_access_sched(struct task_struct *task, unsigned int 
mode);
+
 static inline int ptrace_reparented(struct task_struct *child)
 {
        return !same_thread_group(child->real_parent, child->parent);
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -261,6 +261,9 @@ static int ptrace_check_attach(struct ta
 
 static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
 {
+       if (mode & PTRACE_MODE_SCHED)
+               return false;
+
        if (mode & PTRACE_MODE_NOAUDIT)
                return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE);
        else
@@ -328,9 +331,16 @@ static int __ptrace_may_access(struct ta
             !ptrace_has_cap(mm->user_ns, mode)))
            return -EPERM;
 
+       if (mode & PTRACE_MODE_SCHED)
+               return 0;
        return security_ptrace_access_check(task, mode);
 }
 
+bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode)
+{
+       return __ptrace_may_access(task, mode | PTRACE_MODE_SCHED);
+}
+
 bool ptrace_may_access(struct task_struct *task, unsigned int mode)
 {
        int err;

Reply via email to