From b5fc7ff35a626e2bdc7a64fea4bca47f9b8cd35b Mon Sep 17 00:00:00 2001 From: Vyacheslav Cherkashin Date: Tue, 12 Mar 2013 16:29:11 +0400 Subject: [PATCH] [REFACTOR] create uprobe_handler() move code from kprobe_handler() to uprobe_handler() --- kprobe/arch/asm-arm/dbi_kprobes.c | 113 +++------------------------ kprobe/dbi_insn_slots.c | 2 +- kprobe/dbi_kprobes.c | 2 + uprobe/arch/asm-arm/swap_uprobes.c | 154 +++++++++++++++++++++++++++++++++++-- 4 files changed, 161 insertions(+), 110 deletions(-) diff --git a/kprobe/arch/asm-arm/dbi_kprobes.c b/kprobe/arch/asm-arm/dbi_kprobes.c index 9fd3494..db737a2 100644 --- a/kprobe/arch/asm-arm/dbi_kprobes.c +++ b/kprobe/arch/asm-arm/dbi_kprobes.c @@ -92,16 +92,6 @@ static struct kprobe trampoline_p = .pre_handler = trampoline_probe_handler }; -// is instruction Thumb2 and NOT a branch, etc... -int isThumb2(kprobe_opcode_t insn) -{ - if(( (insn & 0xf800) == 0xe800 || - (insn & 0xf800) == 0xf000 || - (insn & 0xf800) == 0xf800)) return 1; - return 0; -} -EXPORT_SYMBOL_GPL(isThumb2); - int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs) { int i; @@ -390,41 +380,7 @@ void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ct __get_cpu_var(current_kprobe) = p; DBPRINTF ("set_current_kprobe: p=%p addr=%p\n", p, p->addr); } - -static int check_validity_insn(struct kprobe *p, struct pt_regs *regs, struct task_struct *task) -{ - struct kprobe *kp; - - if (unlikely(thumb_mode(regs))) { - if (p->safe_thumb != -1) { - p->ainsn.insn = p->ainsn.insn_thumb; - list_for_each_entry_rcu(kp, &p->list, list) { - kp->ainsn.insn = p->ainsn.insn_thumb; - } - } else { - printk("Error in %s at %d: we are in thumb mode (!) and check instruction was fail \ - (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr); - // Test case when we do our actions on already running application - arch_disarm_uprobe(p, task); - return -1; - } - } else { - if (p->safe_arm != -1) { - p->ainsn.insn = p->ainsn.insn_arm; - list_for_each_entry_rcu(kp, &p->list, list) { - kp->ainsn.insn = p->ainsn.insn_arm; - } - } else { - printk("Error in %s at %d: we are in arm mode (!) and check instruction was fail \ - (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr); - // Test case when we do our actions on already running application - arch_disarm_uprobe(p, task); - return -1; - } - } - - return 0; -} +EXPORT_SYMBOL_GPL(set_current_kprobe); #ifdef TRAP_OVERHEAD_DEBUG static unsigned long trap_handler_counter_debug = 0; @@ -435,8 +391,6 @@ static int kprobe_handler(struct pt_regs *regs) { int err_out = 0; char *msg_out = NULL; - unsigned long user_m = user_mode(regs); - pid_t tgid = (user_m) ? current->tgid : 0; kprobe_opcode_t *addr = (kprobe_opcode_t *) (regs->ARM_pc); struct kprobe *p = NULL, *p_run = NULL; @@ -472,12 +426,7 @@ static int kprobe_handler(struct pt_regs *regs) #endif preempt_disable(); -// printk("### kprobe_handler: task[tgid=%u (%s)] addr=%p\n", tgid, current->comm, addr); - p = get_kprobe(addr, tgid); - - if (user_m && p && (check_validity_insn(p, regs, current) != 0)) { - goto no_kprobe_live; - } + p = get_kprobe(addr, 0); /* We're in an interrupt, but this is clear and BUG()-safe. */ kcb = get_kprobe_ctlblk (); @@ -490,7 +439,7 @@ static int kprobe_handler(struct pt_regs *regs) DBPRINTF("lock???"); if (p) { - if (!tgid && (addr == (kprobe_opcode_t *)kretprobe_trampoline)) { + if (addr == (kprobe_opcode_t *)kretprobe_trampoline) { save_previous_kprobe(kcb, p_run); kcb->kprobe_status = KPROBE_REENTER; reenter = 1; @@ -508,19 +457,6 @@ static int kprobe_handler(struct pt_regs *regs) goto out; } } else { - if(tgid) { //we can reenter probe upon uretprobe exception - DBPRINTF ("check for UNDEF_INSTRUCTION %p\n", addr); - // UNDEF_INSTRUCTION from user space - - p = get_kprobe_by_insn_slot(addr, tgid, regs); - if (p) { - save_previous_kprobe(kcb, p_run); - kcb->kprobe_status = KPROBE_REENTER; - reenter = 1; - retprobe = 1; - DBPRINTF ("uretprobe %p\n", addr); - } - } if(!p) { p = p_run; DBPRINTF ("kprobe_running !!! p = 0x%p p->break_handler = 0x%p", p, p->break_handler); @@ -529,10 +465,7 @@ static int kprobe_handler(struct pt_regs *regs) goto ss_probe; } */ DBPRINTF ("unknown uprobe at %p cur at %p/%p\n", addr, p->addr, p->ainsn.insn); - if (tgid) - ssaddr = p->ainsn.insn + UPROBES_TRAMP_SS_BREAK_IDX; - else - ssaddr = p->ainsn.insn + KPROBES_TRAMP_SS_BREAK_IDX; + ssaddr = p->ainsn.insn + KPROBES_TRAMP_SS_BREAK_IDX; if (addr == ssaddr) { regs->ARM_pc = (unsigned long) (p->addr + 1); DBPRINTF ("finish step at %p cur at %p/%p, redirect to %lx\n", addr, p->addr, p->ainsn.insn, regs->ARM_pc); @@ -552,34 +485,11 @@ static int kprobe_handler(struct pt_regs *regs) } if (!p) { - if (tgid) { - DBPRINTF ("search UNDEF_INSTRUCTION %p\n", addr); - // UNDEF_INSTRUCTION from user space - - p = get_kprobe_by_insn_slot(addr, tgid, regs); - if (!p) { - /* Not one of ours: let kernel handle it */ - DBPRINTF ("no_kprobe"); - goto no_kprobe; - } - retprobe = 1; - DBPRINTF ("uretprobe %p\n", addr); - } else { - /* Not one of ours: let kernel handle it */ - DBPRINTF ("no_kprobe"); - goto no_kprobe; - } - } - // restore opcode for thumb app - if (user_mode( regs ) && thumb_mode( regs )) { - if (!isThumb2(p->opcode)) { - unsigned long tmp = p->opcode >> 16; - write_proc_vm_atomic(current, (unsigned long)((unsigned short*)p->addr + 1), &tmp, 2); - - // "2*sizeof(kprobe_opcode_t)" - strange. Should be "sizeof(kprobe_opcode_t)", need to test - flush_icache_range((unsigned int) p->addr, ((unsigned int)p->addr) + (2 * sizeof(kprobe_opcode_t))); - } + /* Not one of ours: let kernel handle it */ + DBPRINTF ("no_kprobe"); + goto no_kprobe; } + set_current_kprobe(p, NULL, NULL); if(!reenter) kcb->kprobe_status = KPROBE_HIT_ACTIVE; @@ -603,11 +513,6 @@ no_kprobe: err_out = 1; // return with death goto out; -no_kprobe_live: - msg_out = "no_kprobe live\n"; - err_out = 0; // ok - life is life - goto out; - out: preempt_enable_no_resched(); #ifdef OVERHEAD_DEBUG @@ -636,7 +541,6 @@ int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr) local_irq_restore(flags); return ret; } -EXPORT_SYMBOL_GPL(kprobe_trap_handler); int setjmp_pre_handler (struct kprobe *p, struct pt_regs *regs) { @@ -915,6 +819,7 @@ int trampoline_probe_handler (struct kprobe *p, struct pt_regs *regs) return 1; } +EXPORT_SYMBOL_GPL(trampoline_probe_handler); void __arch_prepare_kretprobe (struct kretprobe *rp, struct pt_regs *regs) { diff --git a/kprobe/dbi_insn_slots.c b/kprobe/dbi_insn_slots.c index bb12871..4482223 100644 --- a/kprobe/dbi_insn_slots.c +++ b/kprobe/dbi_insn_slots.c @@ -387,4 +387,4 @@ struct kprobe *get_kprobe_by_insn_slot (void *addr, int tgid, struct task_struct return retVal; } #endif /* CONFIG_ARM */ - +EXPORT_SYMBOL_GPL(get_kprobe_by_insn_slot); diff --git a/kprobe/dbi_kprobes.c b/kprobe/dbi_kprobes.c index 1a42e1b..a69d9b4 100644 --- a/kprobe/dbi_kprobes.c +++ b/kprobe/dbi_kprobes.c @@ -130,11 +130,13 @@ void reset_current_kprobe (void) { __get_cpu_var (current_kprobe) = NULL; } +EXPORT_SYMBOL_GPL(reset_current_kprobe); struct kprobe_ctlblk *get_kprobe_ctlblk (void) { return (&__get_cpu_var (kprobe_ctlblk)); } +EXPORT_SYMBOL_GPL(get_kprobe_ctlblk); /* * This routine is called either: diff --git a/uprobe/arch/asm-arm/swap_uprobes.c b/uprobe/arch/asm-arm/swap_uprobes.c index 6a9ce9d..8c4f0fa 100644 --- a/uprobe/arch/asm-arm/swap_uprobes.c +++ b/uprobe/arch/asm-arm/swap_uprobes.c @@ -9,7 +9,6 @@ kprobe_opcode_t *get_insn_slot(struct task_struct *task, struct hlist_head *page int arch_check_insn_arm(struct arch_specific_insn *ainsn); int prep_pc_dep_insn_execbuf(kprobe_opcode_t *insns, kprobe_opcode_t insn, int uregs); void free_insn_slot(struct hlist_head *page_list, struct task_struct *task, kprobe_opcode_t *slot); -int isThumb2(kprobe_opcode_t insn); void pc_dep_insn_execbuf(void); void gen_insn_execbuf(void); void gen_insn_execbuf_thumb(void); @@ -26,6 +25,14 @@ static kprobe_opcode_t get_addr_b(kprobe_opcode_t insn, kprobe_opcode_t *addr) return (kprobe_opcode_t)((long)addr + 8 + branch_displacement(insn)); } +/* is instruction Thumb2 and NOT a branch, etc... */ +static int is_thumb2(kprobe_opcode_t insn) +{ + return ((insn & 0xf800) == 0xe800 || + (insn & 0xf800) == 0xf000 || + (insn & 0xf800) == 0xf800); +} + static int arch_copy_trampoline_arm_uprobe(struct kprobe *p, struct task_struct *task, int atomic) { kprobe_opcode_t insns[UPROBES_TRAMP_LEN]; @@ -494,7 +501,7 @@ static int arch_copy_trampoline_thumb_uprobe(struct kprobe *p, struct task_struc *((unsigned short*)insns + 13) = 0xdeff; *((unsigned short*)insns + 14) = addr & 0x0000ffff; *((unsigned short*)insns + 15) = addr >> 16; - if (!isThumb2(insn[0])) { + if (!is_thumb2(insn[0])) { addr = ((unsigned int)p->addr) + 2; *((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1; *((unsigned short*)insns + 17) = addr >> 16; @@ -506,7 +513,7 @@ static int arch_copy_trampoline_thumb_uprobe(struct kprobe *p, struct task_struc } else { memcpy(insns, gen_insn_execbuf_thumb, 18 * 2); *((unsigned short*)insns + 13) = 0xdeff; - if (!isThumb2(insn[0])) { + if (!is_thumb2(insn[0])) { addr = ((unsigned int)p->addr) + 2; *((unsigned short*)insns + 2) = insn[0]; *((unsigned short*)insns + 16) = (addr & 0x0000ffff) | 0x1; @@ -585,13 +592,150 @@ int arch_prepare_uprobe(struct kprobe *p, struct task_struct *task, int atomic) return ret; } +static int check_validity_insn(struct kprobe *p, struct pt_regs *regs, struct task_struct *task) +{ + struct kprobe *kp; + + if (unlikely(thumb_mode(regs))) { + if (p->safe_thumb != -1) { + p->ainsn.insn = p->ainsn.insn_thumb; + list_for_each_entry_rcu(kp, &p->list, list) { + kp->ainsn.insn = p->ainsn.insn_thumb; + } + } else { + printk("Error in %s at %d: we are in thumb mode (!) and check instruction was fail \ + (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr); + // Test case when we do our actions on already running application + arch_disarm_uprobe(p, task); + return -1; + } + } else { + if (p->safe_arm != -1) { + p->ainsn.insn = p->ainsn.insn_arm; + list_for_each_entry_rcu(kp, &p->list, list) { + kp->ainsn.insn = p->ainsn.insn_arm; + } + } else { + printk("Error in %s at %d: we are in arm mode (!) and check instruction was fail \ + (%0lX instruction at %p address)!\n", __FILE__, __LINE__, p->opcode, p->addr); + // Test case when we do our actions on already running application + arch_disarm_uprobe(p, task); + return -1; + } + } + + return 0; +} + +static int uprobe_handler(struct pt_regs *regs) +{ + int err_out = 0; + char *msg_out = NULL; + struct task_struct *task = current; + pid_t tgid = task->tgid; + kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->ARM_pc); + struct kprobe *p = NULL; + int ret = 0, retprobe = 0; + struct kprobe_ctlblk *kcb; + +#ifdef SUPRESS_BUG_MESSAGES + int swap_oops_in_progress; + // oops_in_progress used to avoid BUG() messages that slow down kprobe_handler() execution + swap_oops_in_progress = oops_in_progress; + oops_in_progress = 1; +#endif + + p = get_kprobe(addr, tgid); + + if (p && (check_validity_insn(p, regs, task) != 0)) { + goto no_uprobe_live; + } + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + kcb = get_kprobe_ctlblk(); + + if (p == NULL) { + p = get_kprobe_by_insn_slot(addr, tgid, regs); + if (p == NULL) { + /* Not one of ours: let kernel handle it */ + goto no_uprobe; + } + + retprobe = 1; + } + + /* restore opcode for thumb app */ + if (thumb_mode(regs)) { + if (!is_thumb2(p->opcode)) { + unsigned long tmp = p->opcode >> 16; + write_proc_vm_atomic(task, (unsigned long)((unsigned short*)p->addr + 1), &tmp, 2); + + // "2*sizeof(kprobe_opcode_t)" - strange. Should be "sizeof(kprobe_opcode_t)", need to test + flush_icache_range((unsigned int)p->addr, ((unsigned int)p->addr) + (2 * sizeof(kprobe_opcode_t))); + } + } + + set_current_kprobe(p, NULL, NULL); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + if (retprobe) { + ret = trampoline_probe_handler(p, regs); + } else if (p->pre_handler) { + ret = p->pre_handler(p, regs); + if(p->pre_handler != trampoline_probe_handler) { + reset_current_kprobe(); + } + } + + if (ret) { + /* handler has already set things up, so skip ss setup */ + err_out = 0; + goto out; + } + +no_uprobe: + msg_out = "no_uprobe\n"; + err_out = 1; // return with death + goto out; + +no_uprobe_live: + msg_out = "no_uprobe live\n"; + err_out = 0; // ok - life is life + goto out; + +out: +#ifdef SUPRESS_BUG_MESSAGES + oops_in_progress = swap_oops_in_progress; +#endif + + if(msg_out) { + printk(msg_out); + } + + return err_out; +} + +int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr) +{ + int ret; + unsigned long flags; + local_irq_save(flags); + + preempt_disable(); + ret = uprobe_handler(regs); + preempt_enable_no_resched(); + + local_irq_restore(flags); + return ret; +} + /* userspace probes hook (arm) */ static struct undef_hook undef_hook_for_us_arm = { .instr_mask = 0xffffffff, .instr_val = BREAKPOINT_INSTRUCTION, .cpsr_mask = MODE_MASK, .cpsr_val = USR_MODE, - .fn = kprobe_trap_handler + .fn = uprobe_trap_handler }; /* userspace probes hook (thumb) */ @@ -600,7 +744,7 @@ static struct undef_hook undef_hook_for_us_thumb = { .instr_val = BREAKPOINT_INSTRUCTION & 0x0000ffff, .cpsr_mask = MODE_MASK, .cpsr_val = USR_MODE, - .fn = kprobe_trap_handler + .fn = uprobe_trap_handler }; int swap_arch_init_uprobes(void) -- 2.7.4