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

x86/uaccess: Avoid barrier_nospec() in 64-bit copy_from_user()

The barrier_nospec() in 64-bit copy_from_user() is slow. Instead use
pointer masking to force the user pointer to all 1's for an invalid
address.

The kernel test robot reports a 2.6% improvement in the per_thread_ops
benchmark [1].

This is a variation on a patch originally by Josh Poimboeuf [2].

Link: https://lore.kernel.org/202410281344.d02c72a2-oliver.sang@intel.com [1]
Link: https://lore.kernel.org/5b887fe4c580214900e21f6c61095adf9a142735.1730166635.git.jpoimboe@kernel.org [2]
Tested-and-reviewed-by: Josh Poimboeuf <jpoimboe@kernel.org>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+15 -6
+15 -6
include/linux/uaccess.h
··· 38 38 #else 39 39 #define can_do_masked_user_access() 0 40 40 #define masked_user_access_begin(src) NULL 41 + #define mask_user_address(src) (src) 41 42 #endif 42 43 43 44 /* ··· 160 159 { 161 160 unsigned long res = n; 162 161 might_fault(); 163 - if (!should_fail_usercopy() && likely(access_ok(from, n))) { 162 + if (should_fail_usercopy()) 163 + goto fail; 164 + if (can_do_masked_user_access()) 165 + from = mask_user_address(from); 166 + else { 167 + if (!access_ok(from, n)) 168 + goto fail; 164 169 /* 165 170 * Ensure that bad access_ok() speculation will not 166 171 * lead to nasty side effects *after* the copy is 167 172 * finished: 168 173 */ 169 174 barrier_nospec(); 170 - instrument_copy_from_user_before(to, from, n); 171 - res = raw_copy_from_user(to, from, n); 172 - instrument_copy_from_user_after(to, from, n, res); 173 175 } 174 - if (unlikely(res)) 175 - memset(to + (n - res), 0, res); 176 + instrument_copy_from_user_before(to, from, n); 177 + res = raw_copy_from_user(to, from, n); 178 + instrument_copy_from_user_after(to, from, n, res); 179 + if (likely(!res)) 180 + return 0; 181 + fail: 182 + memset(to + (n - res), 0, res); 176 183 return res; 177 184 } 178 185 extern __must_check unsigned long