commit 148de78dc299b87ea3dcf13d73c92431f94643a0 Author: Florian Weimer Date: Thu Dec 14 11:12:02 2017 -0500 x86 pkeys: Use PKRU register of interrupted thread in signal handlers pkeys support for IBM POWER intends to inherited the access rights of the current thread in signal handlers. The advantage is that this preserves access to memory regions associated with non-default keys, enabling additional usage scenarios for memory protection keys which currently do not work due to the unconditional reset to the (configurable) default key in signal handlers. This change does not affect the init_pkru optimization because if the thread's PKRU register is zero due to the init_pkru setting, it will remain zero in the signal handler through inheritance. Signed-off-by: Florian Weimer diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h index a38bf5a..a87e99f7 100644 --- a/arch/x86/include/asm/fpu/internal.h +++ b/arch/x86/include/asm/fpu/internal.h @@ -33,6 +33,7 @@ extern void fpu__drop(struct fpu *fpu); extern int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu); extern void fpu__clear(struct fpu *fpu); +extern void fpu__clear_signal(struct fpu *fpu); extern int fpu__exception_code(struct fpu *fpu, int trap_nr); extern int dump_fpu(struct pt_regs *ptregs, struct user_i387_struct *fpstate); diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c index f92a659..a3b3048 100644 --- a/arch/x86/kernel/fpu/core.c +++ b/arch/x86/kernel/fpu/core.c @@ -370,21 +370,16 @@ static inline void copy_init_fpstate_to_fpregs(void) copy_kernel_to_fxregs(&init_fpstate.fxsave); else copy_kernel_to_fregs(&init_fpstate.fsave); - - if (boot_cpu_has(X86_FEATURE_OSPKE)) - copy_init_pkru_to_fpregs(); } -/* - * Clear the FPU state back to init state. - * - * Called by sys_execve(), by the signal handler code and by various - * error paths. - */ -void fpu__clear(struct fpu *fpu) +static void __fpu_clear(struct fpu *fpu, bool for_signal) { + u32 pkru; + WARN_ON_FPU(fpu != ¤t->thread.fpu); /* Almost certainly an anomaly */ + if (for_signal) + pkru = read_pkru(); fpu__drop(fpu); /* @@ -395,11 +390,43 @@ void fpu__clear(struct fpu *fpu) fpu__initialize(fpu); user_fpu_begin(); copy_init_fpstate_to_fpregs(); + if (boot_cpu_has(X86_FEATURE_OSPKE)) { + /* A signal handler inherits the original PKRU + * value of the interrupted thread. + */ + if (for_signal) + __write_pkru(pkru); + else + copy_init_pkru_to_fpregs(); + } preempt_enable(); } } /* + * Clear the FPU state back to init state. + * + * Called by sys_execve(), the signal handler return code, and by + * various error paths. + */ +void fpu__clear(struct fpu *fpu) +{ + return __fpu_clear(fpu, false); +} + +/* + * Prepare the FPU for invoking a signal handler. + * + * This is like fpu__clear(), but some CPU registers are inherited + * from the current thread and not restored to their initial values, + * to match behavior on other architectures. + */ +void fpu__clear_signal(struct fpu *fpu) +{ + return __fpu_clear(fpu, true); +} + +/* * x87 math exception handling: */ diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index b9e00e8..4263f18 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -757,7 +757,7 @@ static inline int is_x32_frame(struct ksignal *ksig) * Ensure the signal handler starts with the new fpu state. */ if (fpu->initialized) - fpu__clear(fpu); + fpu__clear_signal(fpu); } signal_setup_done(failed, ksig, stepping); }