diff options
Diffstat (limited to 'arch/x86/include/asm/uaccess.h')
| -rw-r--r-- | arch/x86/include/asm/uaccess.h | 222 | 
1 files changed, 210 insertions, 12 deletions
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 5838fa911aa..0d592e0a5b8 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -40,22 +40,30 @@  /*   * Test whether a block of memory is a valid user space address.   * Returns 0 if the range is valid, nonzero otherwise. - * - * This is equivalent to the following test: - * (u33)addr + (u33)size > (u33)current->addr_limit.seg (u65 for x86_64) - * - * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry...   */ +static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit) +{ +	/* +	 * If we have used "sizeof()" for the size, +	 * we know it won't overflow the limit (but +	 * it might overflow the 'addr', so it's +	 * important to subtract the size from the +	 * limit, not add it to the address). +	 */ +	if (__builtin_constant_p(size)) +		return addr > limit - size; + +	/* Arbitrary sizes? Be careful about overflow */ +	addr += size; +	if (addr < size) +		return true; +	return addr > limit; +}  #define __range_not_ok(addr, size, limit)				\  ({									\ -	unsigned long flag, roksum;					\  	__chk_user_ptr(addr);						\ -	asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"		\ -	    : "=&r" (flag), "=r" (roksum)				\ -	    : "1" (addr), "g" ((long)(size)),				\ -	      "rm" (limit));						\ -	flag;								\ +	__chk_range_not_ok((unsigned long __force)(addr), size, limit); \  })  /** @@ -78,7 +86,7 @@   * this function, memory access functions may still return -EFAULT.   */  #define access_ok(type, addr, size) \ -	(likely(__range_not_ok(addr, size, user_addr_max()) == 0)) +	likely(!__range_not_ok(addr, size, user_addr_max()))  /*   * The exception table consists of pairs of addresses relative to the @@ -525,6 +533,98 @@ extern __must_check long strnlen_user(const char __user *str, long n);  unsigned long __must_check clear_user(void __user *mem, unsigned long len);  unsigned long __must_check __clear_user(void __user *mem, unsigned long len); +extern void __cmpxchg_wrong_size(void) +	__compiletime_error("Bad argument size for cmpxchg"); + +#define __user_atomic_cmpxchg_inatomic(uval, ptr, old, new, size)	\ +({									\ +	int __ret = 0;							\ +	__typeof__(ptr) __uval = (uval);				\ +	__typeof__(*(ptr)) __old = (old);				\ +	__typeof__(*(ptr)) __new = (new);				\ +	switch (size) {							\ +	case 1:								\ +	{								\ +		asm volatile("\t" ASM_STAC "\n"				\ +			"1:\t" LOCK_PREFIX "cmpxchgb %4, %2\n"		\ +			"2:\t" ASM_CLAC "\n"				\ +			"\t.section .fixup, \"ax\"\n"			\ +			"3:\tmov     %3, %0\n"				\ +			"\tjmp     2b\n"				\ +			"\t.previous\n"					\ +			_ASM_EXTABLE(1b, 3b)				\ +			: "+r" (__ret), "=a" (__old), "+m" (*(ptr))	\ +			: "i" (-EFAULT), "q" (__new), "1" (__old)	\ +			: "memory"					\ +		);							\ +		break;							\ +	}								\ +	case 2:								\ +	{								\ +		asm volatile("\t" ASM_STAC "\n"				\ +			"1:\t" LOCK_PREFIX "cmpxchgw %4, %2\n"		\ +			"2:\t" ASM_CLAC "\n"				\ +			"\t.section .fixup, \"ax\"\n"			\ +			"3:\tmov     %3, %0\n"				\ +			"\tjmp     2b\n"				\ +			"\t.previous\n"					\ +			_ASM_EXTABLE(1b, 3b)				\ +			: "+r" (__ret), "=a" (__old), "+m" (*(ptr))	\ +			: "i" (-EFAULT), "r" (__new), "1" (__old)	\ +			: "memory"					\ +		);							\ +		break;							\ +	}								\ +	case 4:								\ +	{								\ +		asm volatile("\t" ASM_STAC "\n"				\ +			"1:\t" LOCK_PREFIX "cmpxchgl %4, %2\n"		\ +			"2:\t" ASM_CLAC "\n"				\ +			"\t.section .fixup, \"ax\"\n"			\ +			"3:\tmov     %3, %0\n"				\ +			"\tjmp     2b\n"				\ +			"\t.previous\n"					\ +			_ASM_EXTABLE(1b, 3b)				\ +			: "+r" (__ret), "=a" (__old), "+m" (*(ptr))	\ +			: "i" (-EFAULT), "r" (__new), "1" (__old)	\ +			: "memory"					\ +		);							\ +		break;							\ +	}								\ +	case 8:								\ +	{								\ +		if (!IS_ENABLED(CONFIG_X86_64))				\ +			__cmpxchg_wrong_size();				\ +									\ +		asm volatile("\t" ASM_STAC "\n"				\ +			"1:\t" LOCK_PREFIX "cmpxchgq %4, %2\n"		\ +			"2:\t" ASM_CLAC "\n"				\ +			"\t.section .fixup, \"ax\"\n"			\ +			"3:\tmov     %3, %0\n"				\ +			"\tjmp     2b\n"				\ +			"\t.previous\n"					\ +			_ASM_EXTABLE(1b, 3b)				\ +			: "+r" (__ret), "=a" (__old), "+m" (*(ptr))	\ +			: "i" (-EFAULT), "r" (__new), "1" (__old)	\ +			: "memory"					\ +		);							\ +		break;							\ +	}								\ +	default:							\ +		__cmpxchg_wrong_size();					\ +	}								\ +	*__uval = __old;						\ +	__ret;								\ +}) + +#define user_atomic_cmpxchg_inatomic(uval, ptr, old, new)		\ +({									\ +	access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) ?		\ +		__user_atomic_cmpxchg_inatomic((uval), (ptr),		\ +				(old), (new), sizeof(*(ptr))) :		\ +		-EFAULT;						\ +}) +  /*   * movsl can be slow when source and dest are not both 8-byte aligned   */ @@ -542,5 +642,103 @@ extern struct movsl_mask {  # include <asm/uaccess_64.h>  #endif +unsigned long __must_check _copy_from_user(void *to, const void __user *from, +					   unsigned n); +unsigned long __must_check _copy_to_user(void __user *to, const void *from, +					 unsigned n); + +#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS +# define copy_user_diag __compiletime_error +#else +# define copy_user_diag __compiletime_warning +#endif + +extern void copy_user_diag("copy_from_user() buffer size is too small") +copy_from_user_overflow(void); +extern void copy_user_diag("copy_to_user() buffer size is too small") +copy_to_user_overflow(void) __asm__("copy_from_user_overflow"); + +#undef copy_user_diag + +#ifdef CONFIG_DEBUG_STRICT_USER_COPY_CHECKS + +extern void +__compiletime_warning("copy_from_user() buffer size is not provably correct") +__copy_from_user_overflow(void) __asm__("copy_from_user_overflow"); +#define __copy_from_user_overflow(size, count) __copy_from_user_overflow() + +extern void +__compiletime_warning("copy_to_user() buffer size is not provably correct") +__copy_to_user_overflow(void) __asm__("copy_from_user_overflow"); +#define __copy_to_user_overflow(size, count) __copy_to_user_overflow() + +#else + +static inline void +__copy_from_user_overflow(int size, unsigned long count) +{ +	WARN(1, "Buffer overflow detected (%d < %lu)!\n", size, count); +} + +#define __copy_to_user_overflow __copy_from_user_overflow + +#endif + +static inline unsigned long __must_check +copy_from_user(void *to, const void __user *from, unsigned long n) +{ +	int sz = __compiletime_object_size(to); + +	might_fault(); + +	/* +	 * While we would like to have the compiler do the checking for us +	 * even in the non-constant size case, any false positives there are +	 * a problem (especially when DEBUG_STRICT_USER_COPY_CHECKS, but even +	 * without - the [hopefully] dangerous looking nature of the warning +	 * would make people go look at the respecitive call sites over and +	 * over again just to find that there's no problem). +	 * +	 * And there are cases where it's just not realistic for the compiler +	 * to prove the count to be in range. For example when multiple call +	 * sites of a helper function - perhaps in different source files - +	 * all doing proper range checking, yet the helper function not doing +	 * so again. +	 * +	 * Therefore limit the compile time checking to the constant size +	 * case, and do only runtime checking for non-constant sizes. +	 */ + +	if (likely(sz < 0 || sz >= n)) +		n = _copy_from_user(to, from, n); +	else if(__builtin_constant_p(n)) +		copy_from_user_overflow(); +	else +		__copy_from_user_overflow(sz, n); + +	return n; +} + +static inline unsigned long __must_check +copy_to_user(void __user *to, const void *from, unsigned long n) +{ +	int sz = __compiletime_object_size(from); + +	might_fault(); + +	/* See the comment in copy_from_user() above. */ +	if (likely(sz < 0 || sz >= n)) +		n = _copy_to_user(to, from, n); +	else if(__builtin_constant_p(n)) +		copy_to_user_overflow(); +	else +		__copy_to_user_overflow(sz, n); + +	return n; +} + +#undef __copy_from_user_overflow +#undef __copy_to_user_overflow +  #endif /* _ASM_X86_UACCESS_H */  | 
