Preload: Add orig field pointer passing to kernel 59/131759/2
authorAlexander Aksenov <a.aksenov@samsung.com>
Tue, 17 Jan 2017 13:35:40 +0000 (16:35 +0300)
committerAlexander Aksenov <a.aksenov@samsung.com>
Fri, 2 Jun 2017 12:33:22 +0000 (15:33 +0300)
This field can be filled only in kernel when using preload
mechanism. This commit implements only its passing to write
message handler.

This commit is related with swap-probe commit with the
same name.

Change-Id: Iaca284920862e144ebfc15282e08f3b98dcddae5
Signed-off-by: Alexander Aksenov <a.aksenov@samsung.com>
preload/preload_module.c
preload/preload_threads.c
preload/preload_threads.h

index 0ab869f..0c5a1f4 100644 (file)
@@ -24,8 +24,11 @@ enum {
        HANDLER_RUNNING = 0x1
 };
 
-static struct dentry *handler_dentry = NULL;
-
+struct user_ptrs {
+       char *caller;
+       char *orig;
+       char *call_type;
+};
 
 static inline struct pd_t *__get_process_data(struct uretprobe *rp)
 {
@@ -102,10 +105,18 @@ static inline bool __should_drop(struct sspt_ip *ip, enum preload_call_type ct)
 }
 
 static inline int __msg_sanitization(char *user_msg, size_t len,
-                                    char *call_type_p, char *caller_p)
+                                    struct user_ptrs *ptrs)
 {
-       if ((call_type_p < user_msg) || (call_type_p > user_msg + len) ||
-           (caller_p < user_msg) || (caller_p > user_msg + len))
+       if (ptrs->caller &&
+           (ptrs->caller < user_msg || ptrs->caller > user_msg + len))
+               return -EINVAL;
+
+       if (ptrs->orig &&
+           (ptrs->orig < user_msg || ptrs->orig > user_msg + len))
+               return -EINVAL;
+
+       if (ptrs->call_type &&
+           (ptrs->call_type < user_msg || ptrs->call_type > user_msg + len))
                return -EINVAL;
 
        return 0;
@@ -122,10 +133,9 @@ static unsigned long __do_preload_entry(struct uretprobe_instance *ri,
        unsigned long offset = ip->desc->info.pl_i.handler;
        unsigned long vaddr = 0;
        unsigned long base;
-       unsigned long disable_addr;
-       unsigned long caddr;
        struct vm_area_struct *cvma;
-       enum preload_call_type ct;
+       struct pd_t *pd;
+       struct pt_data_t ptd;
 
        base = lpd_get_handlers_base(hd);
        if (base == 0)
@@ -133,40 +143,29 @@ static unsigned long __do_preload_entry(struct uretprobe_instance *ri,
 
        /* jump to preloaded handler */
        vaddr = base + offset;
-       if (vaddr) {
-               caddr = get_regs_ret_func(regs);
-               cvma = __get_vma_by_addr(current, caddr);
-               ct = pc_call_type(ip, (void *)caddr);
-               disable_addr = __is_probe_non_block(ip) ? ip->orig_addr : 0;
-
-               /* jump only if caller is instumented and it is not a system lib -
-                * this leads to some errors */
-               if (cvma != NULL && cvma->vm_file != NULL &&
-                       cvma->vm_file->f_path.dentry != NULL) {
-
-                       struct dentry *dentry = cvma->vm_file->f_path.dentry;
-                       struct pd_t *pd = lpd_get_parent_pd(hd);
-
-                       if (!pc_check_dentry_is_ignored(dentry) &&
-                           __check_flag_and_call_type(ip, ct) &&
-                           !__is_handlers_call(cvma, pd)) {
-
-                               bool drop = __should_drop(ip, ct);
-                               if (pt_set_data(current, caddr,
-                                                            ct, disable_addr,
-                                                            drop) != 0)
-                                       printk(PRELOAD_PREFIX "Error! Failed "
-                                              "to set caller 0x%lx for "
-                                              "%d/%d\n", caddr,
-                                              current->tgid,
-                                              current->pid);
-                               /* args are not changed */
-                               loader_module_prepare_ujump(ri, regs, vaddr);
-                               if (disable_addr == 0)
-                                       pt_set_flags(current,
-                                                         HANDLER_RUNNING);
-                       }
-               }
+       ptd.caller = get_regs_ret_func(regs);
+       cvma = __get_vma_by_addr(current, ptd.caller);
+       ptd.call_type = pc_call_type(ip, (void *)ptd.caller);
+       ptd.disable_addr = __is_probe_non_block(ip) ? ip->orig_addr : 0;
+       ptd.orig = ip->orig_addr;
+       pd = lpd_get_parent_pd(hd);
+
+       /* jump only if caller is instumented and it is not a system lib -
+        * this leads to some errors
+        */
+       if (cvma != NULL && cvma->vm_file != NULL &&
+           !pc_check_dentry_is_ignored(cvma->vm_file->f_path.dentry) &&
+           __check_flag_and_call_type(ip, ptd.call_type) &&
+           !__is_handlers_call(cvma, pd)) {
+
+               ptd.drop = __should_drop(ip, ptd.call_type);
+               if (pt_set_data(current, &ptd) != 0)
+                       printk(PRELOAD_PREFIX "Error! Failed to store data for %d/%d\n",
+                              current->tgid, current->pid);
+               /* args are not changed */
+               loader_module_prepare_ujump(ri, regs, vaddr);
+               if (ptd.disable_addr == 0)
+                       pt_set_flags(current, HANDLER_RUNNING);
        }
 
        return vaddr;
@@ -268,61 +267,96 @@ static int preload_us_ret(struct uretprobe_instance *ri, struct pt_regs *regs)
 
 
 
-static void __write_data_to_msg(char *msg, size_t len,
-                               unsigned long call_type_off,
-                               unsigned long caller_off,
-                               unsigned long caller_addr)
+static void __write_data_on_demand(char *user, char *msg, size_t len,
+                                  struct user_ptrs *ptrs,
+                                  unsigned long caller_addr)
 {
+       unsigned long caller_ptr = 0, orig_ptr = 0, ct_ptr = 0;
+       unsigned long caller = 0, orig = 0;
        unsigned char call_type = 0;
-       unsigned long caller = 0;
        int ret;
 
-       if (caller_addr != 0) {
+       /* Evaluate addresses whereto write data as:
+        * pointer from user - user buffer beggining + kernel buffer beginning
+        *  If incoming pointer is NULL - do not write
+        */
+
+       if (ptrs->orig) {
+               orig_ptr = (unsigned long)(ptrs->orig - user + msg);
+
+               ret = pt_get_orig(current, &orig);
+               if (ret) {
+                       orig = 0xbadbeef;
+                       printk(PRELOAD_PREFIX "No orig for %d/%d\n",
+                              current->tgid, current->pid);
+               }
+
+               /* Types should be the same as in preload lib!!! */
+               *(uint64_t *)orig_ptr = (uint64_t)orig;
+       }
+
+       if (caller_addr && ptrs->caller && ptrs->call_type) {
+               caller_ptr = (unsigned long)(ptrs->caller - user + msg);
+               ct_ptr = (unsigned long)(ptrs->call_type - user + msg);
+
                caller = caller_addr;
                call_type =
                    pc_call_type_always_inst((void *)caller);
-       } else {
+
+               /* Types should be the same as in preload lib!!! */
+               *(uint64_t *)caller_ptr = (uint64_t)caller;
+               *(uint32_t *)ct_ptr = (uint32_t)call_type;
+
+               return;
+       }
+
+       if (ptrs->caller) {
+               caller_ptr = (unsigned long)(ptrs->caller - user + msg);
+
                ret = pt_get_caller(current, &caller);
-               if (ret != 0) {
+               if (ret) {
                        caller = 0xbadbeef;
-                       printk(PRELOAD_PREFIX "Error! Cannot get caller address"
-                              " for %d/%d\n", current->tgid, current->pid);
+                       printk(PRELOAD_PREFIX "No caller for %d/%d\n",
+                              current->tgid, current->pid);
                }
 
+               /* Types should be the same as in preload lib!!! */
+               *(uint64_t *)caller_ptr = (uint64_t)caller;
+       }
+
+       if (ptrs->call_type) {
+               ct_ptr = (unsigned long)(ptrs->call_type - user + msg);
+
                ret = pt_get_call_type(current, &call_type);
-               if (ret != 0) {
+               if (ret) {
                        call_type = 0xff;
-                       printk(PRELOAD_PREFIX "Error! Cannot get call type for "
-                              "%d/%d\n", current->tgid, current->pid);
+                       printk(PRELOAD_PREFIX "No call type for %d/%d\n",
+                              current->tgid, current->pid);
                }
-       }
 
-       /* Using the same types as in the library. */
-       *(uint32_t *)(msg + call_type_off) = (uint32_t)call_type;
-       *(uintptr_t *)(msg + caller_off) = (uintptr_t)caller;
+               /* Types should be the same as in preload lib!!! */
+               *(uint32_t *)ct_ptr = (uint32_t)call_type;
+       }
 }
 
 static int write_msg_handler(struct uprobe *p, struct pt_regs *regs)
 {
-       char *user_buf;
-       char *buf;
-       char *caller_p;
-       char *call_type_p;
+       struct user_ptrs ptrs;
+       char *user_buf, *buf;
        size_t len;
-       unsigned long caller_offset;
-       unsigned long call_type_offset;
        unsigned long caller_addr;
        int ret;
 
        /* FIXME: swap_get_uarg uses get_user(), it might sleep */
        user_buf = (char *)swap_get_uarg(regs, 0);
        len = swap_get_uarg(regs, 1);
-       call_type_p = (char *)swap_get_uarg(regs, 2);
-       caller_p = (char *)swap_get_uarg(regs, 3);
+       ptrs.call_type = (char *)swap_get_uarg(regs, 2);
+       ptrs.caller = (char *)swap_get_uarg(regs, 3);
        caller_addr = swap_get_uarg(regs, 4);
+       ptrs.orig = (char *)swap_get_uarg(regs, 5);
 
-       if (call_type_p != NULL || caller_p != NULL) {
-               ret = __msg_sanitization(user_buf, len, call_type_p, caller_p);
+       if (ptrs.caller || ptrs.call_type || ptrs.orig) {
+               ret = __msg_sanitization(user_buf, len, &ptrs);
                if (ret != 0) {
                        printk(PRELOAD_PREFIX "Invalid message pointers!\n");
                        return 0;
@@ -346,17 +380,7 @@ static int write_msg_handler(struct uprobe *p, struct pt_regs *regs)
                goto write_msg_fail;
        }
 
-
-       if (call_type_p != NULL || caller_p != NULL) {
-               /* Evaluating call_type and caller offset in message:
-                * data offset = data pointer - beginning of the message.
-                */
-               call_type_offset = (unsigned long)(call_type_p - user_buf);
-               caller_offset = (unsigned long)(caller_p - user_buf);
-
-               __write_data_to_msg(buf, len, call_type_offset, caller_offset,
-                                   caller_addr);
-       }
+       __write_data_on_demand(user_buf, buf, len, &ptrs, caller_addr);
 
        ret = swap_msg_raw(buf, len);
        if (ret != len)
index 675fc18..1d49d6b 100644 (file)
@@ -19,6 +19,7 @@ struct thread_slot {
        struct list_head disabled_addrs;
 
        unsigned long caller;
+       unsigned long orig;
        unsigned char call_type;
        bool drop;   /* TODO Workaround, remove when will be possible to install
                      * several us probes at the same addr. */
@@ -91,6 +92,7 @@ static inline void __remove_whole_disable_list(struct thread_slot *slot)
 static inline void __init_slot(struct thread_slot *slot)
 {
        slot->caller = 0;
+       slot->orig = 0;
        slot->call_type = 0;
        slot->drop = false;
        INIT_LIST_HEAD(&slot->disabled_addrs);
@@ -104,9 +106,11 @@ static inline void __reinit_slot(struct thread_slot *slot)
 
 static inline void __set_slot(struct thread_slot *slot,
                              struct task_struct *task, unsigned long caller,
-                             unsigned char call_type, bool drop)
+                             unsigned long orig, unsigned char call_type,
+                             bool drop)
 {
        slot->caller = caller;
+       slot->orig = orig;
        slot->call_type = call_type;
        slot->drop = drop;
 }
@@ -176,13 +180,21 @@ static inline struct thread_slot *__get_task_slot(struct task_struct *task)
 
 
 
-int pt_set_data(struct task_struct *task, unsigned long caller,
-                unsigned char call_type, unsigned long disable_addr, bool drop)
+int pt_set_data(struct task_struct *task, struct pt_data_t *data)
 {
        struct preload_td *td = get_preload_td(task);
        struct thread_slot *slot;
+       unsigned long caller, disable_addr, orig;
+       unsigned char call_type;
+       bool drop;
        int ret = 0;
 
+       caller = data->caller;
+       disable_addr = data->disable_addr;
+       orig = data->orig;
+       call_type = data->call_type;
+       drop = data->drop;
+
        slot = __grow_slot();
        if (slot == NULL) {
                ret = -ENOMEM;
@@ -196,7 +208,7 @@ int pt_set_data(struct task_struct *task, unsigned long caller,
                goto set_data_done;
        }
 
-       __set_slot(slot, task, caller, call_type, drop);
+       __set_slot(slot, task, caller, orig, call_type, drop);
        list_add_tail(&slot->list, &td->slots);
 
 set_data_done:
@@ -206,56 +218,52 @@ set_data_done:
 int pt_get_caller(struct task_struct *task, unsigned long *caller)
 {
        struct thread_slot *slot;
-       int ret = 0;
 
        slot = __get_task_slot(task);
        if (slot != NULL) {
                *caller = slot->caller;
-               goto get_caller_done;
+               return 0;
        }
 
-       /* If we're here - slot was not found */
-       ret = -EINVAL;
+       return -EINVAL;
+}
 
-get_caller_done:
-       return ret;
+int pt_get_orig(struct task_struct *task, unsigned long *orig)
+{
+       struct thread_slot *slot;
+
+       slot = __get_task_slot(task);
+       if (slot != NULL) {
+               *orig = slot->caller;
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 int pt_get_call_type(struct task_struct *task,
                                  unsigned char *call_type)
 {
        struct thread_slot *slot;
-       int ret = 0;
 
        slot = __get_task_slot(task);
        if (slot != NULL) {
                *call_type = slot->call_type;
-               goto get_call_type_done;
+               return 0;
        }
 
-       /* If we're here - slot was not found */
-       ret = -EINVAL;
-
-get_call_type_done:
-       return ret;
+       return -EINVAL;
 }
 
 int pt_get_drop(struct task_struct *task)
 {
        struct thread_slot *slot;
-       int ret = 0;
 
        slot = __get_task_slot(task);
-       if (slot != NULL) {
-               ret = (int) slot->drop;
-               goto get_drop_done;
-       }
-
-       /* If we're here - slot was not found */
-       ret = -EINVAL;
+       if (slot != NULL)
+               return (int)slot->drop;
 
-get_drop_done:
-       return ret;
+       return -EINVAL;
 }
 
 bool pt_check_disabled_probe(struct task_struct *task, unsigned long addr)
@@ -298,10 +306,8 @@ int pt_put_data(struct task_struct *task)
        if (slot != NULL) {
                __reinit_slot(slot);
                __clean_slot(slot); /* remove from list */
-               goto put_data_done;
        }
 
-put_data_done:
        return ret;
 }
 
index 409320a..43844c7 100644 (file)
@@ -3,16 +3,25 @@
 
 struct task_struct;
 
+struct pt_data_t {
+       unsigned long caller;
+       unsigned long disable_addr;
+       unsigned long orig;
+       unsigned char call_type;
+       bool drop;
+};
+
 unsigned long pt_get_flags(struct task_struct *task);
 void pt_set_flags(struct task_struct *task, unsigned long flags);
 
-int pt_set_data(struct task_struct *task, unsigned long caller,
-                unsigned char call_type, unsigned long disable_addr,
-                bool drop);
+int pt_set_data(struct task_struct *task, struct pt_data_t *data);
+
 int pt_get_caller(struct task_struct *task, unsigned long *caller);
+int pt_get_orig(struct task_struct *task, unsigned long *orig);
 int pt_get_call_type(struct task_struct *task, unsigned char *call_type);
 int pt_get_drop(struct task_struct *task);
 bool pt_check_disabled_probe(struct task_struct *task, unsigned long addr);
+
 void pt_enable_probe(struct task_struct *task, unsigned long addr);
 int pt_put_data(struct task_struct *task);
 int pt_init(void);