From 880aede762149f0ba657c8e80c38156af95755cc Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Thu, 7 Mar 2013 14:27:10 +0400 Subject: [PATCH] [REFACTOR] move dbi_uprobes from swap_kprobe module to swap_uprobe module --- driver/Makefile.am | 2 +- driver/us_proc_inst.c | 2 +- kprobe/Kbuild | 2 +- kprobe/arch/asm-arm/dbi_kprobes.c | 10 +- kprobe/arch/dbi_kprobes.c | 3 + kprobe/dbi_insn_slots.c | 4 +- kprobe/dbi_kprobes.c | 33 +- kprobe/dbi_uprobes.c | 445 ------------------------- uprobe/Makefile.am | 2 +- uprobe/swap_uprobes.c | 462 ++++++++++++++++++++++++++ kprobe/dbi_uprobes.h => uprobe/swap_uprobes.h | 0 11 files changed, 490 insertions(+), 475 deletions(-) delete mode 100644 kprobe/dbi_uprobes.c rename kprobe/dbi_uprobes.h => uprobe/swap_uprobes.h (100%) diff --git a/driver/Makefile.am b/driver/Makefile.am index f1c2a15..eead541 100644 --- a/driver/Makefile.am +++ b/driver/Makefile.am @@ -33,7 +33,7 @@ driver_module_dir = $(realpath $(srcdir)) module_name = swap_driver cross_compiler = $(subst gcc,,$(CC)) -inlude_opt = -I$(realpath $(top_srcdir)/src/modules/ksyms) -I$(realpath $(top_srcdir)/src/common) -I$(realpath $(top_srcdir)/src/profile) +inlude_opt = -I$(realpath $(top_srcdir)/src/modules/ksyms) -I$(realpath $(top_srcdir)/src/common) -I$(realpath $(top_srcdir)/src/profile) -I$(realpath $(top_srcdir)/src/modules/kprobe) extra_cflags = "$(inlude_opt) -DEC_ARCH_$(ARCH) -D__DEBUG $(memchecker) $(debug_opt) $(android_opt) $(slp_opt) $(android_app_opt) $(board_opt)" #bin_SCRIPTS = patchko.sh insmod.sh diff --git a/driver/us_proc_inst.c b/driver/us_proc_inst.c index 6ca87b9..541554d 100644 --- a/driver/us_proc_inst.c +++ b/driver/us_proc_inst.c @@ -19,7 +19,7 @@ #include "us_proc_inst.h" #include "../kprobe/dbi_kprobes_deps.h" -#include "../kprobe/dbi_uprobes.h" +#include "../uprobe/swap_uprobes.h" #include "sspt/sspt.h" #include "java_inst.h" diff --git a/kprobe/Kbuild b/kprobe/Kbuild index 975a317..964c88b 100644 --- a/kprobe/Kbuild +++ b/kprobe/Kbuild @@ -1,5 +1,5 @@ EXTRA_CFLAGS := $(extra_cflags) obj-m := swap_kprobe.o -swap_kprobe-y := dbi_kprobes_deps.o dbi_insn_slots.o dbi_uprobes.o arch/asm/dbi_kprobes.o arch/dbi_kprobes.o dbi_kprobes.o +swap_kprobe-y := dbi_kprobes_deps.o dbi_insn_slots.o arch/asm/dbi_kprobes.o arch/dbi_kprobes.o dbi_kprobes.o swap_kprobe-$(CONFIG_ARM) += arch/asm/dbi_kprobes_arm.o arch/asm/dbi_kprobes_thumb.o diff --git a/kprobe/arch/asm-arm/dbi_kprobes.c b/kprobe/arch/asm-arm/dbi_kprobes.c index a1d3e6a..e9cd526 100644 --- a/kprobe/arch/asm-arm/dbi_kprobes.c +++ b/kprobe/arch/asm-arm/dbi_kprobes.c @@ -39,7 +39,6 @@ #include "../../dbi_kdebug.h" #include "../../dbi_insn_slots.h" #include "../../dbi_kprobes_deps.h" -#include "../../dbi_uprobes.h" #include #include @@ -676,12 +675,14 @@ int arch_prepare_uprobe (struct kprobe *p, struct task_struct *task, int atomic) } return ret; } +EXPORT_SYMBOL_GPL(arch_prepare_uprobe); int arch_prepare_uretprobe (struct kretprobe *p, struct task_struct *task) { DBPRINTF("Warrning: arch_prepare_uretprobe is not implemented\n"); return 0; } +EXPORT_SYMBOL_GPL(arch_prepare_uretprobe); void prepare_singlestep (struct kprobe *p, struct pt_regs *regs) { @@ -1286,6 +1287,7 @@ int setjmp_pre_handler (struct kprobe *p, struct pt_regs *regs) return 1; } +EXPORT_SYMBOL_GPL(setjmp_pre_handler); void dbi_jprobe_return (void) { @@ -1294,6 +1296,7 @@ void dbi_jprobe_return (void) void dbi_arch_uprobe_return (void) { } +EXPORT_SYMBOL_GPL(dbi_arch_uprobe_return); int longjmp_break_handler (struct kprobe *p, struct pt_regs *regs) { @@ -1335,7 +1338,7 @@ int longjmp_break_handler (struct kprobe *p, struct pt_regs *regs) return 0; } - +EXPORT_SYMBOL_GPL(longjmp_break_handler); void arch_arm_kprobe (struct kprobe *p) { @@ -1493,7 +1496,8 @@ int trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs) if (!(hlist_unhashed(&is_p->is_hlist_thumb))) { hlist_del_rcu(&is_p->is_hlist_thumb); } - unregister_uprobe (&crp->kp, current, 1); + + dbi_unregister_kprobe(&crp->kp, current); kfree (crp); } hlist_del(current_node); diff --git a/kprobe/arch/dbi_kprobes.c b/kprobe/arch/dbi_kprobes.c index 18ea8a5..52ee35b 100644 --- a/kprobe/arch/dbi_kprobes.c +++ b/kprobe/arch/dbi_kprobes.c @@ -83,10 +83,12 @@ void arch_arm_uprobe (struct kprobe *p, struct task_struct *tsk) if (!write_proc_vm_atomic (tsk, (unsigned long) p->addr, &insn, sizeof (insn))) panic ("failed to write memory %p!\n", p->addr); } +EXPORT_SYMBOL_GPL(arch_arm_uprobe); void arch_arm_uretprobe (struct kretprobe *p, struct task_struct *tsk) { } +EXPORT_SYMBOL_GPL(arch_arm_uretprobe); void arch_disarm_uprobe (struct kprobe *p, struct task_struct *tsk) { @@ -99,6 +101,7 @@ EXPORT_SYMBOL_GPL(arch_disarm_uprobe); void arch_disarm_uretprobe (struct kretprobe *p, struct task_struct *tsk) { } +EXPORT_SYMBOL_GPL(arch_disarm_uretprobe); int arch_init_module_dependencies(void) { diff --git a/kprobe/dbi_insn_slots.c b/kprobe/dbi_insn_slots.c index c912577..fa883bd 100644 --- a/kprobe/dbi_insn_slots.c +++ b/kprobe/dbi_insn_slots.c @@ -46,7 +46,6 @@ */ #include "dbi_insn_slots.h" -#include "dbi_uprobes.h" #include "dbi_kdebug.h" #include @@ -55,12 +54,15 @@ #include #include +#include extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long pgoff); +struct hlist_head uprobe_insn_slot_table[KPROBE_TABLE_SIZE]; +EXPORT_SYMBOL_GPL(uprobe_insn_slot_table); struct hlist_head kprobe_insn_pages; struct hlist_head uprobe_insn_pages; diff --git a/kprobe/dbi_kprobes.c b/kprobe/dbi_kprobes.c index e3cc3ae..d8f89d4 100644 --- a/kprobe/dbi_kprobes.c +++ b/kprobe/dbi_kprobes.c @@ -55,7 +55,6 @@ #include "dbi_kdebug.h" #include "dbi_kprobes_deps.h" #include "dbi_insn_slots.h" -#include "dbi_uprobes.h" #include #include @@ -74,12 +73,15 @@ DEFINE_PER_CPU (struct kprobe *, current_kprobe) = NULL; static DEFINE_PER_CPU (struct kprobe_ctlblk, kprobe_ctlblk); DEFINE_SPINLOCK (kretprobe_lock); /* Protects kretprobe_inst_table */ +EXPORT_SYMBOL_GPL(kretprobe_lock); static DEFINE_PER_CPU (struct kprobe *, kprobe_instance) = NULL; struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; +EXPORT_SYMBOL_GPL(kprobe_table); static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; atomic_t kprobe_count; +EXPORT_SYMBOL_GPL(kprobe_count); void kretprobe_assert (struct kretprobe_instance *ri, unsigned long orig_ret_address, unsigned long trampoline_address) { @@ -157,6 +159,7 @@ struct kprobe *get_kprobe(kprobe_opcode_t *addr, pid_t tgid) DBPRINTF ("get_kprobe: probe %p", retVal); return retVal; } +EXPORT_SYMBOL_GPL(get_kprobe); /* * Aggregate handlers for multiple kprobes support - these handlers @@ -280,6 +283,7 @@ struct kretprobe_instance *get_used_rp_inst (struct kretprobe *rp) hlist_for_each_entry (ri, node, &rp->used_instances, uflist) return ri; return NULL; } +EXPORT_SYMBOL_GPL(get_used_rp_inst); /* Called with kretprobe_lock held */ void add_rp_inst (struct kretprobe_instance *ri) @@ -327,30 +331,13 @@ void recycle_rp_inst (struct kretprobe_instance *ri) hlist_del (&ri->hlist); } } - -int dbi_disarm_urp_inst(struct kretprobe_instance *ri, struct task_struct *rm_task); - -/* Called with kretprobe_lock held */ -int dbi_disarm_urp_inst_for_task(struct task_struct *parent, struct task_struct *task) -{ - struct kretprobe_instance *ri; - struct hlist_node *node, *tmp; - struct hlist_head *head = kretprobe_inst_table_head(parent->mm); - - hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { - if (parent == ri->task && ri->rp->kp.tgid) { - dbi_disarm_urp_inst(ri, task); - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(dbi_disarm_urp_inst_for_task); +EXPORT_SYMBOL_GPL(recycle_rp_inst); struct hlist_head * kretprobe_inst_table_head (void *hash_key) { return &kretprobe_inst_table[hash_ptr (hash_key, KPROBE_HASH_BITS)]; } +EXPORT_SYMBOL_GPL(kretprobe_inst_table_head); void free_rp_inst (struct kretprobe *rp) { @@ -361,6 +348,7 @@ void free_rp_inst (struct kretprobe *rp) kfree (ri); } } +EXPORT_SYMBOL_GPL(free_rp_inst); /* * Keep all fields in the kprobe consistent @@ -482,6 +470,7 @@ int register_aggr_kprobe (struct kprobe *old_p, struct kprobe *p) } return ret; } +EXPORT_SYMBOL_GPL(register_aggr_kprobe); int dbi_register_kprobe (struct kprobe *p) { @@ -641,6 +630,7 @@ int pre_handler_kretprobe (struct kprobe *p, struct pt_regs *regs) DBPRINTF ("END\n"); return 0; } +EXPORT_SYMBOL_GPL(pre_handler_kretprobe); struct kretprobe *sched_rp; @@ -801,6 +791,7 @@ struct kretprobe * clone_kretprobe (struct kretprobe *rp) return clone; } +EXPORT_SYMBOL_GPL(clone_kretprobe); static void inline set_task_trampoline(struct task_struct *p, struct kretprobe_instance *ri, unsigned long tramp_addr) { @@ -905,8 +896,6 @@ static int __init init_kprobes (void) { INIT_HLIST_HEAD (&kprobe_table[i]); INIT_HLIST_HEAD (&kretprobe_inst_table[i]); - - init_uprobes_insn_slots(i); } atomic_set (&kprobe_count, 0); diff --git a/kprobe/dbi_uprobes.c b/kprobe/dbi_uprobes.c deleted file mode 100644 index 7af607f..0000000 --- a/kprobe/dbi_uprobes.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Dynamic Binary Instrumentation Module based on KProbes - * modules/kprobe/dbi_uprobes.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Copyright (C) Samsung Electronics, 2006-2010 - * - * 2008-2009 Alexey Gerenkov User-Space - * Probes initial implementation; Support x86/ARM/MIPS for both user and kernel spaces. - * 2010 Ekaterina Gorelkina : redesign module for separating core and arch parts - * - */ - - -#include "dbi_uprobes.h" -#include "dbi_insn_slots.h" -#include "dbi_kdebug.h" - -#include -#include -#include - -struct hlist_head uprobe_insn_slot_table[KPROBE_TABLE_SIZE]; - - -#define DEBUG_PRINT_HASH_TABLE 0 - -#if DEBUG_PRINT_HASH_TABLE -void print_kprobe_hash_table(void) -{ - int i; - struct hlist_head *head; - struct hlist_node *node; - struct kprobe *p; - - // print uprobe table - for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { - head = &kprobe_table[i]; - hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) { - printk("####### find K tgid=%u, addr=%x\n", - p->tgid, p->addr); - } - } -} - -void print_kretprobe_hash_table(void) -{ - int i; - struct hlist_head *head; - struct hlist_node *node; - struct kprobe *p; - - // print uprobe table - for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { - head = &kretprobe_inst_table[i]; - hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) { - printk("####### find KR tgid=%u, addr=%x\n", - p->tgid, p->addr); - } - } -} - -void print_uprobe_hash_table(void) -{ - int i; - struct hlist_head *head; - struct hlist_node *node; - struct kprobe *p; - - // print uprobe table - for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { - head = &uprobe_insn_slot_table[i]; - hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) { - printk("####### find U tgid=%u, addr=%x\n", - p->tgid, p->addr); - } - } -} -#endif - - -static void add_uprobe_table(struct kprobe *p) -{ -#ifdef CONFIG_ARM - INIT_HLIST_NODE(&p->is_hlist_arm); - hlist_add_head_rcu(&p->is_hlist_arm, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn_arm, KPROBE_HASH_BITS)]); - INIT_HLIST_NODE(&p->is_hlist_thumb); - hlist_add_head_rcu(&p->is_hlist_thumb, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn_thumb, KPROBE_HASH_BITS)]); -#else /* CONFIG_ARM */ - INIT_HLIST_NODE(&p->is_hlist); - hlist_add_head_rcu(&p->is_hlist, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn, KPROBE_HASH_BITS)]); -#endif /* CONFIG_ARM */ -} - - -static int __register_uprobe(struct kprobe *p, struct task_struct *task, int atomic) -{ - int ret = 0; - struct kprobe *old_p; - - if (!p->addr) { - return -EINVAL; - } - - DBPRINTF("p->addr = 0x%p p = 0x%p\n", p->addr, p); - -// thumb address = address-1; -#if defined(CONFIG_ARM) - // TODO: must be corrected in 'bundle' - if ((unsigned long) p->addr & 0x01) { - p->addr = (kprobe_opcode_t *)((unsigned long)p->addr & 0xfffffffe); - } -#endif - - p->mod_refcounted = 0; - p->nmissed = 0; - INIT_LIST_HEAD(&p->list); -#ifdef KPROBES_PROFILE - p->start_tm.tv_sec = p->start_tm.tv_usec = 0; - p->hnd_tm_sum.tv_sec = p->hnd_tm_sum.tv_usec = 0; - p->count = 0; -#endif - - // get the first item - old_p = get_kprobe(p->addr, p->tgid); - if (old_p) { -#ifdef CONFIG_ARM - p->safe_arm = old_p->safe_arm; - p->safe_thumb = old_p->safe_thumb; -#endif - ret = register_aggr_kprobe(old_p, p); - if (!ret) { - atomic_inc(&kprobe_count); - add_uprobe_table(p); - } - DBPRINTF("goto out\n", ret); - goto out; - } - - ret = arch_prepare_uprobe(p, task, atomic); - if (ret) { - DBPRINTF("goto out\n", ret); - goto out; - } - - DBPRINTF ("before out ret = 0x%x\n", ret); - - // TODO: add uprobe (must be in function) - INIT_HLIST_NODE(&p->hlist); - hlist_add_head_rcu(&p->hlist, &kprobe_table[hash_ptr (p->addr, KPROBE_HASH_BITS)]); - add_uprobe_table(p); - arch_arm_uprobe(p, task); - -out: - DBPRINTF("out ret = 0x%x\n", ret); - return ret; -} - -void unregister_uprobe(struct kprobe *p, struct task_struct *task, int atomic) -{ - dbi_unregister_kprobe (p, task); -} - - -int dbi_register_ujprobe(struct task_struct *task, struct jprobe *jp, int atomic) -{ - int ret = 0; - - /* Todo: Verify probepoint is a function entry point */ - jp->kp.pre_handler = setjmp_pre_handler; - jp->kp.break_handler = longjmp_break_handler; - - ret = __register_uprobe(&jp->kp, task, atomic); - - return ret; -} - -void dbi_unregister_ujprobe(struct task_struct *task, struct jprobe *jp, int atomic) -{ - unregister_uprobe(&jp->kp, task, atomic); - /* - * Here is an attempt to unregister even those probes that have not been - * installed (hence not added to the hlist). - * So if we try to delete them from the hlist we will get NULL pointer - * dereference error. That is why we check whether this node - * really belongs to the hlist. - */ -#ifdef CONFIG_ARM - if (!(hlist_unhashed(&jp->kp.is_hlist_arm))) { - hlist_del_rcu(&jp->kp.is_hlist_arm); - } - if (!(hlist_unhashed(&jp->kp.is_hlist_thumb))) { - hlist_del_rcu(&jp->kp.is_hlist_thumb); - } -#else /* CONFIG_ARM */ - if (!(hlist_unhashed(&jp->kp.is_hlist))) { - hlist_del_rcu(&jp->kp.is_hlist); - } -#endif /* CONFIG_ARM */ -} - -int dbi_register_uretprobe(struct task_struct *task, struct kretprobe *rp, int atomic) -{ - int i, ret = 0; - struct kretprobe_instance *inst; - - DBPRINTF ("START\n"); - - rp->kp.pre_handler = pre_handler_kretprobe; - rp->kp.post_handler = NULL; - rp->kp.fault_handler = NULL; - rp->kp.break_handler = NULL; - - rp->disarm = 0; - - /* Pre-allocate memory for max kretprobe instances */ - if (rp->maxactive <= 0) { -#if 1//def CONFIG_PREEMPT - rp->maxactive = max(10, 2 * NR_CPUS); -#else - rp->maxactive = NR_CPUS; -#endif - } - - INIT_HLIST_HEAD(&rp->used_instances); - INIT_HLIST_HEAD(&rp->free_instances); - - for (i = 0; i < rp->maxactive; i++) { - inst = kmalloc(sizeof(*inst), GFP_KERNEL); - if (inst == NULL) { - free_rp_inst (rp); - ret = -ENOMEM; - goto out; - } - - INIT_HLIST_NODE(&inst->uflist); - hlist_add_head(&inst->uflist, &rp->free_instances); - } - - rp->nmissed = 0; - - /* Establish function exit probe point */ - ret = arch_prepare_uretprobe(rp, task); - if (ret) { - goto out; - } - - /* Establish function entry probe point */ - ret = __register_uprobe(&rp->kp, task, atomic); - if (ret) { - free_rp_inst(rp); - goto out; - } - - arch_arm_uretprobe(rp, task); -out: - return ret; -} - -int dbi_disarm_urp_inst(struct kretprobe_instance *ri, struct task_struct *rm_task) -{ - struct task_struct *task = rm_task ? rm_task : ri->task; - kprobe_opcode_t *tramp = (kprobe_opcode_t *)(ri->rp->kp.ainsn.insn + - UPROBES_TRAMP_RET_BREAK_IDX); - kprobe_opcode_t *stack = ri->sp - RETPROBE_STACK_DEPTH + 1; - kprobe_opcode_t *found = NULL; - kprobe_opcode_t *buf[RETPROBE_STACK_DEPTH]; - int i, retval; - - retval = read_proc_vm_atomic(task, (unsigned long)stack, buf, sizeof(buf)); - if (retval != sizeof(buf)) { - printk("---> %s (%d/%d): failed to read stack from %08lx", - task->comm, task->tgid, task->pid, (unsigned long)stack); - retval = -EFAULT; - goto out; - } - - /* search the stack from the bottom */ - for (i = RETPROBE_STACK_DEPTH - 1; i >= 0; i--) { - if (buf[i] == tramp) { - found = stack + i; - break; - } - } - - if (found) { - printk("---> %s (%d/%d): trampoline found at %08lx (%08lx /%+d) - %p\n", - task->comm, task->tgid, task->pid, - (unsigned long)found, (unsigned long)ri->sp, - found - ri->sp, ri->rp->kp.addr); - retval = write_proc_vm_atomic(task, (unsigned long)found, &ri->ret_addr, - sizeof(ri->ret_addr)); - if (retval != sizeof(ri->ret_addr)) { - printk("---> %s (%d/%d): failed to write value to %08lx", - task->comm, task->tgid, task->pid, (unsigned long)found); - retval = -EFAULT; - } else { - retval = 0; - } - } else { - struct pt_regs *uregs = task_pt_regs(ri->task); - unsigned long ra = dbi_get_ret_addr(uregs); - if (ra == (unsigned long)tramp) { - printk("---> %s (%d/%d): trampoline found at lr = %08lx - %p\n", - task->comm, task->tgid, task->pid, ra, ri->rp->kp.addr); - dbi_set_ret_addr(uregs, (unsigned long)tramp); - retval = 0; - } else { - printk("---> %s (%d/%d): trampoline NOT found at sp = %08lx, lr = %08lx - %p\n", - task->comm, task->tgid, task->pid, - (unsigned long)ri->sp, ra, ri->rp->kp.addr); - retval = -ENOENT; - } - } - -out: - return retval; -} - -void dbi_unregister_uretprobe(struct task_struct *task, struct kretprobe *rp, int atomic, int not_rp2) -{ - unsigned long flags; - struct kretprobe_instance *ri; - struct kretprobe *rp2 = NULL; - - spin_lock_irqsave (&kretprobe_lock, flags); - - while ((ri = get_used_rp_inst(rp)) != NULL) { - if (dbi_disarm_urp_inst(ri, NULL) != 0) - /*panic*/printk("%s (%d/%d): cannot disarm urp instance (%08lx)\n", - ri->task->comm, ri->task->tgid, ri->task->pid, - (unsigned long)rp->kp.addr); - recycle_rp_inst(ri); - } - - if (hlist_empty(&rp->used_instances) || not_rp2) { - struct kprobe *p = &rp->kp; - // if there are no used retprobe instances (i.e. function is not entered) - disarm retprobe - arch_disarm_uretprobe(rp, task);//vmas[1], pages[1], kaddrs[1]); -#ifdef CONFIG_ARM - if (!(hlist_unhashed(&p->is_hlist_arm))) { - hlist_del_rcu(&p->is_hlist_arm); - } - - if (!(hlist_unhashed(&p->is_hlist_thumb))) { - hlist_del_rcu(&p->is_hlist_thumb); - } -#else /* CONFIG_ARM */ - if (!(hlist_unhashed(&p->is_hlist))) { - hlist_del_rcu(&p->is_hlist); - } -#endif /* CONFIG_ARM */ - } else { - struct kprobe *new_p = NULL; - struct kprobe *p = &rp->kp; - rp2 = clone_kretprobe(rp); - if (!rp2) { - DBPRINTF ("dbi_unregister_uretprobe addr %p: failed to clone retprobe!", rp->kp.addr); - } else { - DBPRINTF ("initiating deferred retprobe deletion addr %p", rp->kp.addr); - printk ("initiating deferred retprobe deletion addr %p\n", rp->kp.addr); - arch_disarm_uprobe(&rp->kp, task); - rp2->disarm = 1; - } - /* - * As we cloned retprobe we have to update the entry in the insn slot - * hash list. - */ -#ifdef CONFIG_ARM - if (!(hlist_unhashed(&p->is_hlist_arm))) { - hlist_del_rcu(&p->is_hlist_arm); - } - if (!(hlist_unhashed(&p->is_hlist_thumb))) { - hlist_del_rcu(&p->is_hlist_thumb); - } -#else /* CONFIG_ARM */ - if (!(hlist_unhashed(&p->is_hlist))) { - hlist_del_rcu(&p->is_hlist); - } -#endif /* CONFIG_ARM */ - new_p = &rp2->kp; - add_uprobe_table(new_p); - } - - while ((ri = get_used_rp_inst(rp)) != NULL) { - ri->rp = NULL; - ri->rp2 = rp2; - hlist_del(&ri->uflist); - } - - spin_unlock_irqrestore(&kretprobe_lock, flags); - free_rp_inst(rp); - - unregister_uprobe(&rp->kp, task, atomic); -} - -void dbi_unregister_all_uprobes(struct task_struct *task, int atomic) -{ - struct hlist_head *head; - struct hlist_node *node, *tnode; - struct kprobe *p; - int i; - - for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { - head = &kprobe_table[i]; - hlist_for_each_entry_safe(p, node, tnode, head, hlist) { - if (p->tgid == task->tgid) { - printk("dbi_unregister_all_uprobes: delete uprobe at %p[%lx] for %s/%d\n", - p->addr, (unsigned long)p->opcode, task->comm, task->pid); - unregister_uprobe(p, task, atomic); - } - } - } -} - -void init_uprobes_insn_slots(int i) -{ - INIT_HLIST_HEAD(&uprobe_insn_slot_table[i]); -} - -void dbi_uprobe_return(void) -{ - dbi_arch_uprobe_return(); -} - - -EXPORT_SYMBOL_GPL(dbi_uprobe_return); -EXPORT_SYMBOL_GPL(dbi_register_ujprobe); -EXPORT_SYMBOL_GPL(dbi_unregister_ujprobe); -EXPORT_SYMBOL_GPL(dbi_register_uretprobe); -EXPORT_SYMBOL_GPL(dbi_unregister_uretprobe); -EXPORT_SYMBOL_GPL(dbi_unregister_all_uprobes); diff --git a/uprobe/Makefile.am b/uprobe/Makefile.am index f592a45..2db4c46 100644 --- a/uprobe/Makefile.am +++ b/uprobe/Makefile.am @@ -5,7 +5,7 @@ uprobes_module_dir = $(realpath $(top_srcdir)/src/modules/uprobe) module_name = swap_uprobe cross_compiler = $(subst gcc,,$(CC)) -inlude_opt = -I$(realpath $(top_srcdir)/src/modules/ksyms) +inlude_opt = -I$(realpath $(top_srcdir)/src/modules/ksyms) -I$(realpath $(top_srcdir)/src/modules/kprobe) extra_cflags = "$(inlude_opt) $(android_opt) $(board_opt)" all-local: diff --git a/uprobe/swap_uprobes.c b/uprobe/swap_uprobes.c index cd52983..0b9acd3 100644 --- a/uprobe/swap_uprobes.c +++ b/uprobe/swap_uprobes.c @@ -1,7 +1,462 @@ +/* + * Dynamic Binary Instrumentation Module based on KProbes + * modules/kprobe/dbi_uprobes.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) Samsung Electronics, 2006-2010 + * + * 2008-2009 Alexey Gerenkov User-Space + * Probes initial implementation; Support x86/ARM/MIPS for both user and kernel spaces. + * 2010 Ekaterina Gorelkina : redesign module for separating core and arch parts + * + */ + + +#include "swap_uprobes.h" +#include "dbi_kdebug.h" + +#include +#include #include + +#define DEBUG_PRINT_HASH_TABLE 0 + +#if DEBUG_PRINT_HASH_TABLE +void print_kprobe_hash_table(void) +{ + int i; + struct hlist_head *head; + struct hlist_node *node; + struct kprobe *p; + + // print uprobe table + for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { + head = &kprobe_table[i]; + hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) { + printk("####### find K tgid=%u, addr=%x\n", + p->tgid, p->addr); + } + } +} + +void print_kretprobe_hash_table(void) +{ + int i; + struct hlist_head *head; + struct hlist_node *node; + struct kprobe *p; + + // print uprobe table + for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { + head = &kretprobe_inst_table[i]; + hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) { + printk("####### find KR tgid=%u, addr=%x\n", + p->tgid, p->addr); + } + } +} + +void print_uprobe_hash_table(void) +{ + int i; + struct hlist_head *head; + struct hlist_node *node; + struct kprobe *p; + + // print uprobe table + for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { + head = &uprobe_insn_slot_table[i]; + hlist_for_each_entry_rcu (p, node, head, is_hlist_arm) { + printk("####### find U tgid=%u, addr=%x\n", + p->tgid, p->addr); + } + } +} +#endif + + +static void add_uprobe_table(struct kprobe *p) +{ +#ifdef CONFIG_ARM + INIT_HLIST_NODE(&p->is_hlist_arm); + hlist_add_head_rcu(&p->is_hlist_arm, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn_arm, KPROBE_HASH_BITS)]); + INIT_HLIST_NODE(&p->is_hlist_thumb); + hlist_add_head_rcu(&p->is_hlist_thumb, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn_thumb, KPROBE_HASH_BITS)]); +#else /* CONFIG_ARM */ + INIT_HLIST_NODE(&p->is_hlist); + hlist_add_head_rcu(&p->is_hlist, &uprobe_insn_slot_table[hash_ptr (p->ainsn.insn, KPROBE_HASH_BITS)]); +#endif /* CONFIG_ARM */ +} + + +static int __register_uprobe(struct kprobe *p, struct task_struct *task, int atomic) +{ + int ret = 0; + struct kprobe *old_p; + + if (!p->addr) { + return -EINVAL; + } + + DBPRINTF("p->addr = 0x%p p = 0x%p\n", p->addr, p); + +// thumb address = address-1; +#if defined(CONFIG_ARM) + // TODO: must be corrected in 'bundle' + if ((unsigned long) p->addr & 0x01) { + p->addr = (kprobe_opcode_t *)((unsigned long)p->addr & 0xfffffffe); + } +#endif + + p->mod_refcounted = 0; + p->nmissed = 0; + INIT_LIST_HEAD(&p->list); +#ifdef KPROBES_PROFILE + p->start_tm.tv_sec = p->start_tm.tv_usec = 0; + p->hnd_tm_sum.tv_sec = p->hnd_tm_sum.tv_usec = 0; + p->count = 0; +#endif + + // get the first item + old_p = get_kprobe(p->addr, p->tgid); + if (old_p) { +#ifdef CONFIG_ARM + p->safe_arm = old_p->safe_arm; + p->safe_thumb = old_p->safe_thumb; +#endif + ret = register_aggr_kprobe(old_p, p); + if (!ret) { + atomic_inc(&kprobe_count); + add_uprobe_table(p); + } + DBPRINTF("goto out\n", ret); + goto out; + } + + ret = arch_prepare_uprobe(p, task, atomic); + if (ret) { + DBPRINTF("goto out\n", ret); + goto out; + } + + DBPRINTF ("before out ret = 0x%x\n", ret); + + // TODO: add uprobe (must be in function) + INIT_HLIST_NODE(&p->hlist); + hlist_add_head_rcu(&p->hlist, &kprobe_table[hash_ptr (p->addr, KPROBE_HASH_BITS)]); + add_uprobe_table(p); + arch_arm_uprobe(p, task); + +out: + DBPRINTF("out ret = 0x%x\n", ret); + return ret; +} + +void unregister_uprobe(struct kprobe *p, struct task_struct *task, int atomic) +{ + dbi_unregister_kprobe (p, task); +} + + +int dbi_register_ujprobe(struct task_struct *task, struct jprobe *jp, int atomic) +{ + int ret = 0; + + /* Todo: Verify probepoint is a function entry point */ + jp->kp.pre_handler = setjmp_pre_handler; + jp->kp.break_handler = longjmp_break_handler; + + ret = __register_uprobe(&jp->kp, task, atomic); + + return ret; +} + +void dbi_unregister_ujprobe(struct task_struct *task, struct jprobe *jp, int atomic) +{ + unregister_uprobe(&jp->kp, task, atomic); + /* + * Here is an attempt to unregister even those probes that have not been + * installed (hence not added to the hlist). + * So if we try to delete them from the hlist we will get NULL pointer + * dereference error. That is why we check whether this node + * really belongs to the hlist. + */ +#ifdef CONFIG_ARM + if (!(hlist_unhashed(&jp->kp.is_hlist_arm))) { + hlist_del_rcu(&jp->kp.is_hlist_arm); + } + if (!(hlist_unhashed(&jp->kp.is_hlist_thumb))) { + hlist_del_rcu(&jp->kp.is_hlist_thumb); + } +#else /* CONFIG_ARM */ + if (!(hlist_unhashed(&jp->kp.is_hlist))) { + hlist_del_rcu(&jp->kp.is_hlist); + } +#endif /* CONFIG_ARM */ +} + +int dbi_register_uretprobe(struct task_struct *task, struct kretprobe *rp, int atomic) +{ + int i, ret = 0; + struct kretprobe_instance *inst; + + DBPRINTF ("START\n"); + + rp->kp.pre_handler = pre_handler_kretprobe; + rp->kp.post_handler = NULL; + rp->kp.fault_handler = NULL; + rp->kp.break_handler = NULL; + + rp->disarm = 0; + + /* Pre-allocate memory for max kretprobe instances */ + if (rp->maxactive <= 0) { +#if 1//def CONFIG_PREEMPT + rp->maxactive = max(10, 2 * NR_CPUS); +#else + rp->maxactive = NR_CPUS; +#endif + } + + INIT_HLIST_HEAD(&rp->used_instances); + INIT_HLIST_HEAD(&rp->free_instances); + + for (i = 0; i < rp->maxactive; i++) { + inst = kmalloc(sizeof(*inst), GFP_KERNEL); + if (inst == NULL) { + free_rp_inst (rp); + ret = -ENOMEM; + goto out; + } + + INIT_HLIST_NODE(&inst->uflist); + hlist_add_head(&inst->uflist, &rp->free_instances); + } + + rp->nmissed = 0; + + /* Establish function exit probe point */ + ret = arch_prepare_uretprobe(rp, task); + if (ret) { + goto out; + } + + /* Establish function entry probe point */ + ret = __register_uprobe(&rp->kp, task, atomic); + if (ret) { + free_rp_inst(rp); + goto out; + } + + arch_arm_uretprobe(rp, task); +out: + return ret; +} + +int dbi_disarm_urp_inst(struct kretprobe_instance *ri, struct task_struct *rm_task) +{ + struct task_struct *task = rm_task ? rm_task : ri->task; + kprobe_opcode_t *tramp = (kprobe_opcode_t *)(ri->rp->kp.ainsn.insn + + UPROBES_TRAMP_RET_BREAK_IDX); + kprobe_opcode_t *stack = ri->sp - RETPROBE_STACK_DEPTH + 1; + kprobe_opcode_t *found = NULL; + kprobe_opcode_t *buf[RETPROBE_STACK_DEPTH]; + int i, retval; + + retval = read_proc_vm_atomic(task, (unsigned long)stack, buf, sizeof(buf)); + if (retval != sizeof(buf)) { + printk("---> %s (%d/%d): failed to read stack from %08lx", + task->comm, task->tgid, task->pid, (unsigned long)stack); + retval = -EFAULT; + goto out; + } + + /* search the stack from the bottom */ + for (i = RETPROBE_STACK_DEPTH - 1; i >= 0; i--) { + if (buf[i] == tramp) { + found = stack + i; + break; + } + } + + if (found) { + printk("---> %s (%d/%d): trampoline found at %08lx (%08lx /%+d) - %p\n", + task->comm, task->tgid, task->pid, + (unsigned long)found, (unsigned long)ri->sp, + found - ri->sp, ri->rp->kp.addr); + retval = write_proc_vm_atomic(task, (unsigned long)found, &ri->ret_addr, + sizeof(ri->ret_addr)); + if (retval != sizeof(ri->ret_addr)) { + printk("---> %s (%d/%d): failed to write value to %08lx", + task->comm, task->tgid, task->pid, (unsigned long)found); + retval = -EFAULT; + } else { + retval = 0; + } + } else { + struct pt_regs *uregs = task_pt_regs(ri->task); + unsigned long ra = dbi_get_ret_addr(uregs); + if (ra == (unsigned long)tramp) { + printk("---> %s (%d/%d): trampoline found at lr = %08lx - %p\n", + task->comm, task->tgid, task->pid, ra, ri->rp->kp.addr); + dbi_set_ret_addr(uregs, (unsigned long)tramp); + retval = 0; + } else { + printk("---> %s (%d/%d): trampoline NOT found at sp = %08lx, lr = %08lx - %p\n", + task->comm, task->tgid, task->pid, + (unsigned long)ri->sp, ra, ri->rp->kp.addr); + retval = -ENOENT; + } + } + +out: + return retval; +} + +/* Called with kretprobe_lock held */ +int dbi_disarm_urp_inst_for_task(struct task_struct *parent, struct task_struct *task) +{ + struct kretprobe_instance *ri; + struct hlist_node *node, *tmp; + struct hlist_head *head = kretprobe_inst_table_head(parent->mm); + + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + if (parent == ri->task && ri->rp->kp.tgid) { + dbi_disarm_urp_inst(ri, task); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(dbi_disarm_urp_inst_for_task); + +void dbi_unregister_uretprobe(struct task_struct *task, struct kretprobe *rp, int atomic, int not_rp2) +{ + unsigned long flags; + struct kretprobe_instance *ri; + struct kretprobe *rp2 = NULL; + + spin_lock_irqsave (&kretprobe_lock, flags); + + while ((ri = get_used_rp_inst(rp)) != NULL) { + if (dbi_disarm_urp_inst(ri, NULL) != 0) + /*panic*/printk("%s (%d/%d): cannot disarm urp instance (%08lx)\n", + ri->task->comm, ri->task->tgid, ri->task->pid, + (unsigned long)rp->kp.addr); + recycle_rp_inst(ri); + } + + if (hlist_empty(&rp->used_instances) || not_rp2) { + struct kprobe *p = &rp->kp; + // if there are no used retprobe instances (i.e. function is not entered) - disarm retprobe + arch_disarm_uretprobe(rp, task);//vmas[1], pages[1], kaddrs[1]); +#ifdef CONFIG_ARM + if (!(hlist_unhashed(&p->is_hlist_arm))) { + hlist_del_rcu(&p->is_hlist_arm); + } + + if (!(hlist_unhashed(&p->is_hlist_thumb))) { + hlist_del_rcu(&p->is_hlist_thumb); + } +#else /* CONFIG_ARM */ + if (!(hlist_unhashed(&p->is_hlist))) { + hlist_del_rcu(&p->is_hlist); + } +#endif /* CONFIG_ARM */ + } else { + struct kprobe *new_p = NULL; + struct kprobe *p = &rp->kp; + rp2 = clone_kretprobe(rp); + if (!rp2) { + DBPRINTF ("dbi_unregister_uretprobe addr %p: failed to clone retprobe!", rp->kp.addr); + } else { + DBPRINTF ("initiating deferred retprobe deletion addr %p", rp->kp.addr); + printk ("initiating deferred retprobe deletion addr %p\n", rp->kp.addr); + arch_disarm_uprobe(&rp->kp, task); + rp2->disarm = 1; + } + /* + * As we cloned retprobe we have to update the entry in the insn slot + * hash list. + */ +#ifdef CONFIG_ARM + if (!(hlist_unhashed(&p->is_hlist_arm))) { + hlist_del_rcu(&p->is_hlist_arm); + } + if (!(hlist_unhashed(&p->is_hlist_thumb))) { + hlist_del_rcu(&p->is_hlist_thumb); + } +#else /* CONFIG_ARM */ + if (!(hlist_unhashed(&p->is_hlist))) { + hlist_del_rcu(&p->is_hlist); + } +#endif /* CONFIG_ARM */ + new_p = &rp2->kp; + add_uprobe_table(new_p); + } + + while ((ri = get_used_rp_inst(rp)) != NULL) { + ri->rp = NULL; + ri->rp2 = rp2; + hlist_del(&ri->uflist); + } + + spin_unlock_irqrestore(&kretprobe_lock, flags); + free_rp_inst(rp); + + unregister_uprobe(&rp->kp, task, atomic); +} + +void dbi_unregister_all_uprobes(struct task_struct *task, int atomic) +{ + struct hlist_head *head; + struct hlist_node *node, *tnode; + struct kprobe *p; + int i; + + for (i = 0; i < KPROBE_TABLE_SIZE; ++i) { + head = &kprobe_table[i]; + hlist_for_each_entry_safe(p, node, tnode, head, hlist) { + if (p->tgid == task->tgid) { + printk("dbi_unregister_all_uprobes: delete uprobe at %p[%lx] for %s/%d\n", + p->addr, (unsigned long)p->opcode, task->comm, task->pid); + unregister_uprobe(p, task, atomic); + } + } + } +} + +void init_uprobes_insn_slots(int i) +{ + INIT_HLIST_HEAD(&uprobe_insn_slot_table[i]); +} + +void dbi_uprobe_return(void) +{ + dbi_arch_uprobe_return(); +} + static int __init init_uprobes(void) { + int i; + for (i = 0; i < KPROBE_TABLE_SIZE; i++) { + init_uprobes_insn_slots(i); + } + return 0; } @@ -9,6 +464,13 @@ static void __exit exit_uprobes(void) { } +EXPORT_SYMBOL_GPL(dbi_uprobe_return); +EXPORT_SYMBOL_GPL(dbi_register_ujprobe); +EXPORT_SYMBOL_GPL(dbi_unregister_ujprobe); +EXPORT_SYMBOL_GPL(dbi_register_uretprobe); +EXPORT_SYMBOL_GPL(dbi_unregister_uretprobe); +EXPORT_SYMBOL_GPL(dbi_unregister_all_uprobes); + module_init(init_uprobes); module_exit(exit_uprobes); diff --git a/kprobe/dbi_uprobes.h b/uprobe/swap_uprobes.h similarity index 100% rename from kprobe/dbi_uprobes.h rename to uprobe/swap_uprobes.h -- 2.7.4