aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2012-09-30 12:54:53 -0400
committerOleg Nesterov <oleg@redhat.com>2012-10-07 15:19:41 -0400
commit076a365b3da99b68c5d58e394714d0611f1fa002 (patch)
treeb58ae7da7bfa80200bb9982f344b93cf3f22a7fd
parenta5f658b71bc622b731961ea3addcf146ed3c599f (diff)
uprobes: Do not delete uprobe if uprobe_unregister() fails
delete_uprobe() must not be called if register_for_each_vma(false) fails to remove all breakpoints, __uprobe_unregister() is correct. The problem is that register_for_each_vma(false) always returns 0 and thus this logic does not work. 1. Change verify_opcode() to return 0 rather than -EINVAL when unregister detects the !is_swbp insn, we can treat this case as success and currently unregister paths ignore the error code anyway. 2. Change remove_breakpoint() to propagate the error code from write_opcode(). 3. Change register_for_each_vma(is_register => false) to remove as much breakpoints as possible but return non-zero if remove_breakpoint() fails at least once. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
-rw-r--r--kernel/events/uprobes.c12
1 files changed, 6 insertions, 6 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 588a5575d64c..a1b466d17c17 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -203,7 +203,7 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
203 return 0; 203 return 0;
204 } else { 204 } else {
205 if (!is_swbp) /* unregister: was it changed by us? */ 205 if (!is_swbp) /* unregister: was it changed by us? */
206 return -EINVAL; 206 return 0;
207 } 207 }
208 208
209 return 1; 209 return 1;
@@ -616,15 +616,15 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
616 return ret; 616 return ret;
617} 617}
618 618
619static void 619static int
620remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) 620remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr)
621{ 621{
622 /* can happen if uprobe_register() fails */ 622 /* can happen if uprobe_register() fails */
623 if (!test_bit(MMF_HAS_UPROBES, &mm->flags)) 623 if (!test_bit(MMF_HAS_UPROBES, &mm->flags))
624 return; 624 return 0;
625 625
626 set_bit(MMF_RECALC_UPROBES, &mm->flags); 626 set_bit(MMF_RECALC_UPROBES, &mm->flags);
627 set_orig_insn(&uprobe->arch, mm, vaddr); 627 return set_orig_insn(&uprobe->arch, mm, vaddr);
628} 628}
629 629
630/* 630/*
@@ -740,7 +740,7 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
740 struct mm_struct *mm = info->mm; 740 struct mm_struct *mm = info->mm;
741 struct vm_area_struct *vma; 741 struct vm_area_struct *vma;
742 742
743 if (err) 743 if (err && is_register)
744 goto free; 744 goto free;
745 745
746 down_write(&mm->mmap_sem); 746 down_write(&mm->mmap_sem);
@@ -756,7 +756,7 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
756 if (is_register) 756 if (is_register)
757 err = install_breakpoint(uprobe, mm, vma, info->vaddr); 757 err = install_breakpoint(uprobe, mm, vma, info->vaddr);
758 else 758 else
759 remove_breakpoint(uprobe, mm, info->vaddr); 759 err |= remove_breakpoint(uprobe, mm, info->vaddr);
760 760
761 unlock: 761 unlock:
762 up_write(&mm->mmap_sem); 762 up_write(&mm->mmap_sem);