aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/process_64.c
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2015-04-26 19:47:59 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-26 20:57:38 -0400
commit61f01dd941ba9e06d2bf05994450ecc3d61b6b8b (patch)
tree3a4a4ef2d5e4d44bb4cb1708f9fc4749e9c9824a /arch/x86/kernel/process_64.c
parent1190944f4b12203330ac5ed8784f6c181bf26f2d (diff)
x86_64, asm: Work around AMD SYSRET SS descriptor attribute issue
AMD CPUs don't reinitialize the SS descriptor on SYSRET, so SYSRET with SS == 0 results in an invalid usermode state in which SS is apparently equal to __USER_DS but causes #SS if used. Work around the issue by setting SS to __KERNEL_DS __switch_to, thus ensuring that SYSRET never happens with SS set to NULL. This was exposed by a recent vDSO cleanup. Fixes: e7d6eefaaa44 x86/vdso32/syscall.S: Do not load __USER32_DS to %ss Signed-off-by: Andy Lutomirski <luto@kernel.org> Cc: Peter Anvin <hpa@zytor.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Denys Vlasenko <vda.linux@googlemail.com> Cc: Brian Gerst <brgerst@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/x86/kernel/process_64.c')
-rw-r--r--arch/x86/kernel/process_64.c28
1 files changed, 28 insertions, 0 deletions
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 4baaa972f52a..ddfdbf74f174 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -419,6 +419,34 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
419 task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV)) 419 task_thread_info(prev_p)->flags & _TIF_WORK_CTXSW_PREV))
420 __switch_to_xtra(prev_p, next_p, tss); 420 __switch_to_xtra(prev_p, next_p, tss);
421 421
422 if (static_cpu_has_bug(X86_BUG_SYSRET_SS_ATTRS)) {
423 /*
424 * AMD CPUs have a misfeature: SYSRET sets the SS selector but
425 * does not update the cached descriptor. As a result, if we
426 * do SYSRET while SS is NULL, we'll end up in user mode with
427 * SS apparently equal to __USER_DS but actually unusable.
428 *
429 * The straightforward workaround would be to fix it up just
430 * before SYSRET, but that would slow down the system call
431 * fast paths. Instead, we ensure that SS is never NULL in
432 * system call context. We do this by replacing NULL SS
433 * selectors at every context switch. SYSCALL sets up a valid
434 * SS, so the only way to get NULL is to re-enter the kernel
435 * from CPL 3 through an interrupt. Since that can't happen
436 * in the same task as a running syscall, we are guaranteed to
437 * context switch between every interrupt vector entry and a
438 * subsequent SYSRET.
439 *
440 * We read SS first because SS reads are much faster than
441 * writes. Out of caution, we force SS to __KERNEL_DS even if
442 * it previously had a different non-NULL value.
443 */
444 unsigned short ss_sel;
445 savesegment(ss, ss_sel);
446 if (ss_sel != __KERNEL_DS)
447 loadsegment(ss, __KERNEL_DS);
448 }
449
422 return prev_p; 450 return prev_p;
423} 451}
424 452