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

x86/fpu: Eager switch PKRU state

While most of a task's FPU state is only needed in user space, the
protection keys need to be in place immediately after a context switch.

The reason is that any access to userspace memory while running in
kernel mode also needs to abide by the memory permissions specified in
the protection keys.

The "eager switch" is a preparation for loading the FPU state on return
to userland. Instead of decoupling PKRU state from xstate, update PKRU
within xstate on write operations by the kernel.

For user tasks the PKRU should be always read from the xsave area and it
should not change anything because the PKRU value was loaded as part of
FPU restore.

For kernel threads the default "init_pkru_value" will be written. Before
this commit, the kernel thread would end up with a random value which it
inherited from the previous user task.

[ bigeasy: save pkru to xstate, no cache, don't use __raw_xsave_addr() ]

[ bp: update commit message, sort headers properly in asm/fpu/xstate.h ]

Signed-off-by: Rik van Riel <riel@surriel.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Dave Hansen <dave.hansen@intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Aubrey Li <aubrey.li@intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jann Horn <jannh@google.com>
Cc: "Jason A. Donenfeld" <Jason@zx2c4.com>
Cc: Joerg Roedel <jroedel@suse.de>
Cc: Juergen Gross <jgross@suse.com>
Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
Cc: kvm ML <kvm@vger.kernel.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: x86-ml <x86@kernel.org>
Link: https://lkml.kernel.org/r/20190403164156.19645-16-bigeasy@linutronix.de

authored by

Rik van Riel and committed by
Borislav Petkov
0cecca9d 0556cbdc

+32 -5
+22 -2
arch/x86/include/asm/fpu/internal.h
··· 14 14 #include <linux/compat.h> 15 15 #include <linux/sched.h> 16 16 #include <linux/slab.h> 17 + #include <linux/mm.h> 17 18 18 19 #include <asm/user.h> 19 20 #include <asm/fpu/api.h> ··· 535 534 */ 536 535 static inline void switch_fpu_finish(struct fpu *new_fpu, int cpu) 537 536 { 538 - if (static_cpu_has(X86_FEATURE_FPU)) 539 - __fpregs_load_activate(new_fpu, cpu); 537 + u32 pkru_val = init_pkru_value; 538 + struct pkru_state *pk; 539 + 540 + if (!static_cpu_has(X86_FEATURE_FPU)) 541 + return; 542 + 543 + __fpregs_load_activate(new_fpu, cpu); 544 + 545 + if (!cpu_feature_enabled(X86_FEATURE_OSPKE)) 546 + return; 547 + 548 + /* 549 + * PKRU state is switched eagerly because it needs to be valid before we 550 + * return to userland e.g. for a copy_to_user() operation. 551 + */ 552 + if (current->mm) { 553 + pk = get_xsave_addr(&new_fpu->state.xsave, XFEATURE_PKRU); 554 + if (pk) 555 + pkru_val = pk->pkru; 556 + } 557 + __write_pkru(pkru_val); 540 558 } 541 559 542 560 /*
+4 -2
arch/x86/include/asm/fpu/xstate.h
··· 2 2 #ifndef __ASM_X86_XSAVE_H 3 3 #define __ASM_X86_XSAVE_H 4 4 5 - #include <linux/types.h> 6 - #include <asm/processor.h> 7 5 #include <linux/uaccess.h> 6 + #include <linux/types.h> 7 + 8 + #include <asm/processor.h> 9 + #include <asm/user.h> 8 10 9 11 /* Bit 63 of XCR0 is reserved for future expansion */ 10 12 #define XFEATURE_MASK_EXTEND (~(XFEATURE_MASK_FPSSE | (1ULL << 63)))
+6
arch/x86/include/asm/pgtable.h
··· 1355 1355 #define PKRU_WD_BIT 0x2 1356 1356 #define PKRU_BITS_PER_PKEY 2 1357 1357 1358 + #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS 1359 + extern u32 init_pkru_value; 1360 + #else 1361 + #define init_pkru_value 0 1362 + #endif 1363 + 1358 1364 static inline bool __pkru_allows_read(u32 pkru, u16 pkey) 1359 1365 { 1360 1366 int pkru_pkey_bits = pkey * PKRU_BITS_PER_PKEY;
-1
arch/x86/mm/pkeys.c
··· 126 126 * in the process's lifetime will not accidentally get access 127 127 * to data which is pkey-protected later on. 128 128 */ 129 - static 130 129 u32 init_pkru_value = PKRU_AD_KEY( 1) | PKRU_AD_KEY( 2) | PKRU_AD_KEY( 3) | 131 130 PKRU_AD_KEY( 4) | PKRU_AD_KEY( 5) | PKRU_AD_KEY( 6) | 132 131 PKRU_AD_KEY( 7) | PKRU_AD_KEY( 8) | PKRU_AD_KEY( 9) |