diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-09-07 13:22:28 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-09-09 12:28:47 -0400 |
commit | 8404663f81d212918ff85f493649a7991209fa04 (patch) | |
tree | b695686fd2764914da245ef31649b1deb91ddafa /arch/arm/include | |
parent | 2b2040af0b64cd93e5d4df2494c4486cf604090d (diff) |
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>
Diffstat (limited to 'arch/arm/include')
-rw-r--r-- | arch/arm/include/asm/assembler.h | 8 | ||||
-rw-r--r-- | arch/arm/include/asm/uaccess.h | 40 |
2 files changed, 35 insertions, 13 deletions
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h index 03fb93621d0d..5c8b3bf4d825 100644 --- a/arch/arm/include/asm/assembler.h +++ b/arch/arm/include/asm/assembler.h | |||
@@ -320,4 +320,12 @@ | |||
320 | .size \name , . - \name | 320 | .size \name , . - \name |
321 | .endm | 321 | .endm |
322 | 322 | ||
323 | .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req | ||
324 | #ifndef CONFIG_CPU_USE_DOMAINS | ||
325 | adds \tmp, \addr, #\size - 1 | ||
326 | sbcccs \tmp, \tmp, \limit | ||
327 | bcs \bad | ||
328 | #endif | ||
329 | .endm | ||
330 | |||
323 | #endif /* __ASM_ASSEMBLER_H__ */ | 331 | #endif /* __ASM_ASSEMBLER_H__ */ |
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index 479a6352e0b5..6f83ad6e4d3d 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h | |||
@@ -101,28 +101,39 @@ extern int __get_user_1(void *); | |||
101 | extern int __get_user_2(void *); | 101 | extern int __get_user_2(void *); |
102 | extern int __get_user_4(void *); | 102 | extern int __get_user_4(void *); |
103 | 103 | ||
104 | #define __get_user_x(__r2,__p,__e,__s,__i...) \ | 104 | #define __GUP_CLOBBER_1 "lr", "cc" |
105 | #ifdef CONFIG_CPU_USE_DOMAINS | ||
106 | #define __GUP_CLOBBER_2 "ip", "lr", "cc" | ||
107 | #else | ||
108 | #define __GUP_CLOBBER_2 "lr", "cc" | ||
109 | #endif | ||
110 | #define __GUP_CLOBBER_4 "lr", "cc" | ||
111 | |||
112 | #define __get_user_x(__r2,__p,__e,__l,__s) \ | ||
105 | __asm__ __volatile__ ( \ | 113 | __asm__ __volatile__ ( \ |
106 | __asmeq("%0", "r0") __asmeq("%1", "r2") \ | 114 | __asmeq("%0", "r0") __asmeq("%1", "r2") \ |
115 | __asmeq("%3", "r1") \ | ||
107 | "bl __get_user_" #__s \ | 116 | "bl __get_user_" #__s \ |
108 | : "=&r" (__e), "=r" (__r2) \ | 117 | : "=&r" (__e), "=r" (__r2) \ |
109 | : "0" (__p) \ | 118 | : "0" (__p), "r" (__l) \ |
110 | : __i, "cc") | 119 | : __GUP_CLOBBER_##__s) |
111 | 120 | ||
112 | #define get_user(x,p) \ | 121 | #define get_user(x,p) \ |
113 | ({ \ | 122 | ({ \ |
123 | unsigned long __limit = current_thread_info()->addr_limit - 1; \ | ||
114 | register const typeof(*(p)) __user *__p asm("r0") = (p);\ | 124 | register const typeof(*(p)) __user *__p asm("r0") = (p);\ |
115 | register unsigned long __r2 asm("r2"); \ | 125 | register unsigned long __r2 asm("r2"); \ |
126 | register unsigned long __l asm("r1") = __limit; \ | ||
116 | register int __e asm("r0"); \ | 127 | register int __e asm("r0"); \ |
117 | switch (sizeof(*(__p))) { \ | 128 | switch (sizeof(*(__p))) { \ |
118 | case 1: \ | 129 | case 1: \ |
119 | __get_user_x(__r2, __p, __e, 1, "lr"); \ | 130 | __get_user_x(__r2, __p, __e, __l, 1); \ |
120 | break; \ | 131 | break; \ |
121 | case 2: \ | 132 | case 2: \ |
122 | __get_user_x(__r2, __p, __e, 2, "r3", "lr"); \ | 133 | __get_user_x(__r2, __p, __e, __l, 2); \ |
123 | break; \ | 134 | break; \ |
124 | case 4: \ | 135 | case 4: \ |
125 | __get_user_x(__r2, __p, __e, 4, "lr"); \ | 136 | __get_user_x(__r2, __p, __e, __l, 4); \ |
126 | break; \ | 137 | break; \ |
127 | default: __e = __get_user_bad(); break; \ | 138 | default: __e = __get_user_bad(); break; \ |
128 | } \ | 139 | } \ |
@@ -135,31 +146,34 @@ extern int __put_user_2(void *, unsigned int); | |||
135 | extern int __put_user_4(void *, unsigned int); | 146 | extern int __put_user_4(void *, unsigned int); |
136 | extern int __put_user_8(void *, unsigned long long); | 147 | extern int __put_user_8(void *, unsigned long long); |
137 | 148 | ||
138 | #define __put_user_x(__r2,__p,__e,__s) \ | 149 | #define __put_user_x(__r2,__p,__e,__l,__s) \ |
139 | __asm__ __volatile__ ( \ | 150 | __asm__ __volatile__ ( \ |
140 | __asmeq("%0", "r0") __asmeq("%2", "r2") \ | 151 | __asmeq("%0", "r0") __asmeq("%2", "r2") \ |
152 | __asmeq("%3", "r1") \ | ||
141 | "bl __put_user_" #__s \ | 153 | "bl __put_user_" #__s \ |
142 | : "=&r" (__e) \ | 154 | : "=&r" (__e) \ |
143 | : "0" (__p), "r" (__r2) \ | 155 | : "0" (__p), "r" (__r2), "r" (__l) \ |
144 | : "ip", "lr", "cc") | 156 | : "ip", "lr", "cc") |
145 | 157 | ||
146 | #define put_user(x,p) \ | 158 | #define put_user(x,p) \ |
147 | ({ \ | 159 | ({ \ |
160 | unsigned long __limit = current_thread_info()->addr_limit - 1; \ | ||
148 | register const typeof(*(p)) __r2 asm("r2") = (x); \ | 161 | register const typeof(*(p)) __r2 asm("r2") = (x); \ |
149 | register const typeof(*(p)) __user *__p asm("r0") = (p);\ | 162 | register const typeof(*(p)) __user *__p asm("r0") = (p);\ |
163 | register unsigned long __l asm("r1") = __limit; \ | ||
150 | register int __e asm("r0"); \ | 164 | register int __e asm("r0"); \ |
151 | switch (sizeof(*(__p))) { \ | 165 | switch (sizeof(*(__p))) { \ |
152 | case 1: \ | 166 | case 1: \ |
153 | __put_user_x(__r2, __p, __e, 1); \ | 167 | __put_user_x(__r2, __p, __e, __l, 1); \ |
154 | break; \ | 168 | break; \ |
155 | case 2: \ | 169 | case 2: \ |
156 | __put_user_x(__r2, __p, __e, 2); \ | 170 | __put_user_x(__r2, __p, __e, __l, 2); \ |
157 | break; \ | 171 | break; \ |
158 | case 4: \ | 172 | case 4: \ |
159 | __put_user_x(__r2, __p, __e, 4); \ | 173 | __put_user_x(__r2, __p, __e, __l, 4); \ |
160 | break; \ | 174 | break; \ |
161 | case 8: \ | 175 | case 8: \ |
162 | __put_user_x(__r2, __p, __e, 8); \ | 176 | __put_user_x(__r2, __p, __e, __l, 8); \ |
163 | break; \ | 177 | break; \ |
164 | default: __e = __put_user_bad(); break; \ | 178 | default: __e = __put_user_bad(); break; \ |
165 | } \ | 179 | } \ |