summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0028-x86-fpu-Fault-in-user-stack-if-copy_fpstate_to_sigfr.patch
diff options
context:
space:
mode:
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.patch105
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 = &current->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. */