Skip to content
  • Eric Biggers's avatar
    x86/fpu: Reinitialize FPU registers if restoring FPU state fails · d5c8028b
    Eric Biggers authored
    Userspace can change the FPU state of a task using the ptrace() or
    rt_sigreturn() system calls.  Because reserved bits in the FPU state can
    cause the XRSTOR instruction to fail, the kernel has to carefully
    validate that no reserved bits or other invalid values are being set.
    
    Unfortunately, there have been bugs in this validation code.  For
    example, we were not checking that the 'xcomp_bv' field in the
    xstate_header was 0.  As-is, such bugs are exploitable to read the FPU
    registers of other processes on the system.  To do so, an attacker can
    create a task, assign to it an invalid FPU state, then spin in a loop
    and monitor the values of the FPU registers.  Because the task's FPU
    registers are not being restored, sometimes the FPU registers will have
    the values from another process.
    
    This is likely to continue to be a problem in the future because the
    validation done by the CPU instructions like XRSTOR is not immediately
    visible to kernel developers.  Nor will invalid FPU states ever be
    encountered during ordinary use --- they will only be seen during
    fuzzing or exploits.  There can even be reserved bits outside the
    xstate_header which are easy to forget about.  For example, the MXCSR
    register contains reserved bits, which were not validated by the
    KVM_SET_XSAVE ioctl until commit a575813b ("KVM: x86: Fix load
    damaged SSEx MXCSR register").
    
    Therefore, mitigate this class of vulnerability by restoring the FPU
    registers from init_fpstate if restoring from the task's state fails.
    
    We actually used to do this, but it was (perhaps unwisely) removed by
    commit 9ccc27a5
    
     ("x86/fpu: Remove error return values from
    copy_kernel_to_*regs() functions").  This new patch is also a bit
    different.  First, it only clears the registers, not also the bad
    in-memory state; this is simpler and makes it easier to make the
    mitigation cover all callers of __copy_kernel_to_fpregs().  Second, it
    does the register clearing in an exception handler so that no extra
    instructions are added to context switches.  In fact, we *remove*
    instructions, since previously we were always zeroing the register
    containing 'err' even if CONFIG_X86_DEBUG_FPU was disabled.
    
    Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
    Reviewed-by: default avatarRik van Riel <riel@redhat.com>
    Cc: Andrew Morton <akpm@linux-foundation.org>
    Cc: Andy Lutomirski <luto@amacapital.net>
    Cc: Andy Lutomirski <luto@kernel.org>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Dave Hansen <dave.hansen@linux.intel.com>
    Cc: Dmitry Vyukov <dvyukov@google.com>
    Cc: Eric Biggers <ebiggers3@gmail.com>
    Cc: Fenghua Yu <fenghua.yu@intel.com>
    Cc: Kevin Hao <haokexin@gmail.com>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Michael Halcrow <mhalcrow@google.com>
    Cc: Oleg Nesterov <oleg@redhat.com>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Wanpeng Li <wanpeng.li@hotmail.com>
    Cc: Yu-cheng Yu <yu-cheng.yu@intel.com>
    Cc: kernel-hardening@lists.openwall.com
    Link: http://lkml.kernel.org/r/20170922174156.16780-4-ebiggers3@gmail.com
    Link: http://lkml.kernel.org/r/20170923130016.21448-27-mingo@kernel.org
    
    
    Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
    d5c8028b