diff options
-rw-r--r-- | arch/powerpc/kernel/signal.c | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/uprobes.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/uprobes.c | 54 | ||||
-rw-r--r-- | include/linux/uprobes.h | 10 | ||||
-rw-r--r-- | kernel/events/uprobes.c | 43 | ||||
-rw-r--r-- | kernel/fork.c | 2 | ||||
-rw-r--r-- | kernel/trace/trace_uprobe.c | 2 |
7 files changed, 65 insertions, 56 deletions
diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index a2dc75793bd5..3b997118df50 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c | |||
@@ -158,10 +158,8 @@ static int do_signal(struct pt_regs *regs) | |||
158 | 158 | ||
159 | void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) | 159 | void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags) |
160 | { | 160 | { |
161 | if (thread_info_flags & _TIF_UPROBE) { | 161 | if (thread_info_flags & _TIF_UPROBE) |
162 | clear_thread_flag(TIF_UPROBE); | ||
163 | uprobe_notify_resume(regs); | 162 | uprobe_notify_resume(regs); |
164 | } | ||
165 | 163 | ||
166 | if (thread_info_flags & _TIF_SIGPENDING) | 164 | if (thread_info_flags & _TIF_SIGPENDING) |
167 | do_signal(regs); | 165 | do_signal(regs); |
diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c index d2d46d1014f8..bc77834dbf43 100644 --- a/arch/powerpc/kernel/uprobes.c +++ b/arch/powerpc/kernel/uprobes.c | |||
@@ -64,6 +64,8 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
64 | autask->saved_trap_nr = current->thread.trap_nr; | 64 | autask->saved_trap_nr = current->thread.trap_nr; |
65 | current->thread.trap_nr = UPROBE_TRAP_NR; | 65 | current->thread.trap_nr = UPROBE_TRAP_NR; |
66 | regs->nip = current->utask->xol_vaddr; | 66 | regs->nip = current->utask->xol_vaddr; |
67 | |||
68 | user_enable_single_step(current); | ||
67 | return 0; | 69 | return 0; |
68 | } | 70 | } |
69 | 71 | ||
@@ -119,6 +121,8 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
119 | * to be executed. | 121 | * to be executed. |
120 | */ | 122 | */ |
121 | regs->nip = utask->vaddr + MAX_UINSN_BYTES; | 123 | regs->nip = utask->vaddr + MAX_UINSN_BYTES; |
124 | |||
125 | user_disable_single_step(current); | ||
122 | return 0; | 126 | return 0; |
123 | } | 127 | } |
124 | 128 | ||
@@ -162,6 +166,8 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
162 | 166 | ||
163 | current->thread.trap_nr = utask->autask.saved_trap_nr; | 167 | current->thread.trap_nr = utask->autask.saved_trap_nr; |
164 | instruction_pointer_set(regs, utask->vaddr); | 168 | instruction_pointer_set(regs, utask->vaddr); |
169 | |||
170 | user_disable_single_step(current); | ||
165 | } | 171 | } |
166 | 172 | ||
167 | /* | 173 | /* |
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index aafa5557b396..c71025b67462 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c | |||
@@ -478,6 +478,11 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
478 | regs->ip = current->utask->xol_vaddr; | 478 | regs->ip = current->utask->xol_vaddr; |
479 | pre_xol_rip_insn(auprobe, regs, autask); | 479 | pre_xol_rip_insn(auprobe, regs, autask); |
480 | 480 | ||
481 | autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF); | ||
482 | regs->flags |= X86_EFLAGS_TF; | ||
483 | if (test_tsk_thread_flag(current, TIF_BLOCKSTEP)) | ||
484 | set_task_blockstep(current, false); | ||
485 | |||
481 | return 0; | 486 | return 0; |
482 | } | 487 | } |
483 | 488 | ||
@@ -603,6 +608,16 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
603 | if (auprobe->fixups & UPROBE_FIX_CALL) | 608 | if (auprobe->fixups & UPROBE_FIX_CALL) |
604 | result = adjust_ret_addr(regs->sp, correction); | 609 | result = adjust_ret_addr(regs->sp, correction); |
605 | 610 | ||
611 | /* | ||
612 | * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP | ||
613 | * so we can get an extra SIGTRAP if we do not clear TF. We need | ||
614 | * to examine the opcode to make it right. | ||
615 | */ | ||
616 | if (utask->autask.saved_tf) | ||
617 | send_sig(SIGTRAP, current, 0); | ||
618 | else if (!(auprobe->fixups & UPROBE_FIX_SETF)) | ||
619 | regs->flags &= ~X86_EFLAGS_TF; | ||
620 | |||
606 | return result; | 621 | return result; |
607 | } | 622 | } |
608 | 623 | ||
@@ -647,6 +662,10 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
647 | current->thread.trap_nr = utask->autask.saved_trap_nr; | 662 | current->thread.trap_nr = utask->autask.saved_trap_nr; |
648 | handle_riprel_post_xol(auprobe, regs, NULL); | 663 | handle_riprel_post_xol(auprobe, regs, NULL); |
649 | instruction_pointer_set(regs, utask->vaddr); | 664 | instruction_pointer_set(regs, utask->vaddr); |
665 | |||
666 | /* clear TF if it was set by us in arch_uprobe_pre_xol() */ | ||
667 | if (!utask->autask.saved_tf) | ||
668 | regs->flags &= ~X86_EFLAGS_TF; | ||
650 | } | 669 | } |
651 | 670 | ||
652 | /* | 671 | /* |
@@ -676,38 +695,3 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
676 | send_sig(SIGTRAP, current, 0); | 695 | send_sig(SIGTRAP, current, 0); |
677 | return ret; | 696 | return ret; |
678 | } | 697 | } |
679 | |||
680 | void arch_uprobe_enable_step(struct arch_uprobe *auprobe) | ||
681 | { | ||
682 | struct task_struct *task = current; | ||
683 | struct arch_uprobe_task *autask = &task->utask->autask; | ||
684 | struct pt_regs *regs = task_pt_regs(task); | ||
685 | |||
686 | autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF); | ||
687 | |||
688 | regs->flags |= X86_EFLAGS_TF; | ||
689 | if (test_tsk_thread_flag(task, TIF_BLOCKSTEP)) | ||
690 | set_task_blockstep(task, false); | ||
691 | } | ||
692 | |||
693 | void arch_uprobe_disable_step(struct arch_uprobe *auprobe) | ||
694 | { | ||
695 | struct task_struct *task = current; | ||
696 | struct arch_uprobe_task *autask = &task->utask->autask; | ||
697 | bool trapped = (task->utask->state == UTASK_SSTEP_TRAPPED); | ||
698 | struct pt_regs *regs = task_pt_regs(task); | ||
699 | /* | ||
700 | * The state of TIF_BLOCKSTEP was not saved so we can get an extra | ||
701 | * SIGTRAP if we do not clear TF. We need to examine the opcode to | ||
702 | * make it right. | ||
703 | */ | ||
704 | if (unlikely(trapped)) { | ||
705 | if (!autask->saved_tf) | ||
706 | regs->flags &= ~X86_EFLAGS_TF; | ||
707 | } else { | ||
708 | if (autask->saved_tf) | ||
709 | send_sig(SIGTRAP, task, 0); | ||
710 | else if (!(auprobe->fixups & UPROBE_FIX_SETF)) | ||
711 | regs->flags &= ~X86_EFLAGS_TF; | ||
712 | } | ||
713 | } | ||
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 24594571c5a3..4f628a6fc5b4 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h | |||
@@ -97,12 +97,12 @@ extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_con | |||
97 | extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); | 97 | extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); |
98 | extern int uprobe_mmap(struct vm_area_struct *vma); | 98 | extern int uprobe_mmap(struct vm_area_struct *vma); |
99 | extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); | 99 | extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end); |
100 | extern void uprobe_start_dup_mmap(void); | ||
101 | extern void uprobe_end_dup_mmap(void); | ||
100 | extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); | 102 | extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); |
101 | extern void uprobe_free_utask(struct task_struct *t); | 103 | extern void uprobe_free_utask(struct task_struct *t); |
102 | extern void uprobe_copy_process(struct task_struct *t); | 104 | extern void uprobe_copy_process(struct task_struct *t); |
103 | extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); | 105 | extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); |
104 | extern void __weak arch_uprobe_enable_step(struct arch_uprobe *arch); | ||
105 | extern void __weak arch_uprobe_disable_step(struct arch_uprobe *arch); | ||
106 | extern int uprobe_post_sstep_notifier(struct pt_regs *regs); | 106 | extern int uprobe_post_sstep_notifier(struct pt_regs *regs); |
107 | extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); | 107 | extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); |
108 | extern void uprobe_notify_resume(struct pt_regs *regs); | 108 | extern void uprobe_notify_resume(struct pt_regs *regs); |
@@ -129,6 +129,12 @@ static inline void | |||
129 | uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) | 129 | uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end) |
130 | { | 130 | { |
131 | } | 131 | } |
132 | static inline void uprobe_start_dup_mmap(void) | ||
133 | { | ||
134 | } | ||
135 | static inline void uprobe_end_dup_mmap(void) | ||
136 | { | ||
137 | } | ||
132 | static inline void | 138 | static inline void |
133 | uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) | 139 | uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) |
134 | { | 140 | { |
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 5cc4e7e42e68..dea7acfbb071 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/ptrace.h> /* user_enable_single_step */ | 33 | #include <linux/ptrace.h> /* user_enable_single_step */ |
34 | #include <linux/kdebug.h> /* notifier mechanism */ | 34 | #include <linux/kdebug.h> /* notifier mechanism */ |
35 | #include "../../mm/internal.h" /* munlock_vma_page */ | 35 | #include "../../mm/internal.h" /* munlock_vma_page */ |
36 | #include <linux/percpu-rwsem.h> | ||
36 | 37 | ||
37 | #include <linux/uprobes.h> | 38 | #include <linux/uprobes.h> |
38 | 39 | ||
@@ -71,6 +72,8 @@ static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; | |||
71 | static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; | 72 | static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; |
72 | #define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) | 73 | #define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) |
73 | 74 | ||
75 | static struct percpu_rw_semaphore dup_mmap_sem; | ||
76 | |||
74 | /* | 77 | /* |
75 | * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe | 78 | * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe |
76 | * events active at this time. Probably a fine grained per inode count is | 79 | * events active at this time. Probably a fine grained per inode count is |
@@ -766,10 +769,13 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) | |||
766 | struct map_info *info; | 769 | struct map_info *info; |
767 | int err = 0; | 770 | int err = 0; |
768 | 771 | ||
772 | percpu_down_write(&dup_mmap_sem); | ||
769 | info = build_map_info(uprobe->inode->i_mapping, | 773 | info = build_map_info(uprobe->inode->i_mapping, |
770 | uprobe->offset, is_register); | 774 | uprobe->offset, is_register); |
771 | if (IS_ERR(info)) | 775 | if (IS_ERR(info)) { |
772 | return PTR_ERR(info); | 776 | err = PTR_ERR(info); |
777 | goto out; | ||
778 | } | ||
773 | 779 | ||
774 | while (info) { | 780 | while (info) { |
775 | struct mm_struct *mm = info->mm; | 781 | struct mm_struct *mm = info->mm; |
@@ -799,7 +805,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) | |||
799 | mmput(mm); | 805 | mmput(mm); |
800 | info = free_map_info(info); | 806 | info = free_map_info(info); |
801 | } | 807 | } |
802 | 808 | out: | |
809 | percpu_up_write(&dup_mmap_sem); | ||
803 | return err; | 810 | return err; |
804 | } | 811 | } |
805 | 812 | ||
@@ -1131,6 +1138,16 @@ void uprobe_clear_state(struct mm_struct *mm) | |||
1131 | kfree(area); | 1138 | kfree(area); |
1132 | } | 1139 | } |
1133 | 1140 | ||
1141 | void uprobe_start_dup_mmap(void) | ||
1142 | { | ||
1143 | percpu_down_read(&dup_mmap_sem); | ||
1144 | } | ||
1145 | |||
1146 | void uprobe_end_dup_mmap(void) | ||
1147 | { | ||
1148 | percpu_up_read(&dup_mmap_sem); | ||
1149 | } | ||
1150 | |||
1134 | void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) | 1151 | void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm) |
1135 | { | 1152 | { |
1136 | newmm->uprobes_state.xol_area = NULL; | 1153 | newmm->uprobes_state.xol_area = NULL; |
@@ -1199,6 +1216,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot | |||
1199 | vaddr = kmap_atomic(area->page); | 1216 | vaddr = kmap_atomic(area->page); |
1200 | memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES); | 1217 | memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES); |
1201 | kunmap_atomic(vaddr); | 1218 | kunmap_atomic(vaddr); |
1219 | /* | ||
1220 | * We probably need flush_icache_user_range() but it needs vma. | ||
1221 | * This should work on supported architectures too. | ||
1222 | */ | ||
1223 | flush_dcache_page(area->page); | ||
1202 | 1224 | ||
1203 | return current->utask->xol_vaddr; | 1225 | return current->utask->xol_vaddr; |
1204 | } | 1226 | } |
@@ -1430,16 +1452,6 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp) | |||
1430 | return uprobe; | 1452 | return uprobe; |
1431 | } | 1453 | } |
1432 | 1454 | ||
1433 | void __weak arch_uprobe_enable_step(struct arch_uprobe *arch) | ||
1434 | { | ||
1435 | user_enable_single_step(current); | ||
1436 | } | ||
1437 | |||
1438 | void __weak arch_uprobe_disable_step(struct arch_uprobe *arch) | ||
1439 | { | ||
1440 | user_disable_single_step(current); | ||
1441 | } | ||
1442 | |||
1443 | /* | 1455 | /* |
1444 | * Run handler and ask thread to singlestep. | 1456 | * Run handler and ask thread to singlestep. |
1445 | * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. | 1457 | * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. |
@@ -1493,7 +1505,6 @@ static void handle_swbp(struct pt_regs *regs) | |||
1493 | goto out; | 1505 | goto out; |
1494 | 1506 | ||
1495 | if (!pre_ssout(uprobe, regs, bp_vaddr)) { | 1507 | if (!pre_ssout(uprobe, regs, bp_vaddr)) { |
1496 | arch_uprobe_enable_step(&uprobe->arch); | ||
1497 | utask->active_uprobe = uprobe; | 1508 | utask->active_uprobe = uprobe; |
1498 | utask->state = UTASK_SSTEP; | 1509 | utask->state = UTASK_SSTEP; |
1499 | return; | 1510 | return; |
@@ -1525,7 +1536,6 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) | |||
1525 | else | 1536 | else |
1526 | WARN_ON_ONCE(1); | 1537 | WARN_ON_ONCE(1); |
1527 | 1538 | ||
1528 | arch_uprobe_disable_step(&uprobe->arch); | ||
1529 | put_uprobe(uprobe); | 1539 | put_uprobe(uprobe); |
1530 | utask->active_uprobe = NULL; | 1540 | utask->active_uprobe = NULL; |
1531 | utask->state = UTASK_RUNNING; | 1541 | utask->state = UTASK_RUNNING; |
@@ -1604,6 +1614,9 @@ static int __init init_uprobes(void) | |||
1604 | mutex_init(&uprobes_mmap_mutex[i]); | 1614 | mutex_init(&uprobes_mmap_mutex[i]); |
1605 | } | 1615 | } |
1606 | 1616 | ||
1617 | if (percpu_init_rwsem(&dup_mmap_sem)) | ||
1618 | return -ENOMEM; | ||
1619 | |||
1607 | return register_die_notifier(&uprobe_exception_nb); | 1620 | return register_die_notifier(&uprobe_exception_nb); |
1608 | } | 1621 | } |
1609 | module_init(init_uprobes); | 1622 | module_init(init_uprobes); |
diff --git a/kernel/fork.c b/kernel/fork.c index 8b20ab7d3aa2..c497e57aa654 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -352,6 +352,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) | |||
352 | unsigned long charge; | 352 | unsigned long charge; |
353 | struct mempolicy *pol; | 353 | struct mempolicy *pol; |
354 | 354 | ||
355 | uprobe_start_dup_mmap(); | ||
355 | down_write(&oldmm->mmap_sem); | 356 | down_write(&oldmm->mmap_sem); |
356 | flush_cache_dup_mm(oldmm); | 357 | flush_cache_dup_mm(oldmm); |
357 | uprobe_dup_mmap(oldmm, mm); | 358 | uprobe_dup_mmap(oldmm, mm); |
@@ -469,6 +470,7 @@ out: | |||
469 | up_write(&mm->mmap_sem); | 470 | up_write(&mm->mmap_sem); |
470 | flush_tlb_mm(oldmm); | 471 | flush_tlb_mm(oldmm); |
471 | up_write(&oldmm->mmap_sem); | 472 | up_write(&oldmm->mmap_sem); |
473 | uprobe_end_dup_mmap(); | ||
472 | return retval; | 474 | return retval; |
473 | fail_nomem_anon_vma_fork: | 475 | fail_nomem_anon_vma_fork: |
474 | mpol_put(pol); | 476 | mpol_put(pol); |
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 4ff9ca4f359a..9614db8b0f8c 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
@@ -189,7 +189,7 @@ static int create_trace_uprobe(int argc, char **argv) | |||
189 | if (argv[0][0] == '-') | 189 | if (argv[0][0] == '-') |
190 | is_delete = true; | 190 | is_delete = true; |
191 | else if (argv[0][0] != 'p') { | 191 | else if (argv[0][0] != 'p') { |
192 | pr_info("Probe definition must be started with 'p', 'r' or" " '-'.\n"); | 192 | pr_info("Probe definition must be started with 'p' or '-'.\n"); |
193 | return -EINVAL; | 193 | return -EINVAL; |
194 | } | 194 | } |
195 | 195 | ||