|
@@ -44,6 +44,23 @@ static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */
|
|
|
|
|
|
#define UPROBES_HASH_SZ 13
|
|
|
|
|
|
+/*
|
|
|
+ * We need separate register/unregister and mmap/munmap lock hashes because
|
|
|
+ * of mmap_sem nesting.
|
|
|
+ *
|
|
|
+ * uprobe_register() needs to install probes on (potentially) all processes
|
|
|
+ * and thus needs to acquire multiple mmap_sems (consequtively, not
|
|
|
+ * concurrently), whereas uprobe_mmap() is called while holding mmap_sem
|
|
|
+ * for the particular process doing the mmap.
|
|
|
+ *
|
|
|
+ * uprobe_register()->register_for_each_vma() needs to drop/acquire mmap_sem
|
|
|
+ * because of lock order against i_mmap_mutex. This means there's a hole in
|
|
|
+ * the register vma iteration where a mmap() can happen.
|
|
|
+ *
|
|
|
+ * Thus uprobe_register() can race with uprobe_mmap() and we can try and
|
|
|
+ * install a probe where one is already installed.
|
|
|
+ */
|
|
|
+
|
|
|
/* serialize (un)register */
|
|
|
static struct mutex uprobes_mutex[UPROBES_HASH_SZ];
|
|
|
|
|
@@ -339,7 +356,9 @@ out:
|
|
|
int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
|
|
|
{
|
|
|
int result;
|
|
|
-
|
|
|
+ /*
|
|
|
+ * See the comment near uprobes_hash().
|
|
|
+ */
|
|
|
result = is_swbp_at_addr(mm, vaddr);
|
|
|
if (result == 1)
|
|
|
return -EEXIST;
|
|
@@ -845,6 +864,10 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
|
|
|
|
|
|
if (is_register) {
|
|
|
err = install_breakpoint(uprobe, mm, vma, info->vaddr);
|
|
|
+ /*
|
|
|
+ * We can race against uprobe_mmap(), see the
|
|
|
+ * comment near uprobe_hash().
|
|
|
+ */
|
|
|
if (err == -EEXIST)
|
|
|
err = 0;
|
|
|
} else {
|
|
@@ -1054,8 +1077,10 @@ int uprobe_mmap(struct vm_area_struct *vma)
|
|
|
}
|
|
|
|
|
|
ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr);
|
|
|
-
|
|
|
- /* Ignore double add: */
|
|
|
+ /*
|
|
|
+ * We can race against uprobe_register(), see the
|
|
|
+ * comment near uprobe_hash().
|
|
|
+ */
|
|
|
if (ret == -EEXIST) {
|
|
|
ret = 0;
|
|
|
|