[FIX] uninstall probes on do_munmap() call
authorVyacheslav Cherkashin <v.cherkashin@samsung.com>
Wed, 16 Oct 2013 17:30:49 +0000 (21:30 +0400)
committerVyacheslav Cherkashin <v.cherkashin@samsung.com>
Thu, 17 Oct 2013 09:19:04 +0000 (13:19 +0400)
Change-Id: I370fb69f8d41bea3d007c90dfa9b671b3a99ec81
Signed-off-by: Vyacheslav Cherkashin <v.cherkashin@samsung.com>
us_manager/helper.c
us_manager/sspt/sspt_page.c
us_manager/sspt/sspt_proc.c
us_manager/sspt/sspt_proc.h

index 0c4307b..d829242 100644 (file)
@@ -26,6 +26,7 @@
 #include <dbi_kprobes.h>
 #include <dbi_kprobes_deps.h>
 #include <ksyms.h>
+#include <writer/kernel_operations.h>
 #include "us_slot_manager.h"
 #include "sspt/sspt.h"
 #include "helper.h"
@@ -174,94 +175,78 @@ static struct kprobe mr_kprobe = {
  *                                 do_munmap()                                *
  ******************************************************************************
  */
+struct unmap_data {
+       unsigned long start;
+       size_t len;
+};
 
-static int remove_unmap_probes(struct task_struct *task, struct sspt_proc *proc, unsigned long start, size_t len)
+static void remove_unmap_probes(struct sspt_proc *proc, struct unmap_data *umd)
 {
-       struct mm_struct *mm = task->mm;
-       struct vm_area_struct *vma;
+       struct task_struct *task = proc->task;
+       unsigned long start = umd->start;
+       size_t len = umd->len;
+       LIST_HEAD(head);
 
-       /* FIXME: not implemented */
-       return 0;
+       if (sspt_proc_get_files_by_region(proc, &head, start, len)) {
+               struct sspt_file *file, *n;
+               unsigned long end = start + len;
 
-       if ((start & ~PAGE_MASK) || start > TASK_SIZE || len > TASK_SIZE - start) {
-               return -EINVAL;
-       }
+               list_for_each_entry_safe(file, n, &head, list) {
+                       if (file->vm_start >= end)
+                               continue;
 
-       if ((len = PAGE_ALIGN(len)) == 0) {
-               return -EINVAL;
-       }
-
-       vma = find_vma(mm, start);
-       if (vma && check_vma(vma)) {
-               struct sspt_file *file;
-               unsigned long end = start + len;
-               struct dentry *dentry = vma->vm_file->f_dentry;
-
-               file = sspt_proc_find_file(proc, dentry);
-               if (file) {
-                       if (vma->vm_start == start || vma->vm_end == end) {
-                               sspt_file_uninstall(file, task, US_UNREGS_PROBE);
-                               file->loaded = 0;
-                       } else {
-                               unsigned long page_addr;
-                               struct sspt_page *page;
-
-                               for (page_addr = vma->vm_start; page_addr < vma->vm_end; page_addr += PAGE_SIZE) {
-                                       page = sspt_find_page_mapped(file, page_addr);
-                                       if (page) {
-                                               sspt_unregister_page(page, US_UNREGS_PROBE, task);
-                                       }
-                               }
-
-                               if (sspt_file_check_install_pages(file)) {
-                                       file->loaded = 0;
-                               }
+                       if (file->vm_start >= start)
+                               sspt_file_uninstall(file, task, US_UNINSTALL);
+                       else {
+                               /* TODO: uninstall pages: start..file->vm_end */
                        }
                }
-       }
 
-       return 0;
+               sspt_proc_insert_files(proc, &head);
+       }
 }
 
-/* Detects when target removes IPs. */
-static int unmap_pre_handler(struct kprobe *p, struct pt_regs *regs)
+
+static int entry_handler_unmap(struct kretprobe_instance *ri,
+                              struct pt_regs *regs)
 {
-       /* for ARM */
-       struct mm_struct *mm;
-       unsigned long start;
-       size_t len;
+       struct unmap_data *data = (struct unmap_data *)ri->data;
 
 #if defined(CONFIG_X86)
-       mm = (struct mm_struct *)regs->EREG(ax);
-       start = regs->EREG(dx);
-       len = (size_t)regs->EREG(cx);
+       data->start = regs->dx;
+       data->len = (size_t)regs->cx;
 #elif defined(CONFIG_ARM)
-       mm = (struct mm_struct *)regs->ARM_r0;
-       start = regs->ARM_r1;
-       len = (size_t)regs->ARM_r2;
+       data->start = regs->ARM_r1;
+       data->len = (size_t)regs->ARM_r2;
 #else
 #error this architecture is not supported
 #endif
 
-       struct sspt_proc *proc = NULL;
-       struct task_struct *task = current;
+       return 0;
+}
 
-       if (is_kthread(task))
-               goto out;
+static int ret_handler_unmap(struct kretprobe_instance *ri,
+                            struct pt_regs *regs)
+{
+       struct task_struct *task;
+       struct sspt_proc *proc;
+
+       task = current->group_leader;
+       if (is_kthread(task) ||
+           get_regs_ret_val(regs))
+               return 0;
 
        proc = sspt_proc_get_by_task(task);
-       if (proc) {
-               if (remove_unmap_probes(task, proc, start, len)) {
-                       printk("ERROR do_munmap: start=%lx, len=%x\n", start, len);
-               }
-       }
+       if (proc)
+               remove_unmap_probes(proc, (struct unmap_data *)ri->data);
 
-out:
        return 0;
 }
 
-static struct kprobe unmap_kprobe = {
-       .pre_handler = unmap_pre_handler
+static struct kretprobe unmap_kretprobe = {
+       .entry_handler = entry_handler_unmap,
+       .handler = ret_handler_unmap,
+       .data_size = sizeof(struct unmap_data)
 };
 
 
@@ -271,7 +256,7 @@ int register_helper(void)
        int ret = 0;
 
        /* install kprobe on 'do_munmap' to detect when for remove user space probes */
-       ret = dbi_register_kprobe(&unmap_kprobe);
+       ret = dbi_register_kretprobe(&unmap_kretprobe);
        if (ret) {
                printk("dbi_register_kprobe(do_munmap) result=%d!\n", ret);
                return ret;
@@ -308,7 +293,7 @@ unregister_mr:
        dbi_unregister_kprobe(&mr_kprobe);
 
 unregister_unmap:
-       dbi_unregister_kprobe(&unmap_kprobe);
+       dbi_unregister_kretprobe(&unmap_kretprobe);
 
        return ret;
 }
@@ -324,8 +309,8 @@ void unregister_helper(void)
        /* uninstall kprobe with 'mm_release' */
        dbi_unregister_kprobe(&mr_kprobe);
 
-       /* uninstall kprobe with 'do_munmap' */
-       dbi_unregister_kprobe(&unmap_kprobe);
+       /* uninstall kretprobe with 'do_munmap' */
+       dbi_unregister_kretprobe(&unmap_kretprobe);
 }
 
 int init_helper(void)
@@ -357,7 +342,7 @@ int init_helper(void)
                printk("Cannot find address for do_munmap function!\n");
                return -EINVAL;
        }
-       unmap_kprobe.addr = (kprobe_opcode_t *)addr;
+       unmap_kretprobe.kp.addr = (kprobe_opcode_t *)addr;
 
        return 0;
 }
index fe0ddf2..98ac687 100644 (file)
@@ -157,6 +157,12 @@ int sspt_unregister_page(struct sspt_page *page,
 
        INIT_LIST_HEAD(&ip_list_tmp);
        list_replace_init(&page->ip_list_inst, &ip_list_tmp);
+
+       if (flag == US_UNINSTALL) {
+               head = &page->ip_list_no_inst;
+               goto splice_and_unlock;
+       }
+
        spin_unlock(&page->lock);
 
        list_for_each_entry(ip, &ip_list_tmp, list) {
@@ -170,6 +176,8 @@ int sspt_unregister_page(struct sspt_page *page,
        head = (flag == US_DISARM) ? &page->ip_list_inst : &page->ip_list_no_inst;
 
        spin_lock(&page->lock);
+
+splice_and_unlock:
        list_splice(&ip_list_tmp, head);
        spin_unlock(&page->lock);
 
index be4bd01..3534247 100644 (file)
@@ -255,3 +255,34 @@ int sspt_proc_uninstall(struct sspt_proc *proc, struct task_struct *task, enum U
 
        return err;
 }
+
+static int intersection(unsigned long start_a, unsigned long end_a,
+                       unsigned long start_b, unsigned long end_b)
+{
+       return start_a < start_b ?
+                       end_a > start_b :
+                       start_a < end_b;
+}
+
+int sspt_proc_get_files_by_region(struct sspt_proc *proc,
+                                 struct list_head *head,
+                                 unsigned long start, size_t len)
+{
+       int ret = 0;
+       struct sspt_file *file, *n;
+       unsigned long end = start + len;
+
+       list_for_each_entry_safe(file, n, &proc->file_list, list) {
+               if (intersection(file->vm_start, file->vm_end, start, end)) {
+                       ret = 1;
+                       list_move(&file->list, head);
+               }
+       }
+
+       return ret;
+}
+
+void sspt_proc_insert_files(struct sspt_proc *proc, struct list_head *head)
+{
+       list_splice(head, &proc->file_list);
+}
index eff95ac..8bfd2a9 100644 (file)
@@ -32,8 +32,9 @@ struct slot_manager;
 struct task_struct;
 
 enum US_FLAGS {
-       US_UNREGS_PROBE,
-       US_DISARM
+       US_UNREGS_PROBE,        /* probes remove and disarm */
+       US_DISARM,              /* probes disarm */
+       US_UNINSTALL            /* probes remove from list install */
 };
 
 struct sspt_proc {
@@ -65,4 +66,9 @@ void sspt_proc_install_page(struct sspt_proc *proc, unsigned long page_addr);
 void sspt_proc_install(struct sspt_proc *proc);
 int sspt_proc_uninstall(struct sspt_proc *proc, struct task_struct *task, enum US_FLAGS flag);
 
+int sspt_proc_get_files_by_region(struct sspt_proc *proc,
+                                 struct list_head *head,
+                                 unsigned long start, size_t len);
+void sspt_proc_insert_files(struct sspt_proc *proc, struct list_head *head);
+
 #endif /* __SSPT_PROC__ */