summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver/helper.c6
-rw-r--r--kprobe/arch/asm-x86/dbi_kprobes.c11
-rw-r--r--kprobe/arch/asm-x86/dbi_kprobes.h5
-rw-r--r--uprobe/arch/asm-x86/swap_uprobes.c289
-rw-r--r--uprobe/arch/asm-x86/swap_uprobes.h37
-rw-r--r--uprobe/swap_uprobes.c2
6 files changed, 310 insertions, 40 deletions
diff --git a/driver/helper.c b/driver/helper.c
index 7c78a854..22245cb6 100644
--- a/driver/helper.c
+++ b/driver/helper.c
@@ -142,7 +142,7 @@ static int mr_pre_handler(struct kprobe *p, struct pt_regs *regs)
struct task_struct *task;
#if defined(CONFIG_X86)
- return 0;
+ task = (struct task_struct *)regs->EREG(ax);
#elif defined(CONFIG_ARM)
task = (struct task_struct *)regs->ARM_r0;
#else
@@ -233,7 +233,9 @@ static int unmap_pre_handler(struct kprobe *p, struct pt_regs *regs)
size_t len;
#if defined(CONFIG_X86)
- return 0;
+ mm = (struct mm_struct *)regs->EREG(ax);
+ start = regs->EREG(dx);
+ len = (size_t)regs->EREG(cx);
#elif defined(CONFIG_ARM)
mm = (struct mm_struct *)regs->ARM_r0;
start = regs->ARM_r1;
diff --git a/kprobe/arch/asm-x86/dbi_kprobes.c b/kprobe/arch/asm-x86/dbi_kprobes.c
index 17420734..faf41ef9 100644
--- a/kprobe/arch/asm-x86/dbi_kprobes.c
+++ b/kprobe/arch/asm-x86/dbi_kprobes.c
@@ -216,7 +216,7 @@ static void set_user_jmp_op (void *from, void *to)
/*
* returns non-zero if opcodes can be boosted.
*/
-static __always_inline int can_boost (kprobe_opcode_t * opcodes)
+int can_boost(kprobe_opcode_t *opcodes)
{
#define W(row,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,ba,bb,bc,bd,be,bf) \
(((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \
@@ -296,6 +296,7 @@ retry:
return (opcode != 0x2e && opcode != 0x9a);
}
}
+EXPORT_SYMBOL_GPL(can_boost);
/*
* returns non-zero if opcode modifies the interrupt flag.
@@ -313,12 +314,6 @@ static int is_IF_modifier (kprobe_opcode_t opcode)
return 0;
}
-int arch_check_insn (struct arch_specific_insn *ainsn)
-{
- DBPRINTF("Warrning: arch_check_insn is not implemented for x86\n");
- return 0;
-}
-
int arch_prepare_kprobe(struct kprobe *p, struct slot_manager *sm)
{
kprobe_opcode_t insns[KPROBES_TRAMP_LEN];
@@ -382,7 +377,7 @@ void prepare_singlestep (struct kprobe *p, struct pt_regs *regs)
regs->EREG (ip) = (unsigned long) p->ainsn.insn;
}
}
-
+EXPORT_SYMBOL_GPL(prepare_singlestep);
void save_previous_kprobe (struct kprobe_ctlblk *kcb, struct kprobe *cur_p)
{
diff --git a/kprobe/arch/asm-x86/dbi_kprobes.h b/kprobe/arch/asm-x86/dbi_kprobes.h
index 52ed3fa3..52c7f84a 100644
--- a/kprobe/arch/asm-x86/dbi_kprobes.h
+++ b/kprobe/arch/asm-x86/dbi_kprobes.h
@@ -227,6 +227,11 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
void kretprobe_trampoline(void);
void restore_previous_kprobe(struct kprobe_ctlblk *kcb);
+int can_boost(kprobe_opcode_t *opcodes);
+static inline int arch_check_insn(struct arch_specific_insn *ainsn)
+{
+ return 0;
+}
int arch_init_kprobes(void);
void arch_exit_kprobes(void);
diff --git a/uprobe/arch/asm-x86/swap_uprobes.c b/uprobe/arch/asm-x86/swap_uprobes.c
index e69de29b..f5779ac5 100644
--- a/uprobe/arch/asm-x86/swap_uprobes.c
+++ b/uprobe/arch/asm-x86/swap_uprobes.c
@@ -0,0 +1,289 @@
+#include <linux/kdebug.h>
+#include <asm/dbi_kprobes.h>
+#include <swap_uprobes.h>
+#include <asm/swap_uprobes.h>
+#include <dbi_insn_slots.h>
+
+struct uprobe_ctlblk {
+ unsigned long flags;
+ struct kprobe *p;
+};
+
+static DEFINE_PER_CPU(struct uprobe_ctlblk, ucb) = { 0, NULL };
+
+int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list)
+{
+ int ret = 0;
+ struct kprobe *p = &up->kp;
+ struct task_struct *task = up->task;
+ kprobe_opcode_t insns[UPROBES_TRAMP_LEN];
+
+ if (!ret) {
+ kprobe_opcode_t insn[MAX_INSN_SIZE];
+ struct arch_specific_insn ainsn;
+
+ if (!read_proc_vm_atomic(task, (unsigned long)p->addr, &insn, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)))
+ panic("failed to read memory %p!\n", p->addr);
+
+ ainsn.insn = insn;
+ ret = arch_check_insn(&ainsn);
+ if (!ret) {
+ p->opcode = insn[0];
+ p->ainsn.insn = alloc_insn_slot(up->sm);
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+
+ if (can_boost(insn))
+ p->ainsn.boostable = 0;
+ else
+ p->ainsn.boostable = -1;
+
+ memcpy(&insns[UPROBES_TRAMP_INSN_IDX], insn, MAX_INSN_SIZE*sizeof(kprobe_opcode_t));
+ insns[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
+
+ if (!write_proc_vm_atomic(task, (unsigned long)p->ainsn.insn, insns, sizeof(insns))) {
+ free_insn_slot(up->sm, p->ainsn.insn);
+ panic("failed to write memory %p!\n", p->ainsn.insn);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct uprobe *up = container_of(p, struct uprobe, kp);
+ struct ujprobe *jp = container_of(up, struct ujprobe, up);
+ kprobe_pre_entry_handler_t pre_entry = (kprobe_pre_entry_handler_t)jp->pre_entry;
+ entry_point_t entry = (entry_point_t)jp->entry;
+ unsigned long addr, args[6];
+
+ /* FIXME some user space apps crash if we clean interrupt bit */
+ //regs->EREG(flags) &= ~IF_MASK;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ trace_hardirqs_off();
+#endif
+
+ /* read first 6 args from stack */
+ if (!read_proc_vm_atomic(current, regs->EREG(sp) + 4, args, sizeof(args)))
+ panic("failed to read user space func arguments %lx!\n", regs->EREG(sp) + 4);
+
+ if (pre_entry)
+ p->ss_addr = pre_entry(jp->priv_arg, regs);
+
+ if (entry)
+ entry(args[0], args[1], args[2], args[3], args[4], args[5]);
+ else
+ arch_ujprobe_return();
+
+ return 0;
+}
+
+void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
+{
+ /* Replace the return addr with trampoline addr */
+ unsigned long ra = (unsigned long)(ri->rp->up.kp.ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+
+ if (!read_proc_vm_atomic(current, regs->EREG(sp), &(ri->ret_addr), sizeof(ri->ret_addr)))
+ panic("failed to read user space func ra %lx!\n", regs->EREG(sp));
+
+ if (!write_proc_vm_atomic(current, regs->EREG(sp), &ra, sizeof(ra)))
+ panic("failed to write user space func ra %lx!\n", regs->EREG(sp));
+}
+
+unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs)
+{
+ return (unsigned long)(p->ainsn.insn + UPROBES_TRAMP_RET_BREAK_IDX);
+}
+
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
+{
+ regs->EREG(ip) = orig_ret_addr;
+}
+
+static void set_user_jmp_op(void *from, void *to)
+{
+ struct __arch_jmp_op
+ {
+ char op;
+ long raddr;
+ } __attribute__ ((packed)) jop;
+
+ jop.raddr = (long)(to) - ((long)(from) + 5);
+ jop.op = RELATIVEJUMP_INSTRUCTION;
+
+ if (!write_proc_vm_atomic(current, (unsigned long)from, &jop, sizeof(jop)))
+ panic("failed to write jump opcode to user space %p!\n", from);
+}
+
+static void resume_execution(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
+{
+ unsigned long *tos, tos_dword = 0;
+ unsigned long copy_eip = (unsigned long)p->ainsn.insn;
+ unsigned long orig_eip = (unsigned long)p->addr;
+ kprobe_opcode_t insns[2];
+
+ regs->EREG(flags) &= ~TF_MASK;
+
+ tos = (unsigned long *)&tos_dword;
+ if (!read_proc_vm_atomic(current, regs->EREG(sp), &tos_dword, sizeof(tos_dword)))
+ panic("failed to read dword from top of the user space stack %lx!\n", regs->EREG(sp));
+
+ if (!read_proc_vm_atomic(current, (unsigned long)p->ainsn.insn, insns, 2 * sizeof(kprobe_opcode_t)))
+ panic("failed to read first 2 opcodes of instruction copy from user space %p!\n", p->ainsn.insn);
+
+ switch (insns[0]) {
+ case 0x9c: /* pushfl */
+ *tos &= ~(TF_MASK | IF_MASK);
+ *tos |= flags;
+ break;
+ case 0xc2: /* iret/ret/lret */
+ case 0xc3:
+ case 0xca:
+ case 0xcb:
+ case 0xcf:
+ case 0xea: /* jmp absolute -- eip is correct */
+ /* eip is already adjusted, no more changes required */
+ p->ainsn.boostable = 1;
+ goto no_change;
+ case 0xe8: /* call relative - Fix return addr */
+ *tos = orig_eip + (*tos - copy_eip);
+ break;
+ case 0x9a: /* call absolute -- same as call absolute, indirect */
+ *tos = orig_eip + (*tos - copy_eip);
+
+ if (!write_proc_vm_atomic(current, regs->EREG (sp), &tos_dword, sizeof(tos_dword)))
+ panic("failed to write dword to top of the user space stack %lx!\n", regs->EREG (sp));
+
+ goto no_change;
+ case 0xff:
+ if ((insns[1] & 0x30) == 0x10) {
+ /*
+ * call absolute, indirect
+ * Fix return addr; eip is correct.
+ * But this is not boostable
+ */
+ *tos = orig_eip + (*tos - copy_eip);
+
+ if (!write_proc_vm_atomic(current, regs->EREG(sp), &tos_dword, sizeof(tos_dword)))
+ panic("failed to write dword to top of the user space stack %lx!\n", regs->EREG(sp));
+
+ goto no_change;
+ } else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */
+ ((insns[1] & 0x31) == 0x21)) {
+ /* jmp far, absolute indirect */
+ /* eip is correct. And this is boostable */
+ p->ainsn.boostable = 1;
+ goto no_change;
+ }
+ default:
+ break;
+ }
+
+ if (!write_proc_vm_atomic(current, regs->EREG(sp), &tos_dword, sizeof(tos_dword)))
+ panic("failed to write dword to top of the user space stack %lx!\n", regs->EREG(sp));
+
+ if (p->ainsn.boostable == 0) {
+ if ((regs->EREG(ip) > copy_eip) && (regs->EREG(ip) - copy_eip) + 5 < MAX_INSN_SIZE) {
+ /*
+ * These instructions can be executed directly if it
+ * jumps back to correct address.
+ */
+ set_user_jmp_op((void *) regs->EREG(ip), (void *)orig_eip + (regs->EREG(ip) - copy_eip));
+ p->ainsn.boostable = 1;
+ } else {
+ p->ainsn.boostable = -1;
+ }
+ }
+
+ regs->EREG(ip) = orig_eip + (regs->EREG(ip) - copy_eip);
+
+no_change:
+ return;
+}
+
+static int uprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p;
+ kprobe_opcode_t *addr;
+ struct task_struct *task = current;
+ pid_t tgid = task->tgid;
+
+ addr = (kprobe_opcode_t *)(regs->EREG(ip) - sizeof(kprobe_opcode_t));
+ p = get_ukprobe(addr, tgid);
+
+ if (p == NULL) {
+ p = get_ukprobe_by_insn_slot(addr, tgid, regs);
+
+ if (p == NULL) {
+ printk("no_uprobe\n");
+ return 0;
+ }
+
+ trampoline_uprobe_handler(p, regs);
+ } else {
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ prepare_singlestep(p, regs);
+ }
+
+ __get_cpu_var(ucb).p = p;
+ __get_cpu_var(ucb).flags = (regs->EREG(flags) & (TF_MASK | IF_MASK));
+
+ return 1;
+}
+
+static int post_uprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p = __get_cpu_var(ucb).p;
+ unsigned long flags = __get_cpu_var(ucb).flags;
+
+ resume_execution(p, regs, flags);
+
+ return 1;
+}
+
+static int uprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data)
+{
+ struct die_args *args = (struct die_args *)data;
+ int ret = NOTIFY_DONE;
+
+ if (args->regs && !user_mode_vm(args->regs))
+ return ret;
+
+ switch (val) {
+#ifdef CONFIG_KPROBES
+ case DIE_INT3:
+#else
+ case DIE_TRAP:
+#endif
+ if (uprobe_handler(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_DEBUG:
+ if (post_uprobe_handler(args->regs))
+ ret = NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static struct notifier_block uprobe_exceptions_nb = {
+ .notifier_call = uprobe_exceptions_notify,
+ .priority = INT_MAX
+};
+
+int swap_arch_init_uprobes(void)
+{
+ return register_die_notifier(&uprobe_exceptions_nb);
+}
+
+void swap_arch_exit_uprobes(void)
+{
+ unregister_die_notifier(&uprobe_exceptions_nb);
+}
+
diff --git a/uprobe/arch/asm-x86/swap_uprobes.h b/uprobe/arch/asm-x86/swap_uprobes.h
index 2609dbec..7864fca0 100644
--- a/uprobe/arch/asm-x86/swap_uprobes.h
+++ b/uprobe/arch/asm-x86/swap_uprobes.h
@@ -7,16 +7,8 @@ static inline void arch_ujprobe_return(void)
{
}
-static inline int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list)
-{
- return 0;
-}
-
-static inline int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-
+int arch_prepare_uprobe(struct uprobe *up, struct hlist_head *page_list);
+int setjmp_upre_handler(struct kprobe *p, struct pt_regs *regs);
static inline int longjmp_break_uhandler(struct kprobe *p, struct pt_regs *regs)
{
return 0;
@@ -27,26 +19,11 @@ static inline int arch_opcode_analysis_uretprobe(kprobe_opcode_t opcode)
return 0;
}
-static inline void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
-{
-}
+void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
+unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs);
+void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
-static inline unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs)
-{
- return 0;
-}
-
-static inline void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs)
-{
-}
-
-static inline int swap_arch_init_uprobes(void)
-{
- return 0;
-}
-
-static inline void swap_arch_exit_uprobes(void)
-{
-}
+int swap_arch_init_uprobes(void);
+void swap_arch_exit_uprobes(void);
#endif /* _ARM_SWAP_UPROBES_H */
diff --git a/uprobe/swap_uprobes.c b/uprobe/swap_uprobes.c
index d2f195dc..5bfb51cd 100644
--- a/uprobe/swap_uprobes.c
+++ b/uprobe/swap_uprobes.c
@@ -360,6 +360,8 @@ struct kprobe *get_ukprobe_by_insn_slot(void *addr, pid_t tgid, struct pt_regs *
struct hlist_node *node;
struct kprobe *p;
+ addr -= UPROBES_TRAMP_RET_BREAK_IDX;
+
/* TODO: test - two processes invokes instrumented function */
head = &uprobe_insn_slot_table[hash_ptr(addr, UPROBE_HASH_BITS)];
swap_hlist_for_each_entry_rcu(p, node, head, is_hlist) {