]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
fixes for 4.14
authorSasha Levin <sashal@kernel.org>
Tue, 3 Sep 2019 04:56:24 +0000 (00:56 -0400)
committerSasha Levin <sashal@kernel.org>
Tue, 3 Sep 2019 04:56:24 +0000 (00:56 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.14/series
queue-4.14/uprobes-x86-fix-detection-of-32-bit-user-mode.patch [new file with mode: 0644]

index 3d008b1276f90284829a68a500a70d671f0c8c23..0b8ddcce021c401af6acee7876e48c49af208c2e 100644 (file)
@@ -25,3 +25,4 @@ kvm-x86-skip-populating-logical-dest-map-if-apic-is-not-sw-enabled.patch
 kvm-x86-don-t-update-rip-or-do-single-step-on-faulting-emulation.patch
 x86-apic-do-not-initialize-ldr-and-dfr-for-bigsmp.patch
 x86-apic-include-the-ldr-when-clearing-out-apic-registers.patch
+uprobes-x86-fix-detection-of-32-bit-user-mode.patch
diff --git a/queue-4.14/uprobes-x86-fix-detection-of-32-bit-user-mode.patch b/queue-4.14/uprobes-x86-fix-detection-of-32-bit-user-mode.patch
new file mode 100644 (file)
index 0000000..dadeabf
--- /dev/null
@@ -0,0 +1,130 @@
+From c63e9c2a9ecd90df7a073322a5d90ed9fad19196 Mon Sep 17 00:00:00 2001
+From: Sebastian Mayr <me@sam.st>
+Date: Sun, 28 Jul 2019 17:26:17 +0200
+Subject: uprobes/x86: Fix detection of 32-bit user mode
+
+[ Upstream commit 9212ec7d8357ea630031e89d0d399c761421c83b ]
+
+32-bit processes running on a 64-bit kernel are not always detected
+correctly, causing the process to crash when uretprobes are installed.
+
+The reason for the crash is that in_ia32_syscall() is used to determine the
+process's mode, which only works correctly when called from a syscall.
+
+In the case of uretprobes, however, the function is called from a exception
+and always returns 'false' on a 64-bit kernel. In consequence this leads to
+corruption of the process's return address.
+
+Fix this by using user_64bit_mode() instead of in_ia32_syscall(), which
+is correct in any situation.
+
+[ tglx: Add a comment and the following historical info ]
+
+This should have been detected by the rename which happened in commit
+
+  abfb9498ee13 ("x86/entry: Rename is_{ia32,x32}_task() to in_{ia32,x32}_syscall()")
+
+which states in the changelog:
+
+    The is_ia32_task()/is_x32_task() function names are a big misnomer: they
+    suggests that the compat-ness of a system call is a task property, which
+    is not true, the compatness of a system call purely depends on how it
+    was invoked through the system call layer.
+    .....
+
+and then it went and blindly renamed every call site.
+
+Sadly enough this was already mentioned here:
+
+   8faaed1b9f50 ("uprobes/x86: Introduce sizeof_long(), cleanup adjust_ret_addr() and
+arch_uretprobe_hijack_return_addr()")
+
+where the changelog says:
+
+    TODO: is_ia32_task() is not what we actually want, TS_COMPAT does
+    not necessarily mean 32bit. Fortunately syscall-like insns can't be
+    probed so it actually works, but it would be better to rename and
+    use is_ia32_frame().
+
+and goes all the way back to:
+
+    0326f5a94dde ("uprobes/core: Handle breakpoint and singlestep exceptions")
+
+Oh well. 7+ years until someone actually tried a uretprobe on a 32bit
+process on a 64bit kernel....
+
+Fixes: 0326f5a94dde ("uprobes/core: Handle breakpoint and singlestep exceptions")
+Signed-off-by: Sebastian Mayr <me@sam.st>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: Masami Hiramatsu <mhiramat@kernel.org>
+Cc: Dmitry Safonov <dsafonov@virtuozzo.com>
+Cc: Oleg Nesterov <oleg@redhat.com>
+Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
+Cc: stable@vger.kernel.org
+Link: https://lkml.kernel.org/r/20190728152617.7308-1-me@sam.st
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/uprobes.c | 17 ++++++++++-------
+ 1 file changed, 10 insertions(+), 7 deletions(-)
+
+diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
+index b9a8f34b5e5ab..7a87ef1f5b5e6 100644
+--- a/arch/x86/kernel/uprobes.c
++++ b/arch/x86/kernel/uprobes.c
+@@ -518,9 +518,12 @@ struct uprobe_xol_ops {
+       void    (*abort)(struct arch_uprobe *, struct pt_regs *);
+ };
+-static inline int sizeof_long(void)
++static inline int sizeof_long(struct pt_regs *regs)
+ {
+-      return in_ia32_syscall() ? 4 : 8;
++      /*
++       * Check registers for mode as in_xxx_syscall() does not apply here.
++       */
++      return user_64bit_mode(regs) ? 8 : 4;
+ }
+ static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+@@ -531,9 +534,9 @@ static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+ static int push_ret_address(struct pt_regs *regs, unsigned long ip)
+ {
+-      unsigned long new_sp = regs->sp - sizeof_long();
++      unsigned long new_sp = regs->sp - sizeof_long(regs);
+-      if (copy_to_user((void __user *)new_sp, &ip, sizeof_long()))
++      if (copy_to_user((void __user *)new_sp, &ip, sizeof_long(regs)))
+               return -EFAULT;
+       regs->sp = new_sp;
+@@ -566,7 +569,7 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs
+               long correction = utask->vaddr - utask->xol_vaddr;
+               regs->ip += correction;
+       } else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
+-              regs->sp += sizeof_long(); /* Pop incorrect return address */
++              regs->sp += sizeof_long(regs); /* Pop incorrect return address */
+               if (push_ret_address(regs, utask->vaddr + auprobe->defparam.ilen))
+                       return -ERESTART;
+       }
+@@ -675,7 +678,7 @@ static int branch_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
+        * "call" insn was executed out-of-line. Just restore ->sp and restart.
+        * We could also restore ->ip and try to call branch_emulate_op() again.
+        */
+-      regs->sp += sizeof_long();
++      regs->sp += sizeof_long(regs);
+       return -ERESTART;
+ }
+@@ -966,7 +969,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+ unsigned long
+ arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs)
+ {
+-      int rasize = sizeof_long(), nleft;
++      int rasize = sizeof_long(regs), nleft;
+       unsigned long orig_ret_vaddr = 0; /* clear high bits for 32-bit apps */
+       if (copy_from_user(&orig_ret_vaddr, (void __user *)regs->sp, rasize))
+-- 
+2.20.1
+