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

Merge tag 'x86_fsgsbase_for_v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 fsgsbase updates from Borislav Petkov:
"Misc minor cleanups and corrections to the fsgsbase code and
respective selftests"

* tag 'x86_fsgsbase_for_v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
selftests/x86/fsgsbase: Test PTRACE_PEEKUSER for GSBASE with invalid LDT GS
selftests/x86/fsgsbase: Reap a forgotten child
x86/fsgsbase: Replace static_cpu_has() with boot_cpu_has()
x86/entry/64: Correct the comment over SAVE_AND_SET_GSBASE

+77 -8
+3 -2
arch/x86/entry/entry_64.S
··· 842 842 * retrieve and set the current CPUs kernel GSBASE. The stored value 843 843 * has to be restored in paranoid_exit unconditionally. 844 844 * 845 - * The MSR write ensures that no subsequent load is based on a 846 - * mispredicted GSBASE. No extra FENCE required. 845 + * The unconditional write to GS base below ensures that no subsequent 846 + * loads based on a mispredicted GS base can happen, therefore no LFENCE 847 + * is needed here. 847 848 */ 848 849 SAVE_AND_SET_GSBASE scratch_reg=%rax save_reg=%rbx 849 850 ret
+2 -2
arch/x86/include/asm/fsgsbase.h
··· 57 57 { 58 58 unsigned long fsbase; 59 59 60 - if (static_cpu_has(X86_FEATURE_FSGSBASE)) 60 + if (boot_cpu_has(X86_FEATURE_FSGSBASE)) 61 61 fsbase = rdfsbase(); 62 62 else 63 63 rdmsrl(MSR_FS_BASE, fsbase); ··· 67 67 68 68 static inline void x86_fsbase_write_cpu(unsigned long fsbase) 69 69 { 70 - if (static_cpu_has(X86_FEATURE_FSGSBASE)) 70 + if (boot_cpu_has(X86_FEATURE_FSGSBASE)) 71 71 wrfsbase(fsbase); 72 72 else 73 73 wrmsrl(MSR_FS_BASE, fsbase);
+4 -4
arch/x86/kernel/process_64.c
··· 407 407 { 408 408 unsigned long gsbase; 409 409 410 - if (static_cpu_has(X86_FEATURE_FSGSBASE)) { 410 + if (boot_cpu_has(X86_FEATURE_FSGSBASE)) { 411 411 unsigned long flags; 412 412 413 413 local_irq_save(flags); ··· 422 422 423 423 void x86_gsbase_write_cpu_inactive(unsigned long gsbase) 424 424 { 425 - if (static_cpu_has(X86_FEATURE_FSGSBASE)) { 425 + if (boot_cpu_has(X86_FEATURE_FSGSBASE)) { 426 426 unsigned long flags; 427 427 428 428 local_irq_save(flags); ··· 439 439 440 440 if (task == current) 441 441 fsbase = x86_fsbase_read_cpu(); 442 - else if (static_cpu_has(X86_FEATURE_FSGSBASE) || 442 + else if (boot_cpu_has(X86_FEATURE_FSGSBASE) || 443 443 (task->thread.fsindex == 0)) 444 444 fsbase = task->thread.fsbase; 445 445 else ··· 454 454 455 455 if (task == current) 456 456 gsbase = x86_gsbase_read_cpu_inactive(); 457 - else if (static_cpu_has(X86_FEATURE_FSGSBASE) || 457 + else if (boot_cpu_has(X86_FEATURE_FSGSBASE) || 458 458 (task->thread.gsindex == 0)) 459 459 gsbase = task->thread.gsbase; 460 460 else
+68
tools/testing/selftests/x86/fsgsbase.c
··· 443 443 444 444 #define USER_REGS_OFFSET(r) offsetof(struct user_regs_struct, r) 445 445 446 + static void test_ptrace_write_gs_read_base(void) 447 + { 448 + int status; 449 + pid_t child = fork(); 450 + 451 + if (child < 0) 452 + err(1, "fork"); 453 + 454 + if (child == 0) { 455 + printf("[RUN]\tPTRACE_POKE GS, read GSBASE back\n"); 456 + 457 + printf("[RUN]\tARCH_SET_GS to 1\n"); 458 + if (syscall(SYS_arch_prctl, ARCH_SET_GS, 1) != 0) 459 + err(1, "ARCH_SET_GS"); 460 + 461 + if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) 462 + err(1, "PTRACE_TRACEME"); 463 + 464 + raise(SIGTRAP); 465 + _exit(0); 466 + } 467 + 468 + wait(&status); 469 + 470 + if (WSTOPSIG(status) == SIGTRAP) { 471 + unsigned long base; 472 + unsigned long gs_offset = USER_REGS_OFFSET(gs); 473 + unsigned long base_offset = USER_REGS_OFFSET(gs_base); 474 + 475 + /* Read the initial base. It should be 1. */ 476 + base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL); 477 + if (base == 1) { 478 + printf("[OK]\tGSBASE started at 1\n"); 479 + } else { 480 + nerrs++; 481 + printf("[FAIL]\tGSBASE started at 0x%lx\n", base); 482 + } 483 + 484 + printf("[RUN]\tSet GS = 0x7, read GSBASE\n"); 485 + 486 + /* Poke an LDT selector into GS. */ 487 + if (ptrace(PTRACE_POKEUSER, child, gs_offset, 0x7) != 0) 488 + err(1, "PTRACE_POKEUSER"); 489 + 490 + /* And read the base. */ 491 + base = ptrace(PTRACE_PEEKUSER, child, base_offset, NULL); 492 + 493 + if (base == 0 || base == 1) { 494 + printf("[OK]\tGSBASE reads as 0x%lx with invalid GS\n", base); 495 + } else { 496 + nerrs++; 497 + printf("[FAIL]\tGSBASE=0x%lx (should be 0 or 1)\n", base); 498 + } 499 + } 500 + 501 + ptrace(PTRACE_CONT, child, NULL, NULL); 502 + 503 + wait(&status); 504 + if (!WIFEXITED(status)) 505 + printf("[WARN]\tChild didn't exit cleanly.\n"); 506 + } 507 + 446 508 static void test_ptrace_write_gsbase(void) 447 509 { 448 510 int status; ··· 579 517 580 518 END: 581 519 ptrace(PTRACE_CONT, child, NULL, NULL); 520 + wait(&status); 521 + if (!WIFEXITED(status)) 522 + printf("[WARN]\tChild didn't exit cleanly.\n"); 582 523 } 583 524 584 525 int main() ··· 590 525 591 526 shared_scratch = mmap(NULL, 4096, PROT_READ | PROT_WRITE, 592 527 MAP_ANONYMOUS | MAP_SHARED, -1, 0); 528 + 529 + /* Do these tests before we have an LDT. */ 530 + test_ptrace_write_gs_read_base(); 593 531 594 532 /* Probe FSGSBASE */ 595 533 sethandler(SIGILL, sigill, 0);