[FIX] probes disarming in fork'ed process 44/46844/4
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Tue, 18 Aug 2015 09:32:24 +0000 (12:32 +0300)
committerDmitry Kovalenko <d.kovalenko@samsung.com>
Fri, 28 Aug 2015 05:53:43 +0000 (22:53 -0700)
Disarming is now moved out of atomic context.

Change-Id: I723b11e25dc377a7caea5efb54ed42b34db68868
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
14 files changed:
kprobe/swap_kprobes.h
uprobe/arch/arm/swap-asm/swap_uprobes.c
uprobe/arch/arm/swap-asm/swap_uprobes.h
uprobe/arch/x86/swap-asm/swap_uprobes.c
uprobe/arch/x86/swap-asm/swap_uprobes.h
uprobe/swap_uprobes.c
uprobe/swap_uprobes.h
us_manager/helper.c
us_manager/sspt/sspt_file.c
us_manager/sspt/sspt_file.h
us_manager/sspt/sspt_page.c
us_manager/sspt/sspt_page.h
us_manager/sspt/sspt_proc.c
us_manager/sspt/sspt_proc.h

index 5153e8e..5ad8081 100644 (file)
@@ -294,9 +294,6 @@ void swap_unregister_kretprobe_bottom(struct kretprobe *rp);
 void swap_unregister_kretprobes_bottom(struct kretprobe **rps, size_t size);
 
 
-int swap_disarm_urp_inst_for_task(struct task_struct *parent,
-                                 struct task_struct *task);
-
 int trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs);
 
 
index 1369785..bba8d98 100644 (file)
@@ -698,6 +698,15 @@ int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
        return 0;
 }
 
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+       /* Understand function mode */
+       return ((unsigned long)ri->sp & 1) ?
+                       ((unsigned long)ri->rp->up.kp.ainsn.insn + 0x1b) :
+                       (unsigned long)(ri->rp->up.kp.ainsn.insn +
+                                       UPROBES_TRAMP_RET_BREAK_IDX);
+}
+
 /**
  * @brief Disarms uretprobe instance.
  *
@@ -707,7 +716,7 @@ int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
  * negative error code on error.
  */
 int arch_disarm_urp_inst(struct uretprobe_instance *ri,
-                        struct task_struct *task)
+                        struct task_struct *task, unsigned long tr)
 {
        struct pt_regs *uregs = task_pt_regs(ri->task);
        unsigned long ra = swap_get_ret_addr(uregs);
@@ -716,15 +725,16 @@ int arch_disarm_urp_inst(struct uretprobe_instance *ri,
        unsigned long *stack = sp - RETPROBE_STACK_DEPTH + 1;
        unsigned long *found = NULL;
        unsigned long *buf[RETPROBE_STACK_DEPTH];
+       unsigned long vaddr;
        int i, retval;
 
-       /* Understand function mode */
-       if ((long)ri->sp & 1) {
-               tramp = (unsigned long *)
-                       ((unsigned long)ri->rp->up.kp.ainsn.insn + 0x1b);
+       if (tr == 0) {
+               vaddr = (unsigned long)ri->rp->up.kp.addr;
+               tramp = (unsigned long *)arch_tramp_by_ri(ri);
        } else {
-               tramp = (unsigned long *)(ri->rp->up.kp.ainsn.insn +
-                                         UPROBES_TRAMP_RET_BREAK_IDX);
+               /* ri - invalid */
+               vaddr = 0;
+               tramp = (unsigned long *)tr;
        }
 
        /* check stack */
@@ -752,10 +762,10 @@ int arch_disarm_urp_inst(struct uretprobe_instance *ri,
        }
 
        printk(KERN_INFO "---> %s (%d/%d): trampoline found at "
-              "%08lx (%08lx /%+d) - %p\n",
+              "%08lx (%08lx /%+d) - %lx, set ret_addr=%p\n",
               task->comm, task->tgid, task->pid,
               (unsigned long)found, (unsigned long)sp,
-              found - sp, ri->rp->up.kp.addr);
+              found - sp, vaddr, ri->ret_addr);
        retval = write_proc_vm_atomic(task, (unsigned long)found,
                                      &ri->ret_addr,
                                      sizeof(ri->ret_addr));
@@ -771,16 +781,16 @@ int arch_disarm_urp_inst(struct uretprobe_instance *ri,
 check_lr: /* check lr anyway */
        if (ra == (unsigned long)tramp) {
                printk(KERN_INFO "---> %s (%d/%d): trampoline found at "
-                      "lr = %08lx - %p\n",
-                      task->comm, task->tgid, task->pid,
-                      ra, ri->rp->up.kp.addr);
+                      "lr = %08lx - %lx, set ret_addr=%p\n",
+                      task->comm, task->tgid, task->pid, ra, vaddr, ri->ret_addr);
+
                swap_set_ret_addr(uregs, (unsigned long)ri->ret_addr);
                retval = 0;
        } else if (retval) {
                printk(KERN_INFO "---> %s (%d/%d): trampoline NOT found at "
-                      "sp = %08lx, lr = %08lx - %p\n",
+                      "sp = %08lx, lr = %08lx - %lx, ret_addr=%p\n",
                       task->comm, task->tgid, task->pid,
-                      (unsigned long)sp, ra, ri->rp->up.kp.addr);
+                      (unsigned long)sp, ra, vaddr, ri->ret_addr);
        }
 
        return retval;
index 885f227..d0ba89b 100644 (file)
@@ -82,7 +82,8 @@ static inline int longjmp_break_uhandler(struct kprobe *p, struct pt_regs *regs)
 void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
 int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
 int arch_disarm_urp_inst(struct uretprobe_instance *ri,
-                        struct task_struct *task);
+                        struct task_struct *task, unsigned long tr);
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri);
 
 unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs);
 void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
index 84de6e4..9a336c2 100644 (file)
@@ -54,6 +54,11 @@ static unsigned long trampoline_addr(struct uprobe *up)
                               UPROBES_TRAMP_RET_BREAK_IDX);
 }
 
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+       return trampoline_addr(&ri->rp->up);
+}
+
 static struct uprobe_ctlblk *current_ucb(void)
 {
        /* FIXME hardcoded offset */
@@ -211,12 +216,18 @@ int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
  * negative error code on error.
  */
 int arch_disarm_urp_inst(struct uretprobe_instance *ri,
-                        struct task_struct *task)
+                        struct task_struct *task, unsigned long tr)
 {
        int len;
        unsigned long ret_addr;
        unsigned long sp = (unsigned long)ri->sp;
-       unsigned long tramp_addr = trampoline_addr(&ri->rp->up);
+       unsigned long tramp_addr;
+
+       if (tr == 0)
+               tramp_addr = arch_tramp_by_ri(ri);
+       else
+               tramp_addr = tr; /* ri - invalid */
+
        len = read_proc_vm_atomic(task, sp, &ret_addr, sizeof(ret_addr));
        if (len != sizeof(ret_addr)) {
                printk(KERN_INFO "---> %s (%d/%d): failed to read stack from %08lx\n",
index faef033..7033680 100644 (file)
@@ -86,7 +86,8 @@ static inline int arch_opcode_analysis_uretprobe(struct uretprobe *rp)
 
 int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
 int arch_disarm_urp_inst(struct uretprobe_instance *ri,
-                        struct task_struct *task);
+                        struct task_struct *task, unsigned long tr);
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri);
 unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs);
 void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
 void arch_remove_uprobe(struct uprobe *up);
index 82e64c8..838820e 100644 (file)
@@ -78,6 +78,42 @@ void print_uprobe_hash_table(void)
 }
 #endif
 
+
+struct uinst_info *uinst_info_create(unsigned long vaddr,
+                                    kprobe_opcode_t opcode)
+{
+       struct uinst_info *uinst;
+
+       uinst = kmalloc(sizeof(*uinst), GFP_ATOMIC);
+       if (uinst) {
+               INIT_HLIST_NODE(&uinst->hlist);
+               uinst->vaddr = vaddr;
+               uinst->opcode = opcode;
+       } else {
+               pr_err("Cannot allocate memory for uinst\n");
+       }
+
+       return uinst;
+}
+EXPORT_SYMBOL_GPL(uinst_info_create);
+
+void uinst_info_destroy(struct uinst_info *uinst)
+{
+       kfree(uinst);
+}
+EXPORT_SYMBOL_GPL(uinst_info_destroy);
+
+void uinst_info_disarm(struct uinst_info *uinst, struct task_struct *task)
+{
+       int ret = write_proc_vm_atomic(task, uinst->vaddr,
+                                      &uinst->opcode, sizeof(uinst->opcode));
+       if (!ret) {
+               printk("uinst_info_disarm: failed to write memory "
+                      "tgid=%u, vaddr=%08lx!\n", task->tgid, uinst->vaddr);
+       }
+}
+EXPORT_SYMBOL_GPL(uinst_info_disarm);
+
 /*
  * Keep all fields in the uprobe consistent
  */
@@ -858,36 +894,6 @@ int swap_register_uretprobe(struct uretprobe *rp)
 EXPORT_SYMBOL_GPL(swap_register_uretprobe);
 
 /**
- * @brief Disarms uretprobe instances for the specified child task.
- *
- * @param parent Pointer to the parent task struct.
- * @param task Pointer to the child task struct.
- * @return 0
- */
-int swap_disarm_urp_inst_for_task(struct task_struct *parent,
-                                 struct task_struct *task)
-{
-       unsigned long flags;
-       struct uretprobe_instance *ri;
-       struct hlist_head *head;
-       struct hlist_node *tmp;
-       DECLARE_NODE_PTR_FOR_HLIST(node);
-
-       spin_lock_irqsave(&uretprobe_lock, flags);
-
-       head = uretprobe_inst_table_head(parent->mm);
-       swap_hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
-               if (parent == ri->task)
-                       arch_disarm_urp_inst(ri, task);
-       }
-
-       spin_unlock_irqrestore(&uretprobe_lock, flags);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(swap_disarm_urp_inst_for_task);
-
-/**
  * @brief Disarms uretprobes for specified task.
  *
  * @param task Pointer to the task_struct.
@@ -909,7 +915,7 @@ void swap_discard_pending_uretprobes(struct task_struct *task)
                        printk(KERN_INFO "%s (%d/%d): pending urp inst: %08lx\n",
                               task->comm, task->tgid, task->pid,
                               (unsigned long)ri->rp->up.kp.addr);
-                       arch_disarm_urp_inst(ri, task);
+                       arch_disarm_urp_inst(ri, task, 0);
                        recycle_urp_inst(ri);
                }
        }
@@ -934,7 +940,7 @@ void __swap_unregister_uretprobe(struct uretprobe *rp, int disarm)
        spin_lock_irqsave(&uretprobe_lock, flags);
 
        while ((ri = get_used_urp_inst(rp)) != NULL) {
-               if (arch_disarm_urp_inst(ri, ri->task) != 0)
+               if (arch_disarm_urp_inst(ri, ri->task, 0) != 0)
                        printk(KERN_INFO "%s (%d/%d): "
                               "cannot disarm urp instance (%08lx)\n",
                               ri->task->comm, ri->task->tgid, ri->task->pid,
@@ -1007,6 +1013,94 @@ void swap_ujprobe_return(void)
 }
 EXPORT_SYMBOL_GPL(swap_ujprobe_return);
 
+
+static struct urinst_info *urinst_info_create(struct uretprobe_instance *ri)
+{
+       struct urinst_info *urinst;
+
+       urinst = kmalloc(sizeof(*urinst), GFP_ATOMIC);
+       if (urinst) {
+               INIT_HLIST_NODE(&urinst->hlist);
+               urinst->task = ri->task;
+               urinst->sp = (unsigned long)ri->sp;
+               urinst->tramp = arch_tramp_by_ri(ri);
+               urinst->ret_addr = (unsigned long)ri->ret_addr;
+       } else {
+               pr_err("Cannot allocate memory for urinst\n");
+       }
+
+       return urinst;
+}
+
+static void urinst_info_destroy(struct urinst_info *urinst)
+{
+       kfree(urinst);
+}
+
+static void urinst_info_disarm(struct urinst_info *urinst, struct task_struct *task)
+{
+       struct uretprobe_instance ri;
+       unsigned long tramp = urinst->tramp;
+
+       /* set necessary data*/
+       ri.task = urinst->task;
+       ri.sp = (kprobe_opcode_t *)urinst->sp;
+       ri.ret_addr = (kprobe_opcode_t *)urinst->ret_addr;
+
+       arch_disarm_urp_inst(&ri, task, tramp);
+}
+
+void urinst_info_get_current_hlist(struct hlist_head *head)
+{
+       unsigned long flags;
+       struct task_struct *task = current;
+       struct uretprobe_instance *ri;
+       struct hlist_head *hhead;
+       struct hlist_node *n;
+       struct hlist_node *last = NULL;
+       DECLARE_NODE_PTR_FOR_HLIST(node);
+
+       spin_lock_irqsave(&uretprobe_lock, flags);
+       hhead = uretprobe_inst_table_head(task->mm);
+       swap_hlist_for_each_entry_safe(ri, node, n, hhead, hlist) {
+               if (task == ri->task) {
+                       struct urinst_info *urinst;
+
+                       urinst = urinst_info_create(ri);
+                       if (urinst) {
+                               if (last)
+                                       hlist_add_after(last, &urinst->hlist);
+                               else
+                                       hlist_add_head(&urinst->hlist, head);
+
+                               last = &urinst->hlist;
+                       }
+
+               }
+       }
+       spin_unlock_irqrestore(&uretprobe_lock, flags);
+}
+EXPORT_SYMBOL_GPL(urinst_info_get_current_hlist);
+
+void urinst_info_put_current_hlist(struct hlist_head *head,
+                                 struct task_struct *task)
+{
+       struct urinst_info *urinst;
+       struct hlist_node *tmp;
+       DECLARE_NODE_PTR_FOR_HLIST(node);
+
+       swap_hlist_for_each_entry_safe(urinst, node, tmp, head, hlist) {
+               /* check on disarm */
+               if (task)
+                       urinst_info_disarm(urinst, task);
+
+               hlist_del(&urinst->hlist);
+               urinst_info_destroy(urinst);
+       }
+}
+EXPORT_SYMBOL_GPL(urinst_info_put_current_hlist);
+
+
 static int once(void)
 {
        init_uprobe_table();
index 1e3a9fd..cbee706 100644 (file)
@@ -50,6 +50,33 @@ struct uprobe {
        struct arch_specific_tramp atramp;  /**< Stores trampoline */
 };
 
+struct uinst_info {
+       struct hlist_node hlist;
+
+       unsigned long vaddr;
+       kprobe_opcode_t opcode;
+};
+
+struct urinst_info {
+       struct hlist_node hlist;
+
+       struct task_struct *task;
+       unsigned long sp;
+       unsigned long tramp;
+       unsigned long ret_addr;
+};
+
+struct uinst_info *uinst_info_create(unsigned long vaddr,
+                                    kprobe_opcode_t opcode);
+void uinst_info_destroy(struct uinst_info *uinst);
+void uinst_info_disarm(struct uinst_info *uinst, struct task_struct *task);
+
+
+void urinst_info_get_current_hlist(struct hlist_head *head);
+void urinst_info_put_current_hlist(struct hlist_head *head,
+                                 struct task_struct *task);
+
+
 /**
  * @brief Uprobe pre-entry handler.
  */
index b3a9395..8d9882f 100644 (file)
@@ -178,28 +178,88 @@ static void unregister_ctx_task(void)
  *                              copy_process()                                *
  ******************************************************************************
  */
-static atomic_t copy_process_cnt = ATOMIC_INIT(0);
+static void func_uinst_creare(struct us_ip *ip, void *data)
+{
+       struct hlist_head *head = (struct hlist_head *)data;
+       struct uprobe *up;
+
+       up = probe_info_get_uprobe(ip->info, ip);
+       if (up) {
+               struct uinst_info *uinst;
+               unsigned long vaddr = (unsigned long)up->kp.addr;
+
+               uinst = uinst_info_create(vaddr, up->kp.opcode);
+               if (uinst)
+                       hlist_add_head(&uinst->hlist, head);
+       }
+}
+
+static void disarm_for_task(struct task_struct *child, struct hlist_head *head)
+{
+       struct uinst_info *uinst;
+       struct hlist_node *tmp;
+       DECLARE_NODE_PTR_FOR_HLIST(node);
+
+       swap_hlist_for_each_entry_safe(uinst, node, tmp, head, hlist) {
+               uinst_info_disarm(uinst, child);
+               hlist_del(&uinst->hlist);
+               uinst_info_destroy(uinst);
+       }
+}
+
+struct clean_data {
+       struct task_struct *task;
+
+       struct hlist_head head;
+       struct hlist_head rhead;
+};
 
-static void recover_child(struct task_struct *child_task,
-                         struct sspt_proc *proc)
+/* FIXME: sync with stop */
+static unsigned long cb_clean_child(void *data)
 {
-       sspt_proc_uninstall(proc, child_task, US_DISARM);
-       swap_disarm_urp_inst_for_task(current, child_task);
+       struct clean_data *cdata = (struct clean_data *)data;
+       struct task_struct *child = cdata->task;
+
+       /* disarm up for child */
+       disarm_for_task(child, &cdata->head);
+
+       /* disarm urp for child */
+       urinst_info_put_current_hlist(&cdata->rhead, child);
+
+       return 0;
 }
 
-static void rm_uprobes_child(struct task_struct *task)
+static void rm_uprobes_child(struct kretprobe_instance *ri,
+                            struct pt_regs *regs, struct task_struct *child)
 {
+       int ret;
        struct sspt_proc *proc;
+       struct clean_data cdata = {
+               .task = child,
+               .head = HLIST_HEAD_INIT,
+               .rhead = HLIST_HEAD_INIT
+       };
 
        sspt_proc_write_lock();
 
        proc = sspt_proc_get_by_task(current);
-       if (proc)
-               recover_child(task, proc);
+       if (proc) {
+               sspt_proc_on_each_ip(proc, func_uinst_creare, (void *)&cdata.head);
+               urinst_info_get_current_hlist(&cdata.rhead);
+       }
 
        sspt_proc_write_unlock();
+
+       /* set jumper */
+       ret = set_jump_cb((unsigned long)ri->ret_addr, regs, cb_clean_child,
+                         &cdata, sizeof(cdata));
+       if (ret == 0)
+               ri->ret_addr = (unsigned long *)get_jump_addr();
 }
 
+
+static atomic_t copy_process_cnt = ATOMIC_INIT(0);
+
 static int entry_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
 {
        atomic_inc(&copy_process_cnt);
@@ -220,7 +280,7 @@ static int ret_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
                goto out;
 
        if (task->mm != current->mm) {  /* check flags CLONE_VM */
-               rm_uprobes_child(task);
+               rm_uprobes_child(ri, regs, task);
        }
 out:
        atomic_dec(&copy_process_cnt);
index 917335a..9a1c318 100644 (file)
@@ -198,6 +198,22 @@ void sspt_file_add_ip(struct sspt_file *file, unsigned long offset,
                sspt_add_ip(page, ip);
 }
 
+void sspt_file_on_each_ip(struct sspt_file *file,
+                         void (*func)(struct us_ip *, void *), void *data)
+{
+       int i;
+       const int table_size = (1 << file->page_probes_hash_bits);
+       struct sspt_page *page;
+       struct hlist_head *head;
+       DECLARE_NODE_PTR_FOR_HLIST(node);
+
+       for (i = 0; i < table_size; ++i) {
+               head = &file->page_probes_table[i];
+               swap_hlist_for_each_entry(page, node, head, hlist)
+                       sspt_page_on_each_ip(page, func, data);
+       }
+}
+
 /**
  * @brief Get sspt_page from sspt_file (look)
  *
index 11e8f42..3024a8f 100644 (file)
@@ -56,6 +56,9 @@ struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
 void sspt_file_add_ip(struct sspt_file *file, unsigned long offset,
                      struct probe_info *probe_i);
 
+void sspt_file_on_each_ip(struct sspt_file *file,
+                         void (*func)(struct us_ip *, void *), void *data);
+
 struct sspt_page *sspt_get_page(struct sspt_file *file,
                                unsigned long offset_addr);
 void sspt_put_page(struct sspt_page *page);
index a701ecf..6edf27a 100644 (file)
@@ -223,3 +223,15 @@ int sspt_unregister_page(struct sspt_page *page,
 
        return err;
 }
+
+void sspt_page_on_each_ip(struct sspt_page *page,
+                         void (*func)(struct us_ip *, void *), void *data)
+{
+       struct us_ip *ip;
+
+       spin_lock(&page->lock);
+       list_for_each_entry(ip, &page->ip_list_inst, list)
+               func(ip, data);
+
+       spin_unlock(&page->lock);
+}
index b3a94fc..f06e536 100644 (file)
@@ -60,4 +60,7 @@ int sspt_unregister_page(struct sspt_page *page,
                         enum US_FLAGS flag,
                         struct task_struct *task);
 
+void sspt_page_on_each_ip(struct sspt_page *page,
+                         void (*func)(struct us_ip *, void *), void *data);
+
 #endif /* __SSPT_PAGE__ */
index fff5d16..ce1e024 100644 (file)
@@ -499,6 +499,15 @@ void sspt_proc_on_each_filter(struct sspt_proc *proc,
                func(fl, data);
 }
 
+void sspt_proc_on_each_ip(struct sspt_proc *proc,
+                         void (*func)(struct us_ip *, void *), void *data)
+{
+       struct sspt_file *file;
+
+       list_for_each_entry(file, &proc->file_list, list)
+               sspt_file_on_each_ip(file, func, data);
+}
+
 static void is_send_event(struct sspt_filter *f, void *data)
 {
        bool *is_send = (bool *)data;
index 8597322..0f4318c 100644 (file)
@@ -31,6 +31,7 @@ struct slot_manager;
 struct task_struct;
 struct pf_group;
 struct sspt_filter;
+struct us_ip;
 
 /** Flags for sspt_*_uninstall() */
 enum US_FLAGS {
@@ -105,6 +106,9 @@ void sspt_proc_on_each_filter(struct sspt_proc *proc,
                              void (*func)(struct sspt_filter *, void *),
                              void *data);
 
+void sspt_proc_on_each_ip(struct sspt_proc *proc,
+                         void (*func)(struct us_ip *, void *), void *data);
+
 bool sspt_proc_is_send_event(struct sspt_proc *proc);
 
 int sspt_proc_cb_set(struct sspt_proc_cb *cb);