x86: fix user address masking non-canonical speculation issue

It turns out that AMD has a "Meltdown Lite(tm)" issue with non-canonical
accesses in kernel space.  And so using just the high bit to decide
whether an access is in user space or kernel space ends up with the good
old "leak speculative data" if you have the right gadget using the
result:

  CVE-2020-12965 “Transient Execution of Non-Canonical Accesses“

Now, the kernel surrounds the access with a STAC/CLAC pair, and those
instructions end up serializing execution on older Zen architectures,
which closes the speculation window.

But that was true only up until Zen 5, which renames the AC bit [1].
That improves performance of STAC/CLAC a lot, but also means that the
speculation window is now open.

Note that this affects not just the new address masking, but also the
regular valid_user_address() check used by access_ok(), and the asm
version of the sign bit check in the get_user() helpers.

It does not affect put_user() or clear_user() variants, since there's no
speculative result to be used in a gadget for those operations.

Reported-by: Andrew Cooper <andrew.cooper3@citrix.com>
Link: https://lore.kernel.org/all/80d94591-1297-4afb-b510-c665efd37f10@citrix.com/
Link: https://lore.kernel.org/all/20241023094448.GAZxjFkEOOF_DM83TQ@fat_crate.local/ [1]
Link: https://www.amd.com/en/resources/product-security/bulletin/amd-sb-1010.html
Link: https://arxiv.org/pdf/2108.10771
Cc: Josh Poimboeuf <jpoimboe@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Tested-by: Maciej Wieczor-Retman <maciej.wieczor-retman@intel.com> # LAM case
Fixes: 2865baf540 ("x86: support user address masking instead of non-speculative conditional")
Fixes: 6014bc2756 ("x86-64: make access_ok() independent of LAM")
Fixes: b19b74bc99 ("x86/mm: Rework address range check in get_user() and put_user()")
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2024-10-23 18:17:46 -07:00
parent ae90f6a617
commit 86e6b1547b
4 changed files with 42 additions and 21 deletions

View File

@ -12,6 +12,13 @@
#include <asm/cpufeatures.h> #include <asm/cpufeatures.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/percpu.h> #include <asm/percpu.h>
#include <asm/runtime-const.h>
/*
* Virtual variable: there's no actual backing store for this,
* it can purely be used as 'runtime_const_ptr(USER_PTR_MAX)'
*/
extern unsigned long USER_PTR_MAX;
#ifdef CONFIG_ADDRESS_MASKING #ifdef CONFIG_ADDRESS_MASKING
/* /*
@ -46,19 +53,24 @@ static inline unsigned long __untagged_addr_remote(struct mm_struct *mm,
#endif #endif
/* #define valid_user_address(x) \
* The virtual address space space is logically divided into a kernel ((__force unsigned long)(x) <= runtime_const_ptr(USER_PTR_MAX))
* half and a user half. When cast to a signed type, user pointers
* are positive and kernel pointers are negative.
*/
#define valid_user_address(x) ((__force long)(x) >= 0)
/* /*
* Masking the user address is an alternative to a conditional * Masking the user address is an alternative to a conditional
* user_access_begin that can avoid the fencing. This only works * user_access_begin that can avoid the fencing. This only works
* for dense accesses starting at the address. * for dense accesses starting at the address.
*/ */
#define mask_user_address(x) ((typeof(x))((long)(x)|((long)(x)>>63))) static inline void __user *mask_user_address(const void __user *ptr)
{
unsigned long mask;
asm("cmp %1,%0\n\t"
"sbb %0,%0"
:"=r" (mask)
:"r" (ptr),
"0" (runtime_const_ptr(USER_PTR_MAX)));
return (__force void __user *)(mask | (__force unsigned long)ptr);
}
#define masked_user_access_begin(x) ({ \ #define masked_user_access_begin(x) ({ \
__auto_type __masked_ptr = (x); \ __auto_type __masked_ptr = (x); \
__masked_ptr = mask_user_address(__masked_ptr); \ __masked_ptr = mask_user_address(__masked_ptr); \
@ -69,23 +81,16 @@ static inline unsigned long __untagged_addr_remote(struct mm_struct *mm,
* arbitrary values in those bits rather then masking them off. * arbitrary values in those bits rather then masking them off.
* *
* Enforce two rules: * Enforce two rules:
* 1. 'ptr' must be in the user half of the address space * 1. 'ptr' must be in the user part of the address space
* 2. 'ptr+size' must not overflow into kernel addresses * 2. 'ptr+size' must not overflow into kernel addresses
* *
* Note that addresses around the sign change are not valid addresses, * Note that we always have at least one guard page between the
* and will GP-fault even with LAM enabled if the sign bit is set (see * max user address and the non-canonical gap, allowing us to
* "CR3.LAM_SUP" that can narrow the canonicality check if we ever * ignore small sizes entirely.
* enable it, but not remove it entirely).
*
* So the "overflow into kernel addresses" does not imply some sudden
* exact boundary at the sign bit, and we can allow a lot of slop on the
* size check.
* *
* In fact, we could probably remove the size check entirely, since * In fact, we could probably remove the size check entirely, since
* any kernel accesses will be in increasing address order starting * any kernel accesses will be in increasing address order starting
* at 'ptr', and even if the end might be in kernel space, we'll * at 'ptr'.
* hit the GP faults for non-canonical accesses before we ever get
* there.
* *
* That's a separate optimization, for now just handle the small * That's a separate optimization, for now just handle the small
* constant case. * constant case.

View File

@ -69,6 +69,7 @@
#include <asm/sev.h> #include <asm/sev.h>
#include <asm/tdx.h> #include <asm/tdx.h>
#include <asm/posted_intr.h> #include <asm/posted_intr.h>
#include <asm/runtime-const.h>
#include "cpu.h" #include "cpu.h"
@ -2389,6 +2390,15 @@ void __init arch_cpu_finalize_init(void)
alternative_instructions(); alternative_instructions();
if (IS_ENABLED(CONFIG_X86_64)) { if (IS_ENABLED(CONFIG_X86_64)) {
unsigned long USER_PTR_MAX = TASK_SIZE_MAX-1;
/*
* Enable this when LAM is gated on LASS support
if (cpu_feature_enabled(X86_FEATURE_LAM))
USER_PTR_MAX = (1ul << 63) - PAGE_SIZE - 1;
*/
runtime_const_init(ptr, USER_PTR_MAX);
/* /*
* Make sure the first 2MB area is not mapped by huge pages * Make sure the first 2MB area is not mapped by huge pages
* There are typically fixed size MTRRs in there and overlapping * There are typically fixed size MTRRs in there and overlapping

View File

@ -358,6 +358,7 @@ SECTIONS
#endif #endif
RUNTIME_CONST_VARIABLES RUNTIME_CONST_VARIABLES
RUNTIME_CONST(ptr, USER_PTR_MAX)
. = ALIGN(PAGE_SIZE); . = ALIGN(PAGE_SIZE);

View File

@ -39,8 +39,13 @@
.macro check_range size:req .macro check_range size:req
.if IS_ENABLED(CONFIG_X86_64) .if IS_ENABLED(CONFIG_X86_64)
mov %rax, %rdx movq $0x0123456789abcdef,%rdx
sar $63, %rdx 1:
.pushsection runtime_ptr_USER_PTR_MAX,"a"
.long 1b - 8 - .
.popsection
cmp %rax, %rdx
sbb %rdx, %rdx
or %rdx, %rax or %rdx, %rax
.else .else
cmp $TASK_SIZE_MAX-\size+1, %eax cmp $TASK_SIZE_MAX-\size+1, %eax