Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86: Make __get_user() generate an out-of-line call

Instead of inlining the whole stac/lfence/mov/clac sequence (which also
requires individual exception table entries and several asm instruction
alternatives entries), just generate "call __get_user_nocheck_X" for the
__get_user() cases.

We can use all the same infrastructure that we already do for the
regular "get_user()", and the end result is simpler source code, and
much simpler code generation.

It also means that when I introduce asm goto with input for
"unsafe_get_user()", there are no nasty interactions with the
__get_user() code.

Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+116 -76
+56 -76
arch/x86/include/asm/uaccess.h
··· 96 96 likely(!__range_not_ok(addr, size, user_addr_max())); \ 97 97 }) 98 98 99 - /* 100 - * These are the main single-value transfer routines. They automatically 101 - * use the right size if we just have the right pointer type. 102 - * 103 - * This gets kind of ugly. We want to return _two_ values in "get_user()" 104 - * and yet we don't want to do any pointers, because that is too much 105 - * of a performance impact. Thus we have a few rather ugly macros here, 106 - * and hide all the ugliness from the user. 107 - * 108 - * The "__xxx" versions of the user access functions are versions that 109 - * do not verify the address space, that must have been done previously 110 - * with a separate "access_ok()" call (this is used when we do multiple 111 - * accesses to the same area of user memory). 112 - */ 113 - 114 99 extern int __get_user_1(void); 115 100 extern int __get_user_2(void); 116 101 extern int __get_user_4(void); 117 102 extern int __get_user_8(void); 103 + extern int __get_user_nocheck_1(void); 104 + extern int __get_user_nocheck_2(void); 105 + extern int __get_user_nocheck_4(void); 106 + extern int __get_user_nocheck_8(void); 118 107 extern int __get_user_bad(void); 119 108 120 109 #define __uaccess_begin() stac() ··· 127 138 #define __typefits(x,type,not) \ 128 139 __builtin_choose_expr(sizeof(x)<=sizeof(type),(unsigned type)0,not) 129 140 141 + /* 142 + * This is used for both get_user() and __get_user() to expand to 143 + * the proper special function call that has odd calling conventions 144 + * due to returning both a value and an error, and that depends on 145 + * the size of the pointer passed in. 146 + * 147 + * Careful: we have to cast the result to the type of the pointer 148 + * for sign reasons. 149 + * 150 + * The use of _ASM_DX as the register specifier is a bit of a 151 + * simplification, as gcc only cares about it as the starting point 152 + * and not size: for a 64-bit value it will use %ecx:%edx on 32 bits 153 + * (%ecx being the next register in gcc's x86 register sequence), and 154 + * %rdx on 64 bits. 155 + * 156 + * Clang/LLVM cares about the size of the register, but still wants 157 + * the base register for something that ends up being a pair. 158 + */ 159 + #define do_get_user_call(fn,x,ptr) \ 160 + ({ \ 161 + int __ret_gu; \ 162 + register __inttype(*(ptr)) __val_gu asm("%"_ASM_DX); \ 163 + __chk_user_ptr(ptr); \ 164 + asm volatile("call __" #fn "_%P4" \ 165 + : "=a" (__ret_gu), "=r" (__val_gu), \ 166 + ASM_CALL_CONSTRAINT \ 167 + : "0" (ptr), "i" (sizeof(*(ptr)))); \ 168 + (x) = (__force __typeof__(*(ptr))) __val_gu; \ 169 + __builtin_expect(__ret_gu, 0); \ 170 + }) 171 + 130 172 /** 131 173 * get_user - Get a simple variable from user space. 132 174 * @x: Variable to store result. ··· 176 156 * Return: zero on success, or -EFAULT on error. 177 157 * On error, the variable @x is set to zero. 178 158 */ 179 - /* 180 - * Careful: we have to cast the result to the type of the pointer 181 - * for sign reasons. 159 + #define get_user(x,ptr) ({ might_fault(); do_get_user_call(get_user,x,ptr); }) 160 + 161 + /** 162 + * __get_user - Get a simple variable from user space, with less checking. 163 + * @x: Variable to store result. 164 + * @ptr: Source address, in user space. 182 165 * 183 - * The use of _ASM_DX as the register specifier is a bit of a 184 - * simplification, as gcc only cares about it as the starting point 185 - * and not size: for a 64-bit value it will use %ecx:%edx on 32 bits 186 - * (%ecx being the next register in gcc's x86 register sequence), and 187 - * %rdx on 64 bits. 166 + * Context: User context only. This function may sleep if pagefaults are 167 + * enabled. 188 168 * 189 - * Clang/LLVM cares about the size of the register, but still wants 190 - * the base register for something that ends up being a pair. 169 + * This macro copies a single simple variable from user space to kernel 170 + * space. It supports simple types like char and int, but not larger 171 + * data types like structures or arrays. 172 + * 173 + * @ptr must have pointer-to-simple-variable type, and the result of 174 + * dereferencing @ptr must be assignable to @x without a cast. 175 + * 176 + * Caller must check the pointer with access_ok() before calling this 177 + * function. 178 + * 179 + * Return: zero on success, or -EFAULT on error. 180 + * On error, the variable @x is set to zero. 191 181 */ 192 - #define get_user(x, ptr) \ 193 - ({ \ 194 - int __ret_gu; \ 195 - register __inttype(*(ptr)) __val_gu asm("%"_ASM_DX); \ 196 - __chk_user_ptr(ptr); \ 197 - might_fault(); \ 198 - asm volatile("call __get_user_%P4" \ 199 - : "=a" (__ret_gu), "=r" (__val_gu), \ 200 - ASM_CALL_CONSTRAINT \ 201 - : "0" (ptr), "i" (sizeof(*(ptr)))); \ 202 - (x) = (__force __typeof__(*(ptr))) __val_gu; \ 203 - __builtin_expect(__ret_gu, 0); \ 204 - }) 182 + #define __get_user(x,ptr) do_get_user_call(get_user_nocheck,x,ptr) 205 183 206 184 #define __put_user_x(size, x, ptr, __ret_pu) \ 207 185 asm volatile("call __put_user_" #size : "=a" (__ret_pu) \ ··· 385 367 __builtin_expect(__pu_err, 0); \ 386 368 }) 387 369 388 - #define __get_user_nocheck(x, ptr, size) \ 389 - ({ \ 390 - int __gu_err; \ 391 - __inttype(*(ptr)) __gu_val; \ 392 - __typeof__(ptr) __gu_ptr = (ptr); \ 393 - __typeof__(size) __gu_size = (size); \ 394 - __uaccess_begin_nospec(); \ 395 - __get_user_size(__gu_val, __gu_ptr, __gu_size, __gu_err); \ 396 - __uaccess_end(); \ 397 - (x) = (__force __typeof__(*(ptr)))__gu_val; \ 398 - __builtin_expect(__gu_err, 0); \ 399 - }) 400 - 401 370 /* FIXME: this hack is definitely wrong -AK */ 402 371 struct __large_struct { unsigned long buf[100]; }; 403 372 #define __m(x) (*(struct __large_struct __user *)(x)) ··· 400 395 _ASM_EXTABLE_UA(1b, %l2) \ 401 396 : : ltype(x), "m" (__m(addr)) \ 402 397 : : label) 403 - 404 - /** 405 - * __get_user - Get a simple variable from user space, with less checking. 406 - * @x: Variable to store result. 407 - * @ptr: Source address, in user space. 408 - * 409 - * Context: User context only. This function may sleep if pagefaults are 410 - * enabled. 411 - * 412 - * This macro copies a single simple variable from user space to kernel 413 - * space. It supports simple types like char and int, but not larger 414 - * data types like structures or arrays. 415 - * 416 - * @ptr must have pointer-to-simple-variable type, and the result of 417 - * dereferencing @ptr must be assignable to @x without a cast. 418 - * 419 - * Caller must check the pointer with access_ok() before calling this 420 - * function. 421 - * 422 - * Return: zero on success, or -EFAULT on error. 423 - * On error, the variable @x is set to zero. 424 - */ 425 - 426 - #define __get_user(x, ptr) \ 427 - __get_user_nocheck((x), (ptr), sizeof(*(ptr))) 428 398 429 399 /** 430 400 * __put_user - Write a simple value into user space, with less checking.
+60
arch/x86/lib/getuser.S
··· 35 35 #include <asm/smap.h> 36 36 #include <asm/export.h> 37 37 38 + #define ASM_BARRIER_NOSPEC ALTERNATIVE "", "lfence", X86_FEATURE_LFENCE_RDTSC 39 + 38 40 .text 39 41 SYM_FUNC_START(__get_user_1) 40 42 mov PER_CPU_VAR(current_task), %_ASM_DX ··· 116 114 SYM_FUNC_END(__get_user_8) 117 115 EXPORT_SYMBOL(__get_user_8) 118 116 117 + /* .. and the same for __get_user, just without the range checks */ 118 + SYM_FUNC_START(__get_user_nocheck_1) 119 + ASM_STAC 120 + ASM_BARRIER_NOSPEC 121 + 6: movzbl (%_ASM_AX),%edx 122 + xor %eax,%eax 123 + ASM_CLAC 124 + ret 125 + SYM_FUNC_END(__get_user_nocheck_1) 126 + EXPORT_SYMBOL(__get_user_nocheck_1) 127 + 128 + SYM_FUNC_START(__get_user_nocheck_2) 129 + ASM_STAC 130 + ASM_BARRIER_NOSPEC 131 + 7: movzwl (%_ASM_AX),%edx 132 + xor %eax,%eax 133 + ASM_CLAC 134 + ret 135 + SYM_FUNC_END(__get_user_nocheck_2) 136 + EXPORT_SYMBOL(__get_user_nocheck_2) 137 + 138 + SYM_FUNC_START(__get_user_nocheck_4) 139 + ASM_STAC 140 + ASM_BARRIER_NOSPEC 141 + 8: movl (%_ASM_AX),%edx 142 + xor %eax,%eax 143 + ASM_CLAC 144 + ret 145 + SYM_FUNC_END(__get_user_nocheck_4) 146 + EXPORT_SYMBOL(__get_user_nocheck_4) 147 + 148 + SYM_FUNC_START(__get_user_nocheck_8) 149 + ASM_STAC 150 + ASM_BARRIER_NOSPEC 151 + #ifdef CONFIG_X86_64 152 + 9: movq (%_ASM_AX),%rdx 153 + #else 154 + 9: movl (%_ASM_AX),%edx 155 + 10: movl 4(%_ASM_AX),%ecx 156 + #endif 157 + xor %eax,%eax 158 + ASM_CLAC 159 + ret 160 + SYM_FUNC_END(__get_user_nocheck_8) 161 + EXPORT_SYMBOL(__get_user_nocheck_8) 162 + 119 163 120 164 SYM_CODE_START_LOCAL(.Lbad_get_user_clac) 121 165 ASM_CLAC ··· 182 134 SYM_CODE_END(.Lbad_get_user_8_clac) 183 135 #endif 184 136 137 + /* get_user */ 185 138 _ASM_EXTABLE_UA(1b, .Lbad_get_user_clac) 186 139 _ASM_EXTABLE_UA(2b, .Lbad_get_user_clac) 187 140 _ASM_EXTABLE_UA(3b, .Lbad_get_user_clac) ··· 191 142 #else 192 143 _ASM_EXTABLE_UA(4b, .Lbad_get_user_8_clac) 193 144 _ASM_EXTABLE_UA(5b, .Lbad_get_user_8_clac) 145 + #endif 146 + 147 + /* __get_user */ 148 + _ASM_EXTABLE_UA(6b, .Lbad_get_user_clac) 149 + _ASM_EXTABLE_UA(7b, .Lbad_get_user_clac) 150 + _ASM_EXTABLE_UA(8b, .Lbad_get_user_clac) 151 + #ifdef CONFIG_X86_64 152 + _ASM_EXTABLE_UA(9b, .Lbad_get_user_clac) 153 + #else 154 + _ASM_EXTABLE_UA(9b, .Lbad_get_user_8_clac) 155 + _ASM_EXTABLE_UA(10b, .Lbad_get_user_8_clac) 194 156 #endif