summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2012-09-23 21:30:44 +0200
committerAnton Arapov <anton@redhat.com>2012-10-29 11:47:46 +0100
commit7240b0266ebd54a504900fe55f5ae272a73cfa07 (patch)
tree1f04d2f018bb6653e0f12821f9995ff5e975359b
parent6161b3a82c9f3553a36bf82ad79c6c06599c03ef (diff)
downloadkernel-uprobes-7240b0266ebd54a504900fe55f5ae272a73cfa07.zip
kernel-uprobes-7240b0266ebd54a504900fe55f5ae272a73cfa07.tar.gz
kernel-uprobes-7240b0266ebd54a504900fe55f5ae272a73cfa07.tar.xz
uprobes: Kill set_orig_insn()->is_swbp_at_addr()
Unlike set_swbp(), set_orig_insn()->is_swbp_at_addr() makes sense, although it can't prevent all confusions. But the usage of is_swbp_at_addr() is equally confusing, and it adds the extra get_user_pages() we can avoid. This patch removes set_orig_insn()->is_swbp_at_addr() but changes write_opcode() to do the necessary checks before replace_page(). Perhaps it also makes sense to ensure PAGE_MAPPING_ANON in unregister case. find_active_uprobe() becomes the only user of is_swbp_at_addr(), we can change its semantics. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
-rw-r--r--kernel/events/uprobes.c32
1 files changed, 23 insertions, 9 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index b6f0f71..9248ee7 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -190,6 +190,25 @@ static void copy_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
kunmap_atomic(kaddr);
}
+static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t *new_opcode)
+{
+ uprobe_opcode_t old_opcode;
+ bool is_swbp;
+
+ copy_opcode(page, vaddr, &old_opcode);
+ is_swbp = is_swbp_insn(&old_opcode);
+
+ if (is_swbp_insn(new_opcode)) {
+ if (is_swbp) /* register: already installed? */
+ return 0;
+ } else {
+ if (!is_swbp) /* unregister: was it changed by us? */
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
/*
* NOTE:
* Expect the breakpoint instruction to be the smallest size instruction for
@@ -226,6 +245,10 @@ retry:
if (ret <= 0)
return ret;
+ ret = verify_opcode(old_page, vaddr, &opcode);
+ if (ret <= 0)
+ goto put_old;
+
ret = -ENOMEM;
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
if (!new_page)
@@ -311,15 +334,6 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
int __weak
set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
{
- int result;
-
- result = is_swbp_at_addr(mm, vaddr);
- if (!result)
- return -EINVAL;
-
- if (result != 1)
- return result;
-
return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
}