Skip to content
  • Kees Cook's avatar
    x86/asm: Pin sensitive CR4 bits · 873d50d5
    Kees Cook authored
    Several recent exploits have used direct calls to the native_write_cr4()
    function to disable SMEP and SMAP before then continuing their exploits
    using userspace memory access.
    
    Direct calls of this form can be mitigate by pinning bits of CR4 so that
    they cannot be changed through a common function. This is not intended to
    be a general ROP protection (which would require CFI to defend against
    properly), but rather a way to avoid trivial direct function calling (or
    CFI bypasses via a matching function prototype) as seen in:
    
    https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html
    (https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308
    
    )
    
    The goals of this change:
    
     - Pin specific bits (SMEP, SMAP, and UMIP) when writing CR4.
    
     - Avoid setting the bits too early (they must become pinned only after
       CPU feature detection and selection has finished).
    
     - Pinning mask needs to be read-only during normal runtime.
    
     - Pinning needs to be checked after write to validate the cr4 state
    
    Using __ro_after_init on the mask is done so it can't be first disabled
    with a malicious write.
    
    Since these bits are global state (once established by the boot CPU and
    kernel boot parameters), they are safe to write to secondary CPUs before
    those CPUs have finished feature detection. As such, the bits are set at
    the first cr4 write, so that cr4 write bugs can be detected (instead of
    silently papered over). This uses a few bytes less storage of a location we
    don't have: read-only per-CPU data.
    
    A check is performed after the register write because an attack could just
    skip directly to the register write. Such a direct jump is possible because
    of how this function may be built by the compiler (especially due to the
    removal of frame pointers) where it doesn't add a stack frame (function
    exit may only be a retq without pops) which is sufficient for trivial
    exploitation like in the timer overwrites mentioned above).
    
    The asm argument constraints gain the "+" modifier to convince the compiler
    that it shouldn't make ordering assumptions about the arguments or memory,
    and treat them as changed.
    
    Signed-off-by: default avatarKees Cook <keescook@chromium.org>
    Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Dave Hansen <dave.hansen@intel.com>
    Cc: kernel-hardening@lists.openwall.com
    Link: https://lkml.kernel.org/r/20190618045503.39105-3-keescook@chromium.org
    873d50d5