--- /dev/null
+From 013b2deba9a6b80ca02f4fafd7dedf875e9b4450 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Mon, 4 May 2020 18:47:25 +0200
+Subject: uprobes: ensure that uprobe->offset and ->ref_ctr_offset are properly aligned
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+commit 013b2deba9a6b80ca02f4fafd7dedf875e9b4450 upstream.
+
+uprobe_write_opcode() must not cross page boundary; prepare_uprobe()
+relies on arch_uprobe_analyze_insn() which should validate "vaddr" but
+some architectures (csky, s390, and sparc) don't do this.
+
+We can remove the BUG_ON() check in prepare_uprobe() and validate the
+offset early in __uprobe_register(). The new IS_ALIGNED() check matches
+the alignment check in arch_prepare_kprobe() on supported architectures,
+so I think that all insns must be aligned to UPROBE_SWBP_INSN_SIZE.
+
+Another problem is __update_ref_ctr() which was wrong from the very
+beginning, it can read/write outside of kmap'ed page unless "vaddr" is
+aligned to sizeof(short), __uprobe_register() should check this too.
+
+Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
+Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Reviewed-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
+Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
+Tested-by: Sven Schnelle <svens@linux.ibm.com>
+Cc: Steven Rostedt <rostedt@goodmis.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/events/uprobes.c | 16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+--- a/kernel/events/uprobes.c
++++ b/kernel/events/uprobes.c
+@@ -602,10 +602,6 @@ static int prepare_uprobe(struct uprobe
+ if (ret)
+ goto out;
+
+- /* uprobe_write_opcode() assumes we don't cross page boundary */
+- BUG_ON((uprobe->offset & ~PAGE_MASK) +
+- UPROBE_SWBP_INSN_SIZE > PAGE_SIZE);
+-
+ smp_wmb(); /* pairs with the smp_rmb() in handle_swbp() */
+ set_bit(UPROBE_COPY_INSN, &uprobe->flags);
+
+@@ -884,6 +880,15 @@ int uprobe_register(struct inode *inode,
+ if (offset > i_size_read(inode))
+ return -EINVAL;
+
++ /*
++ * This ensures that copy_from_page(), copy_to_page() and
++ * __update_ref_ctr() can't cross page boundary.
++ */
++ if (!IS_ALIGNED(offset, UPROBE_SWBP_INSN_SIZE))
++ return -EINVAL;
++ if (!IS_ALIGNED(ref_ctr_offset, sizeof(short)))
++ return -EINVAL;
++
+ retry:
+ uprobe = alloc_uprobe(inode, offset);
+ if (!uprobe)
+@@ -1692,6 +1697,9 @@ static int is_trap_at_addr(struct mm_str
+ uprobe_opcode_t opcode;
+ int result;
+
++ if (WARN_ON_ONCE(!IS_ALIGNED(vaddr, UPROBE_SWBP_INSN_SIZE)))
++ return -EINVAL;
++
+ pagefault_disable();
+ result = __copy_from_user_inatomic(&opcode, (void __user*)vaddr,
+ sizeof(opcode));