diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2013-02-12 14:47:31 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-02-12 15:46:40 -0500 |
commit | 3578baaed4613a9fc09bab9f79f6ce2ac682e8a3 (patch) | |
tree | 2f58dbf5dcc6325fd87f17ccafd00f08bed741d8 | |
parent | 16640165c9079e2cf36fdcfca093f29663a716f7 (diff) |
x86, mm: Redesign get_user with a __builtin_choose_expr hack
Instead of using a bitfield, use an odd little trick using typeof,
__builtin_choose_expr, and sizeof. __builtin_choose_expr is
explicitly defined to not convert its type (its argument is required
to be a constant expression) so this should be well-defined.
The code is still not 100% preturbation-free versus the baseline
before 64-bit get_user(), but the differences seem to be very small,
mostly related to padding and to gcc deciding when to spill registers.
Cc: Jamie Lokier <jamie@shareable.org>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: H. J. Lu <hjl.tools@gmail.com>
Link: http://lkml.kernel.org/r/511A8922.6050908@zytor.com
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/include/asm/uaccess.h | 57 |
1 files changed, 14 insertions, 43 deletions
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index a8d12653f304..d710a2555fd6 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h | |||
@@ -125,13 +125,12 @@ extern int __get_user_4(void); | |||
125 | extern int __get_user_8(void); | 125 | extern int __get_user_8(void); |
126 | extern int __get_user_bad(void); | 126 | extern int __get_user_bad(void); |
127 | 127 | ||
128 | #define __get_user_x(size, ret, x, ptr) \ | 128 | /* |
129 | asm volatile("call __get_user_" #size \ | 129 | * This is a type: either unsigned long, if the argument fits into |
130 | : "=a" (ret), "=d" (x) \ | 130 | * that type, or otherwise unsigned long long. |
131 | : "0" (ptr)) \ | 131 | */ |
132 | 132 | #define __inttype(x) \ | |
133 | /* Careful: we have to cast the result to the type of the pointer | 133 | __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL)) |
134 | * for sign reasons */ | ||
135 | 134 | ||
136 | /** | 135 | /** |
137 | * get_user: - Get a simple variable from user space. | 136 | * get_user: - Get a simple variable from user space. |
@@ -149,48 +148,20 @@ extern int __get_user_bad(void); | |||
149 | * | 148 | * |
150 | * Returns zero on success, or -EFAULT on error. | 149 | * Returns zero on success, or -EFAULT on error. |
151 | * On error, the variable @x is set to zero. | 150 | * On error, the variable @x is set to zero. |
151 | * | ||
152 | * Careful: we have to cast the result to the type of the pointer | ||
153 | * for sign reasons. | ||
152 | */ | 154 | */ |
153 | #ifdef CONFIG_X86_32 | ||
154 | #define __get_user_8(ret, x, ptr) \ | ||
155 | do { \ | ||
156 | register unsigned long long __xx asm("%edx"); \ | ||
157 | asm volatile("call __get_user_8" \ | ||
158 | : "=a" (ret), "=r" (__xx) \ | ||
159 | : "0" (ptr)); \ | ||
160 | (x) = __xx; \ | ||
161 | } while (0) | ||
162 | |||
163 | #else | ||
164 | #define __get_user_8(__ret_gu, __val_gu, ptr) \ | ||
165 | __get_user_x(8, __ret_gu, __val_gu, ptr) | ||
166 | #endif | ||
167 | |||
168 | #define get_user(x, ptr) \ | 155 | #define get_user(x, ptr) \ |
169 | ({ \ | 156 | ({ \ |
170 | int __ret_gu; \ | 157 | int __ret_gu; \ |
171 | struct { \ | 158 | register __inttype(*(ptr)) __val_gu asm("%edx"); \ |
172 | unsigned long long __val_n : 8*sizeof(*(ptr)); \ | ||
173 | } __val_gu; \ | ||
174 | __chk_user_ptr(ptr); \ | 159 | __chk_user_ptr(ptr); \ |
175 | might_fault(); \ | 160 | might_fault(); \ |
176 | switch (sizeof(*(ptr))) { \ | 161 | asm volatile("call __get_user_%P3" \ |
177 | case 1: \ | 162 | : "=a" (__ret_gu), "=r" (__val_gu) \ |
178 | __get_user_x(1, __ret_gu, __val_gu.__val_n, ptr); \ | 163 | : "0" (ptr), "i" (sizeof(*(ptr)))); \ |
179 | break; \ | 164 | (x) = (__typeof__(*(ptr))) __val_gu; \ |
180 | case 2: \ | ||
181 | __get_user_x(2, __ret_gu, __val_gu.__val_n, ptr); \ | ||
182 | break; \ | ||
183 | case 4: \ | ||
184 | __get_user_x(4, __ret_gu, __val_gu.__val_n, ptr); \ | ||
185 | break; \ | ||
186 | case 8: \ | ||
187 | __get_user_8(__ret_gu, __val_gu.__val_n, ptr); \ | ||
188 | break; \ | ||
189 | default: \ | ||
190 | __get_user_x(X, __ret_gu, __val_gu.__val_n, ptr); \ | ||
191 | break; \ | ||
192 | } \ | ||
193 | (x) = (__typeof__(*(ptr)))__val_gu.__val_n; \ | ||
194 | __ret_gu; \ | 165 | __ret_gu; \ |
195 | }) | 166 | }) |
196 | 167 | ||