[FIX] __switch_to instrumentation on x86
authorNikita Kalyazin <n.kalyazin@samsung.com>
Fri, 19 Apr 2013 11:28:21 +0000 (15:28 +0400)
committerNikita Kalyazin <n.kalyazin@samsung.com>
Fri, 19 Apr 2013 11:44:13 +0000 (15:44 +0400)
What's been done here:
 - patch next task: value on top of next's stack is patched;
 - for that we need for regs structure (containing sp register);
 - it requires changing API of the following functions:
   - patch_suspended_task(): added regs argument;
   - set_task_trampoline(): task -> patch address;
   - arch_get_patch_addr() added (takes task and regs and returns
 address to patch).

Known issues:
 - x86 __switch_to instrumentation won't work at multi-core systems
   (because there is no code, that removes retprobes on user stop;
   because there is no regs argument passed to
   dbi_unregister_kretprobe());
 - I've left both old arch_get_task_pc()/arch_set_task_pc() and new
   arch_get_patch_addr() APIs.  They do rougly same things.

Tested on:
 - i386 Qemu, Buildroot rootfs, Linux kernel 3.8.2;
 - ARM Qemu, Buildroot rootfs, Linux kernel 3.8.2;
 - U1HD, GT-I8800_c210v30_cluster_20121002_1.

kprobe/arch/asm-arm/dbi_kprobes.c
kprobe/arch/asm-arm/dbi_kprobes.h
kprobe/arch/asm-x86/dbi_kprobes.c
kprobe/arch/asm-x86/dbi_kprobes.h
kprobe/arch/dbi_kprobes.h
kprobe/dbi_kprobes.c

index df89496..c976d79 100644 (file)
@@ -1263,7 +1263,7 @@ int setjmp_pre_handler (struct kprobe *p, struct pt_regs *regs)
        {
                if(!p->tgid && ((unsigned int)p->addr == sched_addr) && sched_rp) {
                        struct thread_info *tinfo = (struct thread_info *)regs->ARM_r2;
-                       patch_suspended_task(sched_rp, tinfo->task);
+                       patch_suspended_task(sched_rp, tinfo->task, regs);
                }
                if (pre_entry)
                        p->ss_addr = (void *)pre_entry (jp->priv_arg, regs);
index ff7dd5f..7f6f59e 100644 (file)
@@ -66,6 +66,12 @@ typedef unsigned long kprobe_opcode_t;
 
 #define UREGS_OFFSET 8
 
+static inline unsigned long *arch_get_patch_addr(struct task_struct *p,
+                                                struct pt_regs *regs)
+{
+       return &task_thread_info(p)->cpu_context.pc;
+}
+
 static inline unsigned long arch_get_task_pc(struct task_struct *p)
 {
        return task_thread_info(p)->cpu_context.pc;
index 60404d9..29d2c24 100644 (file)
@@ -749,8 +749,8 @@ int setjmp_pre_handler (struct kprobe *p, struct pt_regs *regs)
        if (!p->tgid || (p->tgid == current->tgid)) {
                /* handle __switch_to probe */
                if(!p->tgid && (p->addr == sched_addr) && sched_rp) {
-                       struct thread_info *tinfo = NULL; //TODO implement for x86
-                       patch_suspended_task(sched_rp, tinfo->task);
+                       /* FIXME: Acutally 2nd parameter is not used for x86 */
+                       patch_suspended_task(sched_rp, (struct task_struct *)regs->dx, regs);
                }
        }
 
index c3b1741..59456f7 100644 (file)
@@ -91,14 +91,21 @@ typedef u8 kprobe_opcode_t;
 #define KPROBES_TRAMP_LEN              MAX_INSN_SIZE
 #define KPROBES_TRAMP_INSN_IDX          0
 
+static inline unsigned long *arch_get_patch_addr(struct task_struct *p,
+                                                struct pt_regs *regs)
+{
+       return (unsigned long *)kernel_stack_pointer(regs);
+}
+
 static inline unsigned long arch_get_task_pc(struct task_struct *p)
 {
-       return p->thread.ip;
+       /* FIXME: Not implemented yet */
+       return 0;
 }
 
 static inline void arch_set_task_pc(struct task_struct *p, unsigned long val)
 {
-       p->thread.ip = val;
+       /* FIXME: Not implemented yet */
 }
 
 static inline struct pt_regs *dbi_get_syscall_uregs(unsigned long sp)
index 695ecd4..b1237e1 100644 (file)
@@ -81,7 +81,7 @@ extern void arch_disarm_uprobe (struct kprobe *p, struct task_struct *tsk);
 extern void arch_disarm_uretprobe (struct kretprobe *p, struct task_struct *tsk);
 extern int arch_init_kprobes (void);
 extern void dbi_arch_exit_kprobes (void);
-extern int patch_suspended_task(struct kretprobe *rp, struct task_struct *tsk);
+extern int patch_suspended_task(struct kretprobe *rp, struct task_struct *tsk, struct pt_regs *);
 
 void dbi_arch_uprobe_return (void);
 
index 4e401dd..770e7c9 100644 (file)
@@ -802,15 +802,18 @@ struct kretprobe * clone_kretprobe (struct kretprobe *rp)
        return clone;
 }
 
-static void inline set_task_trampoline(struct task_struct *p, struct kretprobe_instance *ri, unsigned long tramp_addr)
+static void inline set_task_trampoline(unsigned long *patch_addr,
+                                      struct kretprobe_instance *ri,
+                                      unsigned long tramp_addr)
 {
-       unsigned long pc = arch_get_task_pc(p);
+       unsigned long pc = *patch_addr;
        if (pc == tramp_addr)
                panic("[%d] %s (%d/%d): pc = %08lx --- [%d] %s (%d/%d)\n",
-                               task_cpu(p), p->comm, p->tgid, p->pid, pc,
-                               task_cpu(current), current->comm, current->tgid, current->pid);
+                     task_cpu(ri->task), ri->task->comm, ri->task->tgid,
+                     ri->task->pid, pc, task_cpu(current), current->comm,
+                     current->tgid, current->pid);
        ri->ret_addr = (kprobe_opcode_t *)pc;
-       arch_set_task_pc(p, tramp_addr);
+       *patch_addr = tramp_addr;
 }
 
 static void inline rm_task_trampoline(struct task_struct *p, struct kretprobe_instance *ri)
@@ -872,11 +875,14 @@ static int dbi_disarm_krp_inst(struct kretprobe_instance *ri)
        return retval;
 }
 
-int patch_suspended_task(struct kretprobe *rp, struct task_struct *task)
+int patch_suspended_task(struct kretprobe *rp,
+                        struct task_struct *task,
+                        struct pt_regs *regs)
 {
        struct kretprobe_instance *ri;
        unsigned long flags;
        kprobe_opcode_t *tramp = (kprobe_opcode_t *)&kretprobe_trampoline;
+       unsigned long *patch_addr;
 
        spin_lock_irqsave(&kretprobe_lock, flags);
 
@@ -888,7 +894,8 @@ int patch_suspended_task(struct kretprobe *rp, struct task_struct *task)
        ri->rp2 = NULL;
        ri->task = task;
        ri->sp = NULL;
-       set_task_trampoline(task, ri, (unsigned long)tramp);
+       patch_addr = arch_get_patch_addr(task, regs);
+       set_task_trampoline(patch_addr, ri, (unsigned long)tramp);
        add_rp_inst(ri);
 
        spin_unlock_irqrestore(&kretprobe_lock, flags);