From: Vyacheslav Cherkashin Date: Wed, 3 Feb 2016 09:05:11 +0000 (+0300) Subject: [FIX] synchronizing of unregister_uprobe() X-Git-Tag: SRR_20160331~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=00f860f7feb78394c35d4a0a3913a6e6f562ccd0;p=kernel%2Fswap-modules.git [FIX] synchronizing of unregister_uprobe() Add atomic usage counter to uprobe struct and check it before remove uprobe. Change-Id: Idc7804d38028413d68d4fbac41813ca3899e5443 Signed-off-by: Vyacheslav Cherkashin --- diff --git a/uprobe/arch/arm/swap-asm/swap_uprobes.c b/uprobe/arch/arm/swap-asm/swap_uprobes.c index 5282820..3809a4e 100644 --- a/uprobe/arch/arm/swap-asm/swap_uprobes.c +++ b/uprobe/arch/arm/swap-asm/swap_uprobes.c @@ -894,21 +894,27 @@ void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task) static int urp_handler(struct pt_regs *regs, pid_t tgid) { struct uprobe *p; + unsigned long flags; unsigned long vaddr = regs->ARM_pc; unsigned long offset_bp = thumb_mode(regs) ? 0x1a : 4 * UPROBES_TRAMP_RET_BREAK_IDX; unsigned long tramp_addr = vaddr - offset_bp; + local_irq_save(flags); p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs); - if (p == NULL) { - printk(KERN_INFO - "no_uprobe: Not one of ours: let kernel handle it %lx\n", - vaddr); + if (unlikely(p == NULL)) { + local_irq_restore(flags); + + pr_info("no_uprobe: Not one of ours: let kernel handle it %lx\n", + vaddr); return 1; } + get_up(p); + local_irq_restore(flags); trampoline_uprobe_handler(p, regs); + put_up(p); return 0; } @@ -940,26 +946,39 @@ static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) { int ret = 0; struct uprobe *p; + unsigned long flags; unsigned long vaddr = regs->ARM_pc | !!thumb_mode(regs); pid_t tgid = current->tgid; + local_irq_save(flags); p = get_uprobe((uprobe_opcode_t *)vaddr, tgid); if (p) { + get_up(p); + local_irq_restore(flags); if (!p->pre_handler || !p->pre_handler(p, regs)) arch_prepare_singlestep(p, regs); + put_up(p); } else { + local_irq_restore(flags); ret = urp_handler(regs, tgid); /* check ARM/THUMB CPU mode matches installed probe mode */ if (ret) { vaddr ^= 1; + + local_irq_save(flags); p = get_uprobe((uprobe_opcode_t *)vaddr, tgid); if (p) { + get_up(p); + local_irq_restore(flags); pr_err("invalid mode: thumb=%d addr=%p insn=%08lx\n", !!thumb_mode(regs), p->addr, p->opcode); ret = 0; disarm_uprobe(p, current); + put_up(p); + } else { + local_irq_restore(flags); } } } diff --git a/uprobe/arch/x86/swap-asm/swap_uprobes.c b/uprobe/arch/x86/swap-asm/swap_uprobes.c index 639a7d3..e850e31 100644 --- a/uprobe/arch/x86/swap-asm/swap_uprobes.c +++ b/uprobe/arch/x86/swap-asm/swap_uprobes.c @@ -525,18 +525,34 @@ static int exceptions_handler(struct pt_regs *regs, return 1; } +static int uprobe_handler_retprobe(struct uprobe *p, struct pt_regs *regs) +{ + int ret; + + ret = trampoline_uprobe_handler(p, regs); + set_current_probe(NULL); + put_up(p); + + return ret; +} + static int uprobe_handler_part2(struct uprobe *p, struct pt_regs *regs) { if (!p->pre_handler(p, regs)) { prepare_tramp(p, regs); if (p->ainsn.boostable == 1 && !p->post_handler) - return 1; + goto exit_and_put_up; save_current_flags(regs); set_current_probe(p); prepare_ss(regs); + + return 1; } +exit_and_put_up: + set_current_probe(NULL); + put_up(p); return 1; } @@ -547,6 +563,7 @@ static int uprobe_handler_atomic(struct pt_regs *regs) struct uprobe *p = get_uprobe((void *)vaddr, tgid); if (p) { + get_up(p); if (p->pre_handler) { set_current_probe(p); exceptions_handler(regs, uprobe_handler_part2); @@ -564,7 +581,8 @@ static int uprobe_handler_atomic(struct pt_regs *regs) } set_current_probe(p); - exceptions_handler(regs, trampoline_uprobe_handler); + get_up(p); + exceptions_handler(regs, uprobe_handler_retprobe); } return 1; @@ -579,6 +597,7 @@ static int post_uprobe_handler(struct uprobe *p, struct pt_regs *regs) /* reset current probe */ set_current_probe(NULL); + put_up(p); return 1; } @@ -631,6 +650,52 @@ static struct notifier_block uprobe_exceptions_nb = { .priority = INT_MAX }; +struct up_valid_struct { + struct uprobe *p; + bool found; +}; + +static int __uprobe_is_valid(struct uprobe *p, void *data) +{ + struct up_valid_struct *valid = (struct up_valid_struct *)data; + + if (valid->p == p) { + valid->found = true; + return 1; + } + + return 0; +} + +static bool uprobe_is_valid(struct uprobe *p) +{ + struct up_valid_struct valid = { + .p = p, + .found = false, + }; + + for_each_uprobe(__uprobe_is_valid, (void *)&valid); + + return valid.found; +} + +static int do_exit_handler(struct kprobe *kp, struct pt_regs *regs) +{ + struct uprobe *p; + + p = get_current_probe(); + if (p && uprobe_is_valid(p)) { + set_current_probe(NULL); + put_up(p); + } + + return 0; +} + +static struct kprobe kp_do_exit = { + .pre_handler = do_exit_handler +}; + /** * @brief Registers notify. * @@ -639,12 +704,17 @@ static struct notifier_block uprobe_exceptions_nb = { int swap_arch_init_uprobes(void) { int ret; + const char *sym; - resume_userspace_addr = swap_ksyms("resume_userspace"); - if (resume_userspace_addr == 0) { - pr_err("symbol 'resume_userspace' not found\n"); - return -ESRCH; - } + sym = "resume_userspace"; + resume_userspace_addr = swap_ksyms(sym); + if (resume_userspace_addr == 0) + goto not_found; + + sym = "do_exit"; + kp_do_exit.addr = (void *)swap_ksyms(sym); + if (kp_do_exit.addr == NULL) + goto not_found; ret = swap_td_raw_reg(&td_raw, sizeof(struct uprobe_ctlblk)); if (ret) @@ -652,9 +722,23 @@ int swap_arch_init_uprobes(void) ret = register_die_notifier(&uprobe_exceptions_nb); if (ret) - swap_td_raw_unreg(&td_raw); + goto unreg_td; + + ret = swap_register_kprobe(&kp_do_exit); + if (ret) + goto unreg_exeption; + return 0; + +unreg_exeption: + unregister_die_notifier(&uprobe_exceptions_nb); +unreg_td: + swap_td_raw_unreg(&td_raw); return ret; + +not_found: + pr_err("symbol '%s' not found\n", sym); + return -ESRCH; } /** @@ -664,6 +748,7 @@ int swap_arch_init_uprobes(void) */ void swap_arch_exit_uprobes(void) { + swap_unregister_kprobe(&kp_do_exit); unregister_die_notifier(&uprobe_exceptions_nb); swap_td_raw_unreg(&td_raw); } diff --git a/uprobe/swap_uprobes.c b/uprobe/swap_uprobes.c index 2cfcf7a..6320e58 100644 --- a/uprobe/swap_uprobes.c +++ b/uprobe/swap_uprobes.c @@ -485,6 +485,36 @@ static struct uretprobe_instance *get_free_urp_inst(struct uretprobe *rp) } /* =================================================================== */ + +void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data) +{ + int i; + struct uprobe *p; + struct hlist_head *head; + struct hlist_node *tnode; + DECLARE_NODE_PTR_FOR_HLIST(node); + + for (i = 0; i < UPROBE_TABLE_SIZE; ++i) { + head = &uprobe_table[i]; + swap_hlist_for_each_entry_safe(p, node, tnode, head, hlist) { + if (func(p, data)) + return; + } + } +} + +static int wait_up_action(atomic_t *val) +{ + BUG_ON(atomic_read(val)); + schedule(); + return 0; +} + +static void wait_up(struct uprobe *p) +{ + wait_on_atomic_t(&p->usage, wait_up_action, TASK_UNINTERRUPTIBLE); +} + /** * @brief Registers uprobe. * @@ -502,6 +532,7 @@ int swap_register_uprobe(struct uprobe *p) p->ainsn.insn = NULL; INIT_LIST_HEAD(&p->list); + atomic_set(&p->usage, 1); /* get the first item */ old_p = get_uprobe(p->addr, p->task->tgid); @@ -598,9 +629,13 @@ valid_p: kfree(old_p); } - if (!in_atomic()) + if (!in_atomic()) { synchronize_sched(); + atomic_dec(&p->usage); + wait_up(p); + } + remove_uprobe(p); } else { if (p->break_handler) diff --git a/uprobe/swap_uprobes.h b/uprobe/swap_uprobes.h index c863978..22ec4d2 100644 --- a/uprobe/swap_uprobes.h +++ b/uprobe/swap_uprobes.h @@ -34,8 +34,8 @@ #define _SWAP_UPROBES_H +#include #include - #include /** @@ -84,6 +84,7 @@ struct uprobe { uprobe_break_handler_t break_handler; /** Saved opcode (which has been replaced with breakpoint).*/ uprobe_opcode_t opcode; + atomic_t usage; #ifdef CONFIG_ARM /** Safe/unsafe to use probe on ARM.*/ unsigned safe_arm:1; @@ -199,6 +200,19 @@ struct uretprobe_instance { char data[0]; /**< Custom data */ }; + +static void inline get_up(struct uprobe *p) +{ + atomic_inc(&p->usage); +} + +static void inline put_up(struct uprobe *p) +{ + if (atomic_dec_and_test(&p->usage)) + wake_up_atomic_t(&p->usage); +} + +void for_each_uprobe(int (*func)(struct uprobe *, void *), void *data); int swap_register_uprobe(struct uprobe *p); void swap_unregister_uprobe(struct uprobe *p); void __swap_unregister_uprobe(struct uprobe *up, int disarm);