CVE-2013-6282 (Other, published 2013-11-20T13:19Z)
Description: value: The (1) get_user and (2) put_user API functions in the Linux kernel before 3.5.5 on the v6k and v7 ARM platforms do not validate certain addresses, which allows attackers to read or modify the contents of arbitrary kernel memory locations via a crafted application, as exploited in the wild against Android devices in October and November 2013.
Severity: HIGH (CVSS v3 base score: )
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/torvalds/linux
Files changed:
arch/arm/include/asm/assembler.h
arch/arm/include/asm/uaccess.h
arch/arm/lib/getuser.S
arch/arm/lib/putuser.S
Commit message: ARM: 7527/1: uaccess: explicitly check __user pointer when !CPU_USE_DOMAINS

The {get,put}_user macros don't perform range checking on the provided
__user address when !CPU_HAS_DOMAINS.

This patch reworks the out-of-line assembly accessors to check the user
address against a specified limit, returning -EFAULT if is is out of
range.

[will: changed get_user register allocation to match put_user]
[rmk: fixed building on older ARM architectures]

Reported-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Cc: stable@vger.kernel.org
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>


Vulnerable code:
#define __get_user_x(__r2,__p,__e,__s,__i...)				\
		: "0" (__p)						\
		: __i, "cc")
			__get_user_x(__r2, __p, __e, 1, "lr");		\
	       		break;						\
			__get_user_x(__r2, __p, __e, 2, "r3", "lr");	\
	       		__get_user_x(__r2, __p, __e, 4, "lr");		\
#define __put_user_x(__r2,__p,__e,__s)					\
		: "0" (__p), "r" (__r2)					\
			__put_user_x(__r2, __p, __e, 1);		\
			__put_user_x(__r2, __p, __e, 2);		\
			__put_user_x(__r2, __p, __e, 4);		\
			__put_user_x(__r2, __p, __e, 8);		\
 *		r2, r3 contains the zero-extended value
#ifdef CONFIG_THUMB2_KERNEL
2: TUSER(ldrb)	r2, [r0]
3: TUSER(ldrb)	r3, [r0, #1]
2: TUSER(ldrb)	r2, [r0], #1
3: TUSER(ldrb)	r3, [r0]
	orr	r2, r2, r3, lsl #8
	orr	r2, r3, r2, lsl #8

Fixed code:
	.macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req
#ifndef CONFIG_CPU_USE_DOMAINS
	adds	\tmp, \addr, #\size - 1
	sbcccs	\tmp, \tmp, \limit
	bcs	\bad
#endif
	.endm

#define __GUP_CLOBBER_1	"lr", "cc"
#ifdef CONFIG_CPU_USE_DOMAINS
#define __GUP_CLOBBER_2	"ip", "lr", "cc"
#else
#define __GUP_CLOBBER_2 "lr", "cc"
#endif
#define __GUP_CLOBBER_4	"lr", "cc"

#define __get_user_x(__r2,__p,__e,__l,__s)				\
		__asmeq("%3", "r1")					\
		: "0" (__p), "r" (__l)					\
		: __GUP_CLOBBER_##__s)
		unsigned long __limit = current_thread_info()->addr_limit - 1; \
		register unsigned long __l asm("r1") = __limit;		\
			__get_user_x(__r2, __p, __e, __l, 1);		\
			break;						\
			__get_user_x(__r2, __p, __e, __l, 2);		\
			__get_user_x(__r2, __p, __e, __l, 4);		\
#define __put_user_x(__r2,__p,__e,__l,__s)				\
		__asmeq("%3", "r1")					\
		: "0" (__p), "r" (__r2), "r" (__l)			\
		unsigned long __limit = current_thread_info()->addr_limit - 1; \
		register unsigned long __l asm("r1") = __limit;		\
			__put_user_x(__r2, __p, __e, __l, 1);		\
			__put_user_x(__r2, __p, __e, __l, 2);		\
			__put_user_x(__r2, __p, __e, __l, 4);		\
			__put_user_x(__r2, __p, __e, __l, 8);		\
 *		r1 contains the address limit, which must be preserved
 *		r2 contains the zero-extended value
#include <asm/assembler.h>
	check_uaccess r0, 1, r1, r2, __get_user_bad
	check_uaccess r0, 2, r1, r2, __get_user_bad
#ifdef CONFIG_CPU_USE_DOMAINS
rb	.req	ip
2:	ldrbt	r2, [r0], #1
3:	ldrbt	rb, [r0], #0
rb	.req	r0
2:	ldrb	r2, [r0]
3:	ldrb	rb, [r0, #1]
	orr	r2, r2, rb, lsl #8
	orr	r2, rb, r2, lsl #8
	check_uaccess r0, 4, r1, r2, __get_user_bad
 *		r1 contains the address limit, which must be preserved
#include <asm/assembler.h>
	check_uaccess r0, 1, r1, ip, __put_user_bad
	check_uaccess r0, 2, r1, ip, __put_user_bad
	check_uaccess r0, 4, r1, ip, __put_user_bad
	check_uaccess r0, 8, r1, ip, __put_user_bad