From: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx> PKRU has space in the task XSAVE buffer, but is not context-switched by XSAVE/XRSTOR. It is switched more eagerly than other FPU state because PKRU affects things like copy_to/from_user(). This is because PKRU affects user *PERMISSION* accesses, not just accesses made from user *MODE* itself. Prepare to move PKRU away from being XSAVE-managed. Allocate space in the thread_struct for it and save/restore it in the context-switch path separately from the XSAVE-managed features. Leave the XSAVE storage in place for now to ensure bisectability. Signed-off-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx> Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: Borislav Petkov <bp@xxxxxxxxx> Cc: x86@xxxxxxxxxx Cc: Andy Lutomirski <luto@xxxxxxxxxx> --- b/arch/x86/include/asm/pkru.h | 5 +++++ b/arch/x86/kernel/cpu/common.c | 3 +++ b/arch/x86/kernel/process_64.c | 9 ++++----- b/arch/x86/mm/pkeys.c | 2 ++ 4 files changed, 14 insertions(+), 5 deletions(-) diff -puN arch/x86/include/asm/pkru.h~pkru-stash-thread-value arch/x86/include/asm/pkru.h --- a/arch/x86/include/asm/pkru.h~pkru-stash-thread-value 2021-06-22 14:49:06.594051763 -0700 +++ b/arch/x86/include/asm/pkru.h 2021-06-22 14:49:06.607051763 -0700 @@ -44,11 +44,16 @@ static inline void write_pkru(u32 pkru) if (!cpu_feature_enabled(X86_FEATURE_OSPKE)) return; /* + * Update the actual register. + * * WRPKRU is relatively expensive compared to RDPKRU. * Avoid WRPKRU when it would not change the value. */ if (pkru != rdpkru()) wrpkru(pkru); + + /* Update the thread-local, context-switched value: */ + current->thread.pkru = pkru; } static inline void pkru_write_default(void) diff -puN arch/x86/kernel/cpu/common.c~pkru-stash-thread-value arch/x86/kernel/cpu/common.c --- a/arch/x86/kernel/cpu/common.c~pkru-stash-thread-value 2021-06-22 14:49:06.596051763 -0700 +++ b/arch/x86/kernel/cpu/common.c 2021-06-22 14:49:06.608051763 -0700 @@ -482,6 +482,9 @@ static __always_inline void setup_pku(st cr4_set_bits(X86_CR4_PKE); /* Load the default PKRU value */ pkru_write_default(); + + /* Establish the default value for future tasks: */ + init_task.thread.pkru = init_pkru_value; } #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS diff -puN arch/x86/kernel/process_64.c~pkru-stash-thread-value arch/x86/kernel/process_64.c --- a/arch/x86/kernel/process_64.c~pkru-stash-thread-value 2021-06-22 14:49:06.599051763 -0700 +++ b/arch/x86/kernel/process_64.c 2021-06-22 14:49:06.608051763 -0700 @@ -349,15 +349,14 @@ static __always_inline void load_seg_leg static __always_inline void x86_pkru_load(struct thread_struct *prev, struct thread_struct *next) { - if (!cpu_feature_enabled(X86_FEATURE_OSPKE)) - return; + u32 pkru = read_pkru(); /* Stash the prev task's value: */ - prev->pkru = rdpkru(); + prev->pkru = pkru; /* - * PKRU writes are slightly expensive. Avoid them when not - * strictly necessary: + * PKRU writes are slightly expensive. Avoid + * them when not strictly necessary: */ if (prev->pkru != next->pkru) wrpkru(next->pkru); diff -puN arch/x86/mm/pkeys.c~pkru-stash-thread-value arch/x86/mm/pkeys.c --- a/arch/x86/mm/pkeys.c~pkru-stash-thread-value 2021-06-22 14:49:06.604051763 -0700 +++ b/arch/x86/mm/pkeys.c 2021-06-22 14:49:06.609051763 -0700 @@ -159,6 +159,8 @@ static ssize_t init_pkru_write_file(stru return -EINVAL; WRITE_ONCE(init_pkru_value, new_init_pkru); + WRITE_ONCE(init_task.thread.pkru, new_init_pkru); + return count; } _