From c3aaa2488bd650e0396accc84e75c1f4de913f97 Mon Sep 17 00:00:00 2001 From: Nikita Kalyazin Date: Fri, 19 Apr 2013 15:28:21 +0400 Subject: [PATCH] [FIX] __switch_to instrumentation on x86 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 | 2 +- kprobe/arch/asm-arm/dbi_kprobes.h | 6 ++++++ kprobe/arch/asm-x86/dbi_kprobes.c | 4 ++-- kprobe/arch/asm-x86/dbi_kprobes.h | 11 +++++++++-- kprobe/arch/dbi_kprobes.h | 2 +- kprobe/dbi_kprobes.c | 21 ++++++++++++++------- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/kprobe/arch/asm-arm/dbi_kprobes.c b/kprobe/arch/asm-arm/dbi_kprobes.c index df89496..c976d79 100644 --- a/kprobe/arch/asm-arm/dbi_kprobes.c +++ b/kprobe/arch/asm-arm/dbi_kprobes.c @@ -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); diff --git a/kprobe/arch/asm-arm/dbi_kprobes.h b/kprobe/arch/asm-arm/dbi_kprobes.h index ff7dd5f..7f6f59e 100644 --- a/kprobe/arch/asm-arm/dbi_kprobes.h +++ b/kprobe/arch/asm-arm/dbi_kprobes.h @@ -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; diff --git a/kprobe/arch/asm-x86/dbi_kprobes.c b/kprobe/arch/asm-x86/dbi_kprobes.c index 60404d9..29d2c24 100644 --- a/kprobe/arch/asm-x86/dbi_kprobes.c +++ b/kprobe/arch/asm-x86/dbi_kprobes.c @@ -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); } } diff --git a/kprobe/arch/asm-x86/dbi_kprobes.h b/kprobe/arch/asm-x86/dbi_kprobes.h index c3b1741..59456f7 100644 --- a/kprobe/arch/asm-x86/dbi_kprobes.h +++ b/kprobe/arch/asm-x86/dbi_kprobes.h @@ -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) diff --git a/kprobe/arch/dbi_kprobes.h b/kprobe/arch/dbi_kprobes.h index 695ecd4..b1237e1 100644 --- a/kprobe/arch/dbi_kprobes.h +++ b/kprobe/arch/dbi_kprobes.h @@ -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); diff --git a/kprobe/dbi_kprobes.c b/kprobe/dbi_kprobes.c index 4e401dd..770e7c9 100644 --- a/kprobe/dbi_kprobes.c +++ b/kprobe/dbi_kprobes.c @@ -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); -- 2.7.4