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

powerpc/uaccess: Perform barrier_nospec() in KUAP allowance helpers

barrier_nospec() in uaccess helpers is there to protect against
speculative accesses around access_ok().

When using user_access_begin() sequences together with
unsafe_get_user() like macros, barrier_nospec() is called for
every single read although we know the access_ok() is done
onece.

Since all user accesses must be granted by a call to either
allow_read_from_user() or allow_read_write_user() which will
always happen after the access_ok() check, move the barrier_nospec()
there.

Reported-by: Christopher M. Riedl <cmr@codefail.de>
Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/c72f014730823b413528e90ab6c4d3bcb79f8497.1612692067.git.christophe.leroy@csgroup.eu

authored by

Christophe Leroy and committed by
Michael Ellerman
8524e2e7 22b89ba1

+3 -11
+2
arch/powerpc/include/asm/kup.h
··· 91 91 92 92 static inline void allow_read_from_user(const void __user *from, unsigned long size) 93 93 { 94 + barrier_nospec(); 94 95 allow_user_access(NULL, from, size, KUAP_READ); 95 96 } 96 97 ··· 103 102 static inline void allow_read_write_user(void __user *to, const void __user *from, 104 103 unsigned long size) 105 104 { 105 + barrier_nospec(); 106 106 allow_user_access(to, from, size, KUAP_READ_WRITE); 107 107 } 108 108
+1 -11
arch/powerpc/include/asm/uaccess.h
··· 315 315 __chk_user_ptr(__gu_addr); \ 316 316 if (!is_kernel_addr((unsigned long)__gu_addr)) \ 317 317 might_fault(); \ 318 - barrier_nospec(); \ 319 318 if (do_allow) \ 320 319 __get_user_size(__gu_val, __gu_addr, __gu_size, __gu_err); \ 321 320 else \ ··· 332 333 __typeof__(size) __gu_size = (size); \ 333 334 \ 334 335 might_fault(); \ 335 - if (access_ok(__gu_addr, __gu_size)) { \ 336 - barrier_nospec(); \ 336 + if (access_ok(__gu_addr, __gu_size)) \ 337 337 __get_user_size(__gu_val, __gu_addr, __gu_size, __gu_err); \ 338 - } \ 339 338 (x) = (__force __typeof__(*(ptr)))__gu_val; \ 340 339 \ 341 340 __gu_err; \ ··· 347 350 __typeof__(size) __gu_size = (size); \ 348 351 \ 349 352 __chk_user_ptr(__gu_addr); \ 350 - barrier_nospec(); \ 351 353 __get_user_size(__gu_val, __gu_addr, __gu_size, __gu_err); \ 352 354 (x) = (__force __typeof__(*(ptr)))__gu_val; \ 353 355 \ ··· 391 395 { 392 396 unsigned long ret; 393 397 394 - barrier_nospec(); 395 398 allow_read_write_user(to, from, n); 396 399 ret = __copy_tofrom_user(to, from, n); 397 400 prevent_read_write_user(to, from, n); ··· 407 412 408 413 switch (n) { 409 414 case 1: 410 - barrier_nospec(); 411 415 __get_user_size(*(u8 *)to, from, 1, ret); 412 416 break; 413 417 case 2: 414 - barrier_nospec(); 415 418 __get_user_size(*(u16 *)to, from, 2, ret); 416 419 break; 417 420 case 4: 418 - barrier_nospec(); 419 421 __get_user_size(*(u32 *)to, from, 4, ret); 420 422 break; 421 423 case 8: 422 - barrier_nospec(); 423 424 __get_user_size(*(u64 *)to, from, 8, ret); 424 425 break; 425 426 } ··· 423 432 return 0; 424 433 } 425 434 426 - barrier_nospec(); 427 435 allow_read_from_user(from, n); 428 436 ret = __copy_tofrom_user((__force void __user *)to, from, n); 429 437 prevent_read_from_user(from, n);