Commit 4d8efc2d authored by Robin Murphy's avatar Robin Murphy Committed by Catalin Marinas
Browse files

arm64: Use pointer masking to limit uaccess speculation



Similarly to x86, mitigate speculation past an access_ok() check by
masking the pointer against the address limit before use.

Even if we don't expect speculative writes per se, it is plausible that
a CPU may still speculate at least as far as fetching a cache line for
writing, hence we also harden put_user() and clear_user() for peace of
mind.

Signed-off-by: default avatarRobin Murphy <robin.murphy@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 51369e39
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -227,6 +227,26 @@ static inline void uaccess_enable_not_uao(void)
	__uaccess_enable(ARM64_ALT_PAN_NOT_UAO);
}

/*
 * Sanitise a uaccess pointer such that it becomes NULL if above the
 * current addr_limit.
 */
#define uaccess_mask_ptr(ptr) (__typeof__(ptr))__uaccess_mask_ptr(ptr)
static inline void __user *__uaccess_mask_ptr(const void __user *ptr)
{
	void __user *safe_ptr;

	asm volatile(
	"	bics	xzr, %1, %2\n"
	"	csel	%0, %1, xzr, eq\n"
	: "=&r" (safe_ptr)
	: "r" (ptr), "r" (current_thread_info()->addr_limit)
	: "cc");

	csdb();
	return safe_ptr;
}

/*
 * The "__xxx" versions of the user access functions do not verify the address
 * space - it must have been done previously with a separate "access_ok()"
@@ -297,7 +317,7 @@ do { \
	__typeof__(*(ptr)) __user *__p = (ptr);				\
	might_fault();							\
	access_ok(VERIFY_READ, __p, sizeof(*__p)) ?			\
		__get_user((x), __p) :					\
		__p = uaccess_mask_ptr(__p), __get_user((x), __p) :	\
		((x) = 0, -EFAULT);					\
})

@@ -361,7 +381,7 @@ do { \
	__typeof__(*(ptr)) __user *__p = (ptr);				\
	might_fault();							\
	access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ?			\
		__put_user((x), __p) :					\
		__p = uaccess_mask_ptr(__p), __put_user((x), __p) :	\
		-EFAULT;						\
})

@@ -377,7 +397,7 @@ extern unsigned long __must_check __clear_user(void __user *addr, unsigned long
static inline unsigned long __must_check clear_user(void __user *to, unsigned long n)
{
	if (access_ok(VERIFY_WRITE, to, n))
		n = __clear_user(to, n);
		n = __clear_user(__uaccess_mask_ptr(to), n);
	return n;
}