diff options
author | Oleg Nesterov <oleg@redhat.com> | 2012-09-30 12:54:53 -0400 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2012-10-07 15:19:41 -0400 |
commit | 076a365b3da99b68c5d58e394714d0611f1fa002 (patch) | |
tree | b58ae7da7bfa80200bb9982f344b93cf3f22a7fd | |
parent | a5f658b71bc622b731961ea3addcf146ed3c599f (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.c | 12 |
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 | ||
619 | static void | 619 | static int |
620 | remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) | 620 | remove_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); |