Skip to content

Commit dbfe295

Browse files
Jiri KosinaKAGA-KOKO
authored andcommitted
x86/speculation: Apply IBPB more strictly to avoid cross-process data leak
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: 18bf3c3 ("x86/speculation: Use Indirect Branch Prediction Barrier in context switch") Originally-by: Tim Chen <tim.c.chen@linux.intel.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz> Signed-off-by: Thomas 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.pm
1 parent 0cbb76d commit dbfe295

File tree

3 files changed

+49
-13
lines changed

3 files changed

+49
-13
lines changed

arch/x86/mm/tlb.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <linux/export.h>
88
#include <linux/cpu.h>
99
#include <linux/debugfs.h>
10+
#include <linux/ptrace.h>
1011

1112
#include <asm/tlbflush.h>
1213
#include <asm/mmu_context.h>
@@ -180,6 +181,19 @@ static void sync_current_stack_to_mm(struct mm_struct *mm)
180181
}
181182
}
182183

184+
static bool ibpb_needed(struct task_struct *tsk, u64 last_ctx_id)
185+
{
186+
/*
187+
* Check if the current (previous) task has access to the memory
188+
* of the @tsk (next) task. If access is denied, make sure to
189+
* issue a IBPB to stop user->user Spectre-v2 attacks.
190+
*
191+
* Note: __ptrace_may_access() returns 0 or -ERRNO.
192+
*/
193+
return (tsk && tsk->mm && tsk->mm->context.ctx_id != last_ctx_id &&
194+
ptrace_may_access_sched(tsk, PTRACE_MODE_SPEC_IBPB));
195+
}
196+
183197
void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
184198
struct task_struct *tsk)
185199
{
@@ -262,18 +276,13 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
262276
* one process from doing Spectre-v2 attacks on another.
263277
*
264278
* As an optimization, flush indirect branches only when
265-
* switching into processes that disable dumping. This
266-
* protects high value processes like gpg, without having
267-
* too high performance overhead. IBPB is *expensive*!
268-
*
269-
* This will not flush branches when switching into kernel
270-
* threads. It will also not flush if we switch to idle
271-
* thread and back to the same process. It will flush if we
272-
* switch to a different non-dumpable process.
279+
* switching into a processes that can't be ptrace by the
280+
* current one (as in such case, attacker has much more
281+
* convenient way how to tamper with the next process than
282+
* branch buffer poisoning).
273283
*/
274-
if (tsk && tsk->mm &&
275-
tsk->mm->context.ctx_id != last_ctx_id &&
276-
get_dumpable(tsk->mm) != SUID_DUMP_USER)
284+
if (static_cpu_has(X86_FEATURE_USE_IBPB) &&
285+
ibpb_needed(tsk, last_ctx_id))
277286
indirect_branch_prediction_barrier();
278287

279288
if (IS_ENABLED(CONFIG_VMAP_STACK)) {

include/linux/ptrace.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,17 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
6262
#define PTRACE_MODE_READ 0x01
6363
#define PTRACE_MODE_ATTACH 0x02
6464
#define PTRACE_MODE_NOAUDIT 0x04
65-
#define PTRACE_MODE_FSCREDS 0x08
66-
#define PTRACE_MODE_REALCREDS 0x10
65+
#define PTRACE_MODE_FSCREDS 0x08
66+
#define PTRACE_MODE_REALCREDS 0x10
67+
#define PTRACE_MODE_SCHED 0x20
68+
#define PTRACE_MODE_IBPB 0x40
6769

6870
/* shorthands for READ/ATTACH and FSCREDS/REALCREDS combinations */
6971
#define PTRACE_MODE_READ_FSCREDS (PTRACE_MODE_READ | PTRACE_MODE_FSCREDS)
7072
#define PTRACE_MODE_READ_REALCREDS (PTRACE_MODE_READ | PTRACE_MODE_REALCREDS)
7173
#define PTRACE_MODE_ATTACH_FSCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS)
7274
#define PTRACE_MODE_ATTACH_REALCREDS (PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS)
75+
#define PTRACE_MODE_SPEC_IBPB (PTRACE_MODE_ATTACH_REALCREDS | PTRACE_MODE_IBPB)
7376

7477
/**
7578
* ptrace_may_access - check whether the caller is permitted to access
@@ -87,6 +90,20 @@ extern void exit_ptrace(struct task_struct *tracer, struct list_head *dead);
8790
*/
8891
extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
8992

93+
/**
94+
* ptrace_may_access - check whether the caller is permitted to access
95+
* a target task.
96+
* @task: target task
97+
* @mode: selects type of access and caller credentials
98+
*
99+
* Returns true on success, false on denial.
100+
*
101+
* Similar to ptrace_may_access(). Only to be called from context switch
102+
* code. Does not call into audit and the regular LSM hooks due to locking
103+
* constraints.
104+
*/
105+
extern bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode);
106+
90107
static inline int ptrace_reparented(struct task_struct *child)
91108
{
92109
return !same_thread_group(child->real_parent, child->parent);

kernel/ptrace.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
261261

262262
static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
263263
{
264+
if (mode & PTRACE_MODE_SCHED)
265+
return false;
266+
264267
if (mode & PTRACE_MODE_NOAUDIT)
265268
return has_ns_capability_noaudit(current, ns, CAP_SYS_PTRACE);
266269
else
@@ -328,9 +331,16 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
328331
!ptrace_has_cap(mm->user_ns, mode)))
329332
return -EPERM;
330333

334+
if (mode & PTRACE_MODE_SCHED)
335+
return 0;
331336
return security_ptrace_access_check(task, mode);
332337
}
333338

339+
bool ptrace_may_access_sched(struct task_struct *task, unsigned int mode)
340+
{
341+
return __ptrace_may_access(task, mode | PTRACE_MODE_SCHED);
342+
}
343+
334344
bool ptrace_may_access(struct task_struct *task, unsigned int mode)
335345
{
336346
int err;

0 commit comments

Comments
 (0)