UPROBES_TRAMP_RET_BREAK_IDX);
}
-static DEFINE_PER_CPU(struct uprobe_ctlblk, ucb) = { 0, NULL };
+unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+{
+ return trampoline_addr(&ri->rp->up);
+}
-static struct kprobe *get_current_probe(void)
+static struct uprobe_ctlblk *current_ucb(void)
{
- return __get_cpu_var(ucb).p;
+ /* FIXME hardcoded offset */
+ return (struct uprobe_ctlblk *)(end_of_stack(current) + 20);
}
-static void set_current_probe(struct kprobe *p)
+static struct kprobe *get_current_probe(void)
{
- __get_cpu_var(ucb).p = p;
+ return current_ucb()->p;
}
-static void reset_current_probe(void)
+static void set_current_probe(struct kprobe *p)
{
- set_current_probe(NULL);
+ current_ucb()->p = p;
}
static void save_current_flags(struct pt_regs *regs)
{
- __get_cpu_var(ucb).flags = regs->EREG(flags);
+ current_ucb()->flags = regs->flags;
}
-static void restore_current_flags(struct pt_regs *regs)
+static void restore_current_flags(struct pt_regs *regs, unsigned long flags)
{
- regs->EREG(flags) &= ~IF_MASK;
- regs->EREG(flags) |= __get_cpu_var(ucb).flags & IF_MASK;
+ regs->flags &= ~IF_MASK;
+ regs->flags |= flags & IF_MASK;
}
/**
enum { call_relative_opcode = 0xe8 };
if (!read_proc_vm_atomic(task, (unsigned long)p->addr,
- tramp, MAX_INSN_SIZE))
- panic("failed to read memory %p!\n", p->addr);
+ tramp, MAX_INSN_SIZE)) {
+ printk(KERN_ERR "failed to read memory %p!\n", p->addr);
+ return -EINVAL;
+ }
/* TODO: this is a workaround */
if (tramp[0] == call_relative_opcode) {
printk(KERN_INFO "cannot install probe: 1st instruction is call\n");
- return -1;
+ return -EINVAL;
}
tramp[UPROBES_TRAMP_RET_BREAK_IDX] = BREAKPOINT_INSTRUCTION;
p->ainsn.insn = swap_slot_alloc(up->sm);
if (p->ainsn.insn == NULL) {
- printk(KERN_INFO "trampoline out of memory\n");
+ printk(KERN_ERR "trampoline out of memory\n");
return -ENOMEM;
}
if (!write_proc_vm_atomic(task, (unsigned long)p->ainsn.insn,
tramp, sizeof(up->atramp.tramp))) {
swap_slot_free(up->sm, p->ainsn.insn);
- printk("failed to write memory %p!\n", tramp);
+ printk(KERN_INFO "failed to write memory %p!\n", tramp);
return -EINVAL;
}
+ /* for uretprobe */
+ add_uprobe_table(p);
+
return 0;
}
/* 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);
+ printk(KERN_WARNING
+ "failed to read user space func arguments %lx!\n",
+ regs->sp + 4);
if (pre_entry)
p->ss_addr[smp_processor_id()] = (kprobe_opcode_t *)
* @param regs Pointer to CPU register data.
* @return Void.
*/
-void arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
+int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs)
{
/* Replace the return addr with trampoline addr */
unsigned long ra = trampoline_addr(&ri->rp->up);
+ unsigned long ret_addr;
ri->sp = (kprobe_opcode_t *)regs->sp;
- 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 (get_user(ret_addr, (unsigned long *)regs->sp)) {
+ pr_err("failed to read user space func ra %lx addr=%p!\n",
+ regs->sp, ri->rp->up.kp.addr);
+ return -EINVAL;
+ }
+
+ if (put_user(ra, (unsigned long *)regs->sp)) {
+ pr_err("failed to write user space func ra %lx!\n", regs->sp);
+ return -EINVAL;
+ }
- 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));
+ ri->ret_addr = (kprobe_opcode_t *)ret_addr;
- add_uprobe_table(&ri->rp->up.kp);
+ return 0;
}
/**
* negative error code on error.
*/
int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task)
+ struct task_struct *task, unsigned long tr)
{
- int len;
unsigned long ret_addr;
unsigned long sp = (unsigned long)ri->sp;
- unsigned long tramp_addr = trampoline_addr(&ri->rp->up);
- len = read_proc_vm_atomic(task, sp, &ret_addr, sizeof(ret_addr));
- if (len != sizeof(ret_addr)) {
+ unsigned long tramp_addr;
+
+ if (tr == 0)
+ tramp_addr = arch_tramp_by_ri(ri);
+ else
+ tramp_addr = tr; /* ri - invalid */
+
+ if (get_user(ret_addr, (unsigned long *)sp)) {
printk(KERN_INFO "---> %s (%d/%d): failed to read stack from %08lx\n",
task->comm, task->tgid, task->pid, sp);
return -EFAULT;
}
if (tramp_addr == ret_addr) {
- len = write_proc_vm_atomic(task, sp, &ri->ret_addr,
- sizeof(ri->ret_addr));
- if (len != sizeof(ri->ret_addr)) {
+ if (put_user((unsigned long)ri->ret_addr, (unsigned long *)sp)) {
printk(KERN_INFO "---> %s (%d/%d): failed to write "
"orig_ret_addr to %08lx",
task->comm, task->tgid, task->pid, sp);
swap_slot_free(up->sm, p->ainsn.insn);
}
+int arch_arm_uprobe(struct uprobe *p)
+{
+ int ret;
+ kprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
+ unsigned long vaddr = (unsigned long)p->kp.addr;
+
+ ret = write_proc_vm_atomic(p->task, vaddr, &insn, sizeof(insn));
+ if (!ret) {
+ pr_err("arch_arm_uprobe: failed to write memory tgid=%u vaddr=%08lx\n",
+ p->task->tgid, vaddr);
+
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void arch_disarm_uprobe(struct kprobe *p, struct task_struct *task)
+{
+ int ret;
+ unsigned long vaddr = (unsigned long)p->addr;
+
+ ret = write_proc_vm_atomic(task, vaddr, &p->opcode, sizeof(p->opcode));
+ if (!ret) {
+ pr_err("arch_disarm_uprobe: failed to write memory tgid=%u, vaddr=%08lx\n",
+ task->tgid, vaddr);
+ }
+}
+
static void set_user_jmp_op(void *from, void *to)
{
struct __arch_jmp_op {
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);
+ if (put_user(jop.op, (char *)from) ||
+ put_user(jop.raddr, (long *)(from + 1)))
+ pr_err("failed to write jump opcode to user space %p\n", from);
}
static void resume_execution(struct kprobe *p,
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 (get_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to read from user space sp=%lx!\n", regs->sp);
+ return;
+ }
- 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);
+ if (get_user(*(unsigned short *)insns, (unsigned short *)p->ainsn.insn)) {
+ pr_err("failed to read first 2 opcodes %p!\n", p->ainsn.insn);
+ return;
+ }
switch (insns[0]) {
case 0x9c: /* pushfl */
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));
+ if (put_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to write dword to sp=%lx\n", regs->sp);
+ return;
+ }
goto no_change;
case 0xff:
*/
*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));
+ if (put_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to write dword to sp=%lx\n", regs->sp);
+ return;
+ }
goto no_change;
} else if (((insns[1] & 0x31) == 0x20) || /* jmp near, absolute
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 (put_user(tos_dword, (unsigned long *)regs->sp)) {
+ pr_err("failed to write dword to sp=%lx\n", regs->sp);
+ return;
+ }
if (p->ainsn.boostable == 0) {
if ((regs->EREG(ip) > copy_eip) && (regs->EREG(ip) - copy_eip) +
return;
}
+static bool prepare_ss_addr(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long *ss_addr = (long *)&p->ss_addr[smp_processor_id()];
+
+ if (*ss_addr) {
+ regs->ip = *ss_addr;
+ *ss_addr = 0;
+ return true;
+ } else {
+ regs->ip = (unsigned long)p->ainsn.insn;
+ return false;
+ }
+}
+
+static void prepare_ss(struct pt_regs *regs)
+{
+ /* set single step mode */
+ regs->flags |= TF_MASK;
+ regs->flags &= ~IF_MASK;
+}
+
static int uprobe_handler(struct pt_regs *regs)
{
struct kprobe *p;
return 1;
} else {
if (!p->pre_handler || !p->pre_handler(p, regs)) {
-
if (p->ainsn.boostable == 1 && !p->post_handler) {
- if (p->ss_addr[smp_processor_id()]) {
- regs->EREG(ip) = (unsigned long)p->ss_addr[smp_processor_id()];
- p->ss_addr[smp_processor_id()] = NULL;
- } else {
- regs->EREG(ip) = (unsigned long)p->ainsn.insn;
- }
+ prepare_ss_addr(p, regs);
return 1;
}
- prepare_singlestep(p, regs);
+ if (prepare_ss_addr(p, regs) == false) {
+ set_current_probe(p);
+ prepare_ss(regs);
+ }
}
}
- set_current_probe(p);
-
return 1;
}
static int post_uprobe_handler(struct pt_regs *regs)
{
struct kprobe *p = get_current_probe();
- unsigned long flags = __get_cpu_var(ucb).flags;
+ unsigned long flags = current_ucb()->flags;
- if (p == NULL)
+ if (p == NULL) {
+ printk("task[%u %u %s] current uprobe is not found\n",
+ current->tgid, current->pid, current->comm);
return 0;
+ }
resume_execution(p, regs, flags);
- restore_current_flags(regs);
+ restore_current_flags(regs, flags);
- reset_current_probe();
+ /* clean stack */
+ current_ucb()->p = 0;
+ current_ucb()->flags = 0;
return 1;
}