aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2012-08-06 07:15:09 -0400
committerOleg Nesterov <oleg@redhat.com>2012-08-28 12:21:16 -0400
commit647c42dfd40fec032a4c8525a755160f0765921f (patch)
treef8bcb634d5cbebcc2168d6834d8a3a27d221c062 /kernel/events
parent8bd874456e2ec49b9e64372ddc89a6f88901d184 (diff)
uprobes: Kill uprobes_state->count
uprobes_state->count is only needed to avoid the slow path in uprobe_pre_sstep_notifier(). It is also checked in uprobe_munmap() but ironically its only goal to decrement this counter. However, it is very broken. Just some examples: - uprobe_mmap() can race with uprobe_unregister() and wrongly increment the counter if it hits the non-uprobe "int3". Note that install_breakpoint() checks ->consumers first and returns -EEXIST if it is NULL. "atomic_sub() if error" in uprobe_mmap() looks obviously wrong too. - uprobe_munmap() can race with uprobe_register() and wrongly decrement the counter by the same reason. - Suppose an appication tries to increase the mmapped area via sys_mremap(). vma_adjust() does uprobe_munmap(whole_vma) first, this can nullify the counter temporarily and race with another thread which can hit the bp, the application will be killed by SIGTRAP. - Suppose an application mmaps 2 consecutive areas in the same file and one (or both) of these areas has uprobes. In the likely case mmap_region()->vma_merge() suceeds. Like above, this leads to uprobe_munmap/uprobe_mmap from vma_merge()->vma_adjust() but then mmap_region() does another uprobe_mmap(resulting_vma) and doubles the counter. This patch only removes this counter and fixes the compile errors, then we will try to cleanup the changed code and add something else instead. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/events')
-rw-r--r--kernel/events/uprobes.c38
1 files changed, 2 insertions, 36 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 0cefde276641..6f1664d217dc 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -678,18 +678,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
678 uprobe->flags |= UPROBE_COPY_INSN; 678 uprobe->flags |= UPROBE_COPY_INSN;
679 } 679 }
680 680
681 /*
682 * Ideally, should be updating the probe count after the breakpoint
683 * has been successfully inserted. However a thread could hit the
684 * breakpoint we just inserted even before the probe count is
685 * incremented. If this is the first breakpoint placed, breakpoint
686 * notifier might ignore uprobes and pass the trap to the thread.
687 * Hence increment before and decrement on failure.
688 */
689 atomic_inc(&mm->uprobes_state.count);
690 ret = set_swbp(&uprobe->arch, mm, vaddr); 681 ret = set_swbp(&uprobe->arch, mm, vaddr);
691 if (ret)
692 atomic_dec(&mm->uprobes_state.count);
693 682
694 return ret; 683 return ret;
695} 684}
@@ -697,8 +686,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
697static void 686static void
698remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr) 687remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr)
699{ 688{
700 if (!set_orig_insn(&uprobe->arch, mm, vaddr, true)) 689 set_orig_insn(&uprobe->arch, mm, vaddr, true);
701 atomic_dec(&mm->uprobes_state.count);
702} 690}
703 691
704/* 692/*
@@ -1051,13 +1039,6 @@ int uprobe_mmap(struct vm_area_struct *vma)
1051 1039
1052 if (!is_swbp_at_addr(vma->vm_mm, vaddr)) 1040 if (!is_swbp_at_addr(vma->vm_mm, vaddr))
1053 continue; 1041 continue;
1054
1055 /*
1056 * Unable to insert a breakpoint, but
1057 * breakpoint lies underneath. Increment the
1058 * probe count.
1059 */
1060 atomic_inc(&vma->vm_mm->uprobes_state.count);
1061 } 1042 }
1062 1043
1063 if (!ret) 1044 if (!ret)
@@ -1068,9 +1049,6 @@ int uprobe_mmap(struct vm_area_struct *vma)
1068 1049
1069 mutex_unlock(uprobes_mmap_hash(inode)); 1050 mutex_unlock(uprobes_mmap_hash(inode));
1070 1051
1071 if (ret)
1072 atomic_sub(count, &vma->vm_mm->uprobes_state.count);
1073
1074 return ret; 1052 return ret;
1075} 1053}
1076 1054
@@ -1089,9 +1067,6 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon
1089 if (!atomic_read(&vma->vm_mm->mm_users)) /* called by mmput() ? */ 1067 if (!atomic_read(&vma->vm_mm->mm_users)) /* called by mmput() ? */
1090 return; 1068 return;
1091 1069
1092 if (!atomic_read(&vma->vm_mm->uprobes_state.count))
1093 return;
1094
1095 inode = vma->vm_file->f_mapping->host; 1070 inode = vma->vm_file->f_mapping->host;
1096 if (!inode) 1071 if (!inode)
1097 return; 1072 return;
@@ -1100,13 +1075,6 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon
1100 build_probe_list(inode, vma, start, end, &tmp_list); 1075 build_probe_list(inode, vma, start, end, &tmp_list);
1101 1076
1102 list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { 1077 list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
1103 unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset);
1104 /*
1105 * An unregister could have removed the probe before
1106 * unmap. So check before we decrement the count.
1107 */
1108 if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1)
1109 atomic_dec(&vma->vm_mm->uprobes_state.count);
1110 put_uprobe(uprobe); 1078 put_uprobe(uprobe);
1111 } 1079 }
1112 mutex_unlock(uprobes_mmap_hash(inode)); 1080 mutex_unlock(uprobes_mmap_hash(inode));
@@ -1217,7 +1185,6 @@ void uprobe_clear_state(struct mm_struct *mm)
1217void uprobe_reset_state(struct mm_struct *mm) 1185void uprobe_reset_state(struct mm_struct *mm)
1218{ 1186{
1219 mm->uprobes_state.xol_area = NULL; 1187 mm->uprobes_state.xol_area = NULL;
1220 atomic_set(&mm->uprobes_state.count, 0);
1221} 1188}
1222 1189
1223/* 1190/*
@@ -1585,8 +1552,7 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs)
1585{ 1552{
1586 struct uprobe_task *utask; 1553 struct uprobe_task *utask;
1587 1554
1588 if (!current->mm || !atomic_read(&current->mm->uprobes_state.count)) 1555 if (!current->mm)
1589 /* task is currently not uprobed */
1590 return 0; 1556 return 0;
1591 1557
1592 utask = current->utask; 1558 utask = current->utask;