aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/include
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-09-07 13:22:28 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-09-09 12:28:47 -0400
commit8404663f81d212918ff85f493649a7991209fa04 (patch)
treeb695686fd2764914da245ef31649b1deb91ddafa /arch/arm/include
parent2b2040af0b64cd93e5d4df2494c4486cf604090d (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.h8
-rw-r--r--arch/arm/include/asm/uaccess.h40
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 *);
101extern int __get_user_2(void *); 101extern int __get_user_2(void *);
102extern int __get_user_4(void *); 102extern 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);
135extern int __put_user_4(void *, unsigned int); 146extern int __put_user_4(void *, unsigned int);
136extern int __put_user_8(void *, unsigned long long); 147extern 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 } \