summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2012-09-29 21:56:57 +0200
committerAnton Arapov <anton@redhat.com>2012-10-29 11:47:49 +0100
commit05b2fce312e33f1ea0fa5b47992ad8302ffc4dd7 (patch)
tree30f8ff7a43ad2507daa37889d67a41e3e60d10ca
parentd249a061ebaefc9a61103824eaffa5eb2286d3c5 (diff)
downloadkernel-uprobes-05b2fce312e33f1ea0fa5b47992ad8302ffc4dd7.zip
kernel-uprobes-05b2fce312e33f1ea0fa5b47992ad8302ffc4dd7.tar.gz
kernel-uprobes-05b2fce312e33f1ea0fa5b47992ad8302ffc4dd7.tar.xz
uprobes: Fix handle_swbp() vs unregister() + register() race
Strictly speaking this race was added by me in 56bb4cf6. However I think that this bug is just another indication that we should move copy_insn/uprobe_analyze_insn code from install_breakpoint() to uprobe_register(), there are a lot of other reasons for that. Until then, add a hack to close the race. A task can hit uprobe U1, but before it calls find_uprobe() this uprobe can be unregistered *AND* another uprobe U2 can be added to uprobes_tree at the same inode/offset. In this case handle_swbp() will use the not-fully-initialized U2, in particular its arch.insn for xol. Add the additional !UPROBE_COPY_INSN check into handle_swbp(), if this flag is not set we simply restart as if the new uprobe was not inserted yet. This is not very nice, we need barriers, but we will remove this hack when we change uprobe_register(). Note: with or without this patch install_breakpoint() can race with itself, yet another reson to kill UPROBE_COPY_INSN altogether. And even the usage of uprobe->flags is not safe. See the next patches. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
-rw-r--r--kernel/events/uprobes.c9
1 files changed, 9 insertions, 0 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index a1b466d..c718fef 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -596,6 +596,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
BUG_ON((uprobe->offset & ~PAGE_MASK) +
UPROBE_SWBP_INSN_SIZE > PAGE_SIZE);
+ smp_wmb(); /* pairs with rmb() in find_active_uprobe() */
uprobe->flags |= UPROBE_COPY_INSN;
}
@@ -1436,6 +1437,14 @@ static void handle_swbp(struct pt_regs *regs)
}
return;
}
+ /*
+ * TODO: move copy_insn/etc into _register and remove this hack.
+ * After we hit the bp, _unregister + _register can install the
+ * new and not-yet-analyzed uprobe at the same address, restart.
+ */
+ smp_rmb(); /* pairs with wmb() in install_breakpoint() */
+ if (unlikely(!(uprobe->flags & UPROBE_COPY_INSN)))
+ goto restart;
utask = current->utask;
if (!utask) {