Commit cf986457 authored by Jiri Kosina's avatar Jiri Kosina Committed by Khalid Elmously

x86/speculation: Apply IBPB more strictly to avoid cross-process data leak

BugLink: https://bugs.launchpad.net/bugs/1830176

commit dbfe2953 upstream.

Currently, IBPB is only issued in cases when switching into a non-dumpable
process, the rationale being to protect such 'important and security
sensitive' processess (such as GPG) from data leaking into a different
userspace process via spectre v2.

This is however completely insufficient to provide proper userspace-to-userpace
spectrev2 protection, as any process can poison branch buffers before being
scheduled out, and the newly scheduled process immediately becomes spectrev2
victim.

In order to minimize the performance impact (for usecases that do require
spectrev2 protection), issue the barrier only in cases when switching between
processess where the victim can't be ptraced by the potential attacker (as in
such cases, the attacker doesn't have to bother with branch buffers at all).

[ tglx: Split up PTRACE_MODE_NOACCESS_CHK into PTRACE_MODE_SCHED and
  PTRACE_MODE_IBPB to be able to do ptrace() context tracking reasonably
  fine-grained ]

Fixes: 18bf3c3e ("x86/speculation: Use Indirect Branch Prediction Barrier in context switch")
Originally-by: default avatarTim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc:  "WoodhouseDavid" <dwmw@amazon.co.uk>
Cc: Andi Kleen <ak@linux.intel.com>
Cc:  "SchauflerCasey" <casey.schaufler@intel.com>
Link: https://lkml.kernel.org/r/nycvar.YFH.7.76.1809251437340.15880@cbobk.fhfr.pmSigned-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarJuerg Haefliger <juergh@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent 7963d89c
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/ptrace.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
...@@ -102,6 +103,19 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, ...@@ -102,6 +103,19 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next,
local_irq_restore(flags); local_irq_restore(flags);
} }
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, void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk) struct task_struct *tsk)
{ {
...@@ -116,18 +130,13 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, ...@@ -116,18 +130,13 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
* one process from doing Spectre-v2 attacks on another. * one process from doing Spectre-v2 attacks on another.
* *
* As an optimization, flush indirect branches only when * As an optimization, flush indirect branches only when
* switching into processes that disable dumping. This * switching into a processes that can't be ptrace by the
* protects high value processes like gpg, without having * current one (as in such case, attacker has much more
* too high performance overhead. IBPB is *expensive*! * convenient way how to tamper with the next process than
* * branch buffer poisoning).
* 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.
*/ */
if (tsk && tsk->mm && if (static_cpu_has(X86_FEATURE_USE_IBPB) &&
tsk->mm->context.ctx_id != last_ctx_id && ibpb_needed(tsk, last_ctx_id))
get_dumpable(tsk->mm) != SUID_DUMP_USER)
indirect_branch_prediction_barrier(); indirect_branch_prediction_barrier();
/* /*
......
...@@ -57,14 +57,17 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); ...@@ -57,14 +57,17 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
#define PTRACE_MODE_READ 0x01 #define PTRACE_MODE_READ 0x01
#define PTRACE_MODE_ATTACH 0x02 #define PTRACE_MODE_ATTACH 0x02
#define PTRACE_MODE_NOAUDIT 0x04 #define PTRACE_MODE_NOAUDIT 0x04
#define PTRACE_MODE_FSCREDS 0x08 #define PTRACE_MODE_FSCREDS 0x08
#define PTRACE_MODE_REALCREDS 0x10 #define PTRACE_MODE_REALCREDS 0x10
#define PTRACE_MODE_SCHED 0x20
#define PTRACE_MODE_IBPB 0x40
/* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */ /* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */
#define PTRACE_MODE_READ_FSCREDS (PTRACE_MODE_READ | PTRACE_MODE_FSCREDS) #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_READ_REALCREDS (PTRACE_MODE_READ | PTRACE_MODE_REALCREDS)
#define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS) #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_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS)
#define PTRACE_MODE_SPEC_IBPB (PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MODE_IBPB)
/** /**
* ptrace_may_access - check whether the caller is permitted to access * ptrace_may_access - check whether the caller is permitted to access
...@@ -82,6 +85,20 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead); ...@@ -82,6 +85,20 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
*/ */
extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); 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) static inline int ptrace_reparented(struct task_struct *child)
{ {
return !same_thread_group(child->real_parent, child->parent); return !same_thread_group(child->real_parent, child->parent);
......
...@@ -228,6 +228,9 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state) ...@@ -228,6 +228,9 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
{ {
if (mode & PTRACE_MODE_SCHED)
return false;
if (mode & PTRACE_MODE_NOAUDIT) if (mode & PTRACE_MODE_NOAUDIT)
return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE); return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE);
else else
...@@ -295,9 +298,16 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode) ...@@ -295,9 +298,16 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
!ptrace_has_cap(mm->user_ns, mode))) !ptrace_has_cap(mm->user_ns, mode)))
return -EPERM; return -EPERM;
if (mode & PTRACE_MODE_SCHED)
return 0;
return security_ptrace_access_check(task, mode); 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) bool ptrace_may_access(struct task_struct *task, unsigned int mode)
{ {
int err; int err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment