diff options
Diffstat (limited to 'debian/patches-rt/0028-x86-fpu-Fault-in-user-stack-if-copy_fpstate_to_sigfr.patch')
-rw-r--r-- | debian/patches-rt/0028-x86-fpu-Fault-in-user-stack-if-copy_fpstate_to_sigfr.patch | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/debian/patches-rt/0028-x86-fpu-Fault-in-user-stack-if-copy_fpstate_to_sigfr.patch b/debian/patches-rt/0028-x86-fpu-Fault-in-user-stack-if-copy_fpstate_to_sigfr.patch new file mode 100644 index 000000000..5e3e058c0 --- /dev/null +++ b/debian/patches-rt/0028-x86-fpu-Fault-in-user-stack-if-copy_fpstate_to_sigfr.patch @@ -0,0 +1,105 @@ +From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Date: Mon, 29 Apr 2019 18:39:53 +0200 +Subject: [PATCH] x86/fpu: Fault-in user stack if copy_fpstate_to_sigframe() + fails +Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.0/older/patches-5.0.10-rt7.tar.xz + +In the compacted form, XSAVES may save only the XMM+SSE state but skip +FP (x87 state). + +This is denoted by header->xfeatures = 6. The fastpath +(copy_fpregs_to_sigframe()) does that but _also_ initialises the FP +state (cwd to 0x37f, mxcsr as we do, remaining fields to 0). + +The slowpath (copy_xstate_to_user()) leaves most of the FP +state untouched. Only mxcsr and mxcsr_flags are set due to +xfeatures_mxcsr_quirk(). Now that XFEATURE_MASK_FP is set +unconditionally, see + + 04944b793e18 ("x86: xsave: set FP, SSE bits in the xsave header in the user sigcontext"), + +on return from the signal, random garbage is loaded as the FP state. + +Instead of utilizing copy_xstate_to_user(), fault-in the user memory +and retry the fast path. Ideally, the fast path succeeds on the second +attempt but may be retried again if the memory is swapped out due +to memory pressure. If the user memory can not be faulted-in then +get_user_pages() returns an error so we don't loop forever. + +Fault in memory via get_user_pages() so copy_fpregs_to_sigframe() +succeeds without a fault. + +Fixes: 69277c98f5eef ("x86/fpu: Always store the registers in copy_fpstate_to_sigframe()") +Reported-by: Kurt Kanzenbach <kurt.kanzenbach@linutronix.de> +Suggested-by: Dave Hansen <dave.hansen@intel.com> +Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> +Signed-off-by: Borislav Petkov <bp@suse.de> +Acked-by: Thomas Gleixner <tglx@linutronix.de> +Cc: Andy Lutomirski <luto@amacapital.net> +Cc: Dave Hansen <dave.hansen@intel.com> +Cc: "H. Peter Anvin" <hpa@zytor.com> +Cc: Ingo Molnar <mingo@kernel.org> +Cc: Jann Horn <jannh@google.com> +Cc: Jason@zx2c4.com +Cc: kvm ML <kvm@vger.kernel.org> +Cc: Paolo Bonzini <pbonzini@redhat.com> +Cc: Rik van Riel <riel@surriel.com> +Cc: rkrcmar@redhat.com +Cc: x86-ml <x86@kernel.org> +Link: https://lkml.kernel.org/r/20190429163953.gqxgsc5okqxp4olv@linutronix.de +--- + arch/x86/kernel/fpu/signal.c | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +--- a/arch/x86/kernel/fpu/signal.c ++++ b/arch/x86/kernel/fpu/signal.c +@@ -158,7 +158,6 @@ static inline int copy_fpregs_to_sigfram + int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size) + { + struct fpu *fpu = ¤t->thread.fpu; +- struct xregs_state *xsave = &fpu->state.xsave; + struct task_struct *tsk = current; + int ia32_fxstate = (buf != buf_fx); + int ret = -EFAULT; +@@ -174,12 +173,13 @@ int copy_fpstate_to_sigframe(void __user + sizeof(struct user_i387_ia32_struct), NULL, + (struct _fpstate_32 __user *) buf) ? -1 : 1; + ++retry: + fpregs_lock(); + /* + * Load the FPU register if they are not valid for the current task. + * With a valid FPU state we can attempt to save the state directly to +- * userland's stack frame which will likely succeed. If it does not, do +- * the slowpath. ++ * userland's stack frame which will likely succeed. If it does not, ++ * resolve the fault in the user memory and try again. + */ + if (test_thread_flag(TIF_NEED_FPU_LOAD)) + __fpregs_load_activate(); +@@ -193,14 +193,17 @@ int copy_fpstate_to_sigframe(void __user + fpregs_unlock(); + + if (ret) { +- if (using_compacted_format()) { +- if (copy_xstate_to_user(buf_fx, xsave, 0, size)) +- return -1; +- } else { +- fpstate_sanitize_xstate(fpu); +- if (__copy_to_user(buf_fx, xsave, fpu_user_xstate_size)) +- return -1; +- } ++ int aligned_size; ++ int nr_pages; ++ ++ aligned_size = offset_in_page(buf_fx) + fpu_user_xstate_size; ++ nr_pages = DIV_ROUND_UP(aligned_size, PAGE_SIZE); ++ ++ ret = get_user_pages((unsigned long)buf_fx, nr_pages, ++ FOLL_WRITE, NULL, NULL); ++ if (ret == nr_pages) ++ goto retry; ++ return -EFAULT; + } + + /* Save the fsave header for the 32-bit frames. */ |