[FIX] synchronizing of unregister_uprobe() 64/58964/1
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Wed, 3 Feb 2016 09:05:11 +0000 (12:05 +0300)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Fri, 5 Feb 2016 09:55:18 +0000 (12:55 +0300)
Add atomic usage counter to uprobe struct and check it before remove uprobe.

Change-Id: Idc7804d38028413d68d4fbac41813ca3899e5443
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
uprobe/arch/arm/swap-asm/swap_uprobes.c
uprobe/arch/x86/swap-asm/swap_uprobes.c
uprobe/swap_uprobes.c
uprobe/swap_uprobes.h

index 5282820..3809a4e 100644 (file)
@@ -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);
                        }
                }
        }
index 639a7d3..e850e31 100644 (file)
@@ -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);
 }
index 2cfcf7a..6320e58 100644 (file)
@@ -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)
index c863978..22ec4d2 100644 (file)
@@ -34,8 +34,8 @@
 #define _SWAP_UPROBES_H
 
 
+#include <master/wait.h>
 #include <kprobe/swap_kprobes.h>
-
 #include <swap-asm/swap_uprobes.h>
 
 /**
@@ -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);