struct us_ip *ip = container_of(ri->rp, struct us_ip, retprobe);
struct us_priv *priv = (struct us_priv *)ri->data;
unsigned long flags = get_preload_flags(current);
- unsigned long offset = ip->info->pl_i.handler;
+ unsigned long offset = ip->desc->info.pl_i.handler;
unsigned long vaddr = 0;
+ unsigned long base;
char __user *path = NULL;
if ((flags & HANDLER_RUNNING) ||
* @return 0 on success,\n
* negative error code on error.
*/
-int arch_prepare_uprobe(struct uprobe *up)
+int arch_prepare_uprobe(struct uprobe *p)
{
- struct kprobe *p = up2kp(up);
- struct task_struct *task = up->task;
+ int ret;
- unsigned long vaddr = (unsigned long)p->addr;
+ struct task_struct *task = p->task;
+ unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
unsigned long insn;
-
- if (vaddr & 0x01) {
- printk(KERN_INFO "Error in %s at %d: attempt "
- "to register uprobe at an unaligned address\n",
- __FILE__, __LINE__);
- return -EINVAL;
- }
+ int thumb_mode = (unsigned long)p->addr & 1;
+ unsigned long tramp[UPROBES_TRAMP_LEN];
+ unsigned long __user *utramp;
+ enum { tramp_len = sizeof(tramp) };
if (!read_proc_vm_atomic(task, vaddr, &insn, sizeof(insn))) {
printk(KERN_ERR "failed to read memory %lx!\n", vaddr);
return -EINVAL;
}
- p->opcode = insn;
-
- arch_copy_trampoline_arm_uprobe(p);
- arch_copy_trampoline_thumb_uprobe(p);
-
- if ((p->safe_arm) && (p->safe_thumb)) {
- printk(KERN_INFO "Error in %s at %d: failed "
- "arch_copy_trampoline_*_uprobe() (both) "
- "[tgid=%u, addr=%lx, data=%lx]\n",
- __FILE__, __LINE__, task->tgid, vaddr, insn);
- return -EFAULT;
+ ret = thumb_mode ?
+ arch_make_trampoline_thumb(vaddr, insn,
+ tramp, tramp_len) :
+ arch_make_trampoline_arm(vaddr, insn, tramp);
+ if (ret) {
+ pr_err("failed to make tramp, addr=%p\n", p->addr);
+ return ret;
}
- p->atramp.utramp = swap_slot_alloc(p->sm);
- if (p->atramp.utramp == NULL) {
- utramp = swap_slot_alloc(up->sm);
++ utramp = swap_slot_alloc(p->sm);
+ if (utramp == NULL) {
printk(KERN_INFO "Error: swap_slot_alloc failed (%08lx)\n",
vaddr);
return -ENOMEM;
}
- if (!write_proc_vm_atomic(up->task, (unsigned long)utramp, tramp,
++ if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp,
+ tramp_len)) {
+ pr_err("failed to write memory tramp=%p!\n", utramp);
- swap_slot_free(up->sm, utramp);
++ swap_slot_free(p->sm, utramp);
+ return -EINVAL;
+ }
+
+ flush_insns(utramp, tramp_len);
+ p->ainsn.insn = utramp;
+ p->opcode = insn;
+
+ /* for uretprobe */
+ add_uprobe_table(p);
+
return 0;
}
return 0;
}
- ((unsigned long)ri->rp->up.kp.ainsn.insn + 0x1b) :
- (unsigned long)(ri->rp->up.kp.ainsn.insn +
+ unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri)
+ {
+ /* Understand function mode */
+ return ((unsigned long)ri->sp & 1) ?
++ ((unsigned long)ri->rp->up.ainsn.insn + 0x1b) :
++ (unsigned long)(ri->rp->up.ainsn.insn +
+ UPROBES_TRAMP_RET_BREAK_IDX);
+ }
+
/**
* @brief Disarms uretprobe instance.
*
unsigned long *stack = sp - RETPROBE_STACK_DEPTH + 1;
unsigned long *found = NULL;
unsigned long *buf[RETPROBE_STACK_DEPTH];
+ unsigned long vaddr;
int i, retval;
- /* Understand function mode */
- if ((long)ri->sp & 1) {
- tramp = (unsigned long *)
- ((unsigned long)ri->rp->up.ainsn.insn + 0x1b);
+ if (tr == 0) {
- vaddr = (unsigned long)ri->rp->up.kp.addr;
++ vaddr = (unsigned long)ri->rp->up.addr;
+ tramp = (unsigned long *)arch_tramp_by_ri(ri);
} else {
- tramp = (unsigned long *)(ri->rp->up.ainsn.insn +
- UPROBES_TRAMP_RET_BREAK_IDX);
+ /* ri - invalid */
+ vaddr = 0;
+ tramp = (unsigned long *)tr;
}
/* check stack */
*/
void arch_remove_uprobe(struct uprobe *up)
{
- swap_slot_free(up->sm, up->atramp.utramp);
- swap_slot_free(up->sm, up->kp.ainsn.insn);
++ swap_slot_free(up->sm, up->ainsn.insn);
}
- static void restore_opcode_for_thumb(struct uprobe *p, struct pt_regs *regs)
+ int arch_arm_uprobe(struct uprobe *p)
{
- if (thumb_mode(regs) && !is_thumb2(p->opcode)) {
- u16 tmp = p->opcode >> 16;
- write_proc_vm_atomic(current,
- (unsigned long)((u16 *)p->addr + 1), &tmp, 2);
- flush_insns(p->addr, 4);
+ int ret;
- unsigned long vaddr = (unsigned long)p->kp.addr & ~((unsigned long)1);
- int thumb_mode = (unsigned long)p->kp.addr & 1;
++ unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
++ int thumb_mode = (unsigned long)p->addr & 1;
+ int len = 4 >> thumb_mode; /* if thumb_mode then len = 2 */
+ unsigned long insn = thumb_mode ? UBP_THUMB : UBP_ARM;
+
+ ret = write_proc_vm_atomic(p->task, vaddr, &insn, len);
+ if (!ret) {
+ pr_err("arch_arm_uprobe: failed to write memory tgid=%u addr=%08lx len=%d\n",
+ p->task->tgid, vaddr, len);
+
+ return -EACCES;
+ } else {
+ flush_insns(vaddr, len);
}
+
+ return 0;
}
- static int make_trampoline(struct uprobe *p, struct pt_regs *regs)
-void arch_disarm_uprobe(struct kprobe *p, struct task_struct *task)
++void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task)
{
- unsigned long *tramp, *utramp;
- int sw;
+ int ret;
- /*
- * 0 bit - thumb mode (0 - arm, 1 - thumb)
- * 1 bit - arm mode support (0 - off, 1 on)
- * 2 bit - thumb mode support (0 - off, 1 on)`
- */
- sw = (!!thumb_mode(regs)) |
- (int)!p->safe_arm << 1 |
- (int)!p->safe_thumb << 2;
-
- switch (sw) {
- /* ARM */
- case 0b110:
- case 0b010:
- tramp = p->atramp.tramp_arm;
- break;
- /* THUMB */
- case 0b111:
- case 0b101:
- restore_opcode_for_thumb(p, regs);
- tramp = p->atramp.tramp_thumb;
- break;
- default:
- printk(KERN_INFO "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);
-
- disarm_uprobe(p, p->task);
+ unsigned long vaddr = (unsigned long)p->addr & ~((unsigned long)1);
+ int thumb_mode = (unsigned long)p->addr & 1;
+ int len = 4 >> thumb_mode; /* if thumb_mode then len = 2 */
- return 1;
+ ret = write_proc_vm_atomic(task, vaddr, &p->opcode, len);
+ if (!ret) {
+ pr_err("arch_disarm_uprobe: failed to write memory tgid=%u addr=%08lx len=%d\n",
+ task->tgid, vaddr, len);
+ } else {
+ flush_insns(vaddr, len);
}
+ }
- utramp = p->atramp.utramp;
+ static int urp_handler(struct pt_regs *regs, pid_t tgid)
+ {
- struct kprobe *p;
++ struct uprobe *p;
+ unsigned long vaddr = regs->ARM_pc;
+ unsigned long offset_bp = thumb_mode(regs) ?
+ 0x1a :
+ 4 * UPROBES_TRAMP_RET_BREAK_IDX;
+ unsigned long tramp_addr = vaddr - offset_bp;
- if (!write_proc_vm_atomic(p->task, (unsigned long)utramp, tramp,
- UPROBES_TRAMP_LEN * sizeof(*tramp))) {
- printk(KERN_ERR "failed to write memory %p!\n", utramp);
- return -EINVAL;
- p = get_ukprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
++ p = get_uprobe_by_insn_slot((void *)tramp_addr, tgid, regs);
+ if (p == NULL) {
+ printk(KERN_INFO
+ "no_uprobe: Not one of ours: let kernel handle it %lx\n",
+ vaddr);
+ return 1;
}
- flush_insns(utramp, UPROBES_TRAMP_LEN * sizeof(*tramp));
- p->ainsn.insn = utramp;
+ trampoline_uprobe_handler(p, regs);
return 0;
}
-
+/**
+ * @brief Prepares singlestep for current CPU.
+ *
- * @param p Pointer to uprobe.
++ * @param p Pointer to kprobe.
+ * @param regs Pointer to CPU registers data.
+ * @return Void.
+ */
-
- static void uprobe_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
++static void arch_prepare_singlestep(struct uprobe *p, struct pt_regs *regs)
+{
+ int cpu = smp_processor_id();
+
+ if (p->ss_addr[cpu]) {
+ regs->ARM_pc = (unsigned long)p->ss_addr[cpu];
+ p->ss_addr[cpu] = NULL;
+ } else {
+ regs->ARM_pc = (unsigned long)p->ainsn.insn;
+ }
+}
- static int uprobe_handler(struct pt_regs *regs)
- {
- uprobe_opcode_t *addr = (uprobe_opcode_t *)(regs->ARM_pc);
- struct task_struct *task = current;
- pid_t tgid = task->tgid;
- struct uprobe *p;
-
- p = get_uprobe(addr, tgid);
- if (p == NULL) {
- unsigned long offset_bp = thumb_mode(regs) ?
- 0x1a :
- 4 * UPROBES_TRAMP_RET_BREAK_IDX;
- void *tramp_addr = (void *)addr - offset_bp;
-
- p = get_uprobe_by_insn_slot(tramp_addr, tgid, regs);
- if (p == NULL) {
- printk(KERN_INFO "no_uprobe: Not one of ours: let "
- "kernel handle it %p\n", addr);
- return 1;
- }
-
- trampoline_uprobe_handler(p, regs);
- } else {
- if (p->ainsn.insn == NULL) {
-
- if (make_trampoline(p, regs)) {
- printk(KERN_INFO "no_uprobe live\n");
- return 0;
- }
-
- /* for uretprobe */
- add_uprobe_table(p);
- }
-
- if (!p->pre_handler || !p->pre_handler(p, regs))
- uprobe_prepare_singlestep(p, regs);
- }
-
- return 0;
- }
-
/**
* @brief Breakpoint instruction handler.
*
*/
int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
{
- int ret;
+ int ret = 0;
- struct kprobe *p;
++ struct uprobe *p;
unsigned long flags;
- local_irq_save(flags);
+ unsigned long vaddr = regs->ARM_pc | !!thumb_mode(regs);
+ pid_t tgid = current->tgid;
+ local_irq_save(flags);
preempt_disable();
- ret = uprobe_handler(regs);
- swap_preempt_enable_no_resched();
- p = get_ukprobe((kprobe_opcode_t *)vaddr, tgid);
++ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
- struct uprobe *up = kp2up(p);
+ bool prepare = false;
+
- if (up->atomic_ctx) {
++ if (p->atomic_ctx) {
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ prepare = true;
+ } else {
+ swap_preempt_enable_no_resched();
+ local_irq_restore(flags);
+
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ prepare = true;
+
+ local_irq_save(flags);
+ preempt_disable();
+ }
+
+ if (prepare)
- prepare_singlestep(p, regs);
++ arch_prepare_singlestep(p, regs);
+ } else {
+ ret = urp_handler(regs, tgid);
+
+ /* check ARM/THUMB mode on correct */
+ if (ret) {
+ vaddr ^= 1;
- p = get_ukprobe((kprobe_opcode_t *)vaddr, tgid);
++ p = get_uprobe((uprobe_opcode_t *)vaddr, tgid);
+ if (p) {
+ pr_err("invalid mode: thumb=%d addr=%p insn=%08lx\n",
+ !!thumb_mode(regs), p->addr, p->opcode);
+ ret = 0;
+
+ swap_preempt_enable_no_resched();
+ local_irq_restore(flags);
+
+ disarm_uprobe(p, current);
+
+ local_irq_save(flags);
+ preempt_disable();
+ }
+ }
+ }
+
+ swap_preempt_enable_no_resched();
local_irq_restore(flags);
+
return ret;
}
struct uretprobe;
struct uretprobe_instance;
+typedef unsigned long uprobe_opcode_t;
+
+/**
+ * @struct arch_insn
+ * @brief Architecture depend copy of original instruction.
+ * @var arch_insn::insn
+ * Copy of the original instruction.
+ */
+struct arch_insn {
+ uprobe_opcode_t *insn;
+};
+
/**
- * @struct arch_specific_tramp
+ * @struct arch_tramp
* @brief Stores arch-dependent trampolines.
*/
-struct arch_specific_tramp {
+struct arch_tramp {
+ unsigned long tramp_arm[UPROBES_TRAMP_LEN]; /**< ARM trampoline */
+ unsigned long tramp_thumb[UPROBES_TRAMP_LEN]; /**< Thumb trampoline */
- void *utramp; /**< Pointer to trampoline */
++ void *utramp; /**< Pointer to trampoline */
};
void arch_opcode_analysis_uretprobe(struct uretprobe *rp);
int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task);
+ struct task_struct *task, unsigned long tr);
+ unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri);
-unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs);
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
void arch_remove_uprobe(struct uprobe *up);
-void arch_disarm_uprobe(struct kprobe *p, struct task_struct *task);
+ int arch_arm_uprobe(struct uprobe *p);
++void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
{
{
/* Replace the return addr with trampoline addr */
unsigned long ra = trampoline_addr(&ri->rp->up);
- ri->sp = (uprobe_opcode_t *)regs->sp;
+ 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))) {
- printk(KERN_ERR "failed to read user space func ra %lx addr=%p!\n",
- regs->EREG(sp), ri->rp->up.addr);
+ 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);
++ regs->sp, ri->rp->up.addr);
return -EINVAL;
}
return -EINVAL;
}
- ri->ret_addr = (kprobe_opcode_t *)ret_addr;
++ ri->ret_addr = (uprobe_opcode_t *)ret_addr;
+
return 0;
}
* @param up Pointer to the target uprobe.
* @return Void.
*/
-void arch_remove_uprobe(struct uprobe *up)
+void arch_remove_uprobe(struct uprobe *p)
{
- struct kprobe *p = up2kp(up);
-
- swap_slot_free(up->sm, p->ainsn.insn);
+ swap_slot_free(p->sm, p->ainsn.insn);
}
- kprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
- unsigned long vaddr = (unsigned long)p->kp.addr;
+ int arch_arm_uprobe(struct uprobe *p)
+ {
+ int ret;
-void arch_disarm_uprobe(struct kprobe *p, struct task_struct *task)
++ uprobe_opcode_t insn = BREAKPOINT_INSTRUCTION;
++ unsigned long vaddr = (unsigned long)p->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 uprobe *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)))
- printk(KERN_WARNING
- "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,
+static void resume_execution(struct uprobe *p,
struct pt_regs *regs,
unsigned long flags)
{
int arch_prepare_uretprobe(struct uretprobe_instance *ri, struct pt_regs *regs);
int arch_disarm_urp_inst(struct uretprobe_instance *ri,
- struct task_struct *task);
+ struct task_struct *task, unsigned long tr);
+ unsigned long arch_tramp_by_ri(struct uretprobe_instance *ri);
-unsigned long arch_get_trampoline_addr(struct kprobe *p, struct pt_regs *regs);
+unsigned long arch_get_trampoline_addr(struct uprobe *p, struct pt_regs *regs);
void arch_set_orig_ret_addr(unsigned long orig_ret_addr, struct pt_regs *regs);
void arch_remove_uprobe(struct uprobe *up);
-void arch_disarm_uprobe(struct kprobe *p, struct task_struct *task);
+ int arch_arm_uprobe(struct uprobe *p);
++void arch_disarm_uprobe(struct uprobe *p, struct task_struct *task);
static inline unsigned long swap_get_uarg(struct pt_regs *regs, unsigned long n)
{
/*
* Keep all fields in the uprobe consistent
*/
-static inline void copy_uprobe(struct kprobe *old_p, struct kprobe *p)
+static inline void copy_uprobe(struct uprobe *old_p, struct uprobe *p)
{
- memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t));
- memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn));
+ memcpy(&p->opcode, &old_p->opcode, sizeof(uprobe_opcode_t));
+ memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_insn));
- #ifdef CONFIG_ARM
- p->safe_arm = old_p->safe_arm;
- p->safe_thumb = old_p->safe_thumb;
- #endif
}
/*
* @param task Pointer to the target task.
* @return Void.
*/
-void disarm_uprobe(struct kprobe *p, struct task_struct *task)
+void disarm_uprobe(struct uprobe *p, struct task_struct *task)
{
- int ret = write_proc_vm_atomic(task, (unsigned long)p->addr,
- &p->opcode, sizeof(p->opcode));
- if (!ret) {
- printk("disarm_uprobe: failed to write memory "
- "tgid=%u, addr=%p!\n", task->tgid, p->addr);
- }
+ arch_disarm_uprobe(p, task);
}
EXPORT_SYMBOL_GPL(disarm_uprobe);
if (!p->addr)
return -EINVAL;
- /* thumb address = address-1; */
- #if defined(CONFIG_ARM)
- /* TODO: must be corrected in 'bundle' */
- if ((unsigned long) p->addr & 0x01)
- p->addr = (uprobe_opcode_t *)((unsigned long)p->addr &
- 0xfffffffe);
- #endif
-
p->ainsn.insn = NULL;
- 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;
printk(KERN_INFO "%s (%d/%d): "
"cannot disarm urp instance (%08lx)\n",
ri->task->comm, ri->task->tgid, ri->task->pid,
- (unsigned long)rp->up.kp.addr);
+ (unsigned long)rp->up.addr);
+
+ if (is_current)
+ spin_lock_irqsave(&uretprobe_lock, flags);
+
recycle_urp_inst(ri);
}
/**
* @struct uprobe
- * @brief Stores uprobe data, based on kprobe.
+ * @brief Stores uprobe data.
*/
struct uprobe {
- struct kprobe kp; /**< Kprobe for this uprobe */
- struct task_struct *task; /**< Pointer to the task struct */
- struct slot_manager *sm; /**< Pointer to slot manager */
- struct arch_specific_tramp atramp; /**< Stores trampoline */
+ struct hlist_node hlist; /**< Hash list.*/
+ /** List of probes to search by instruction slot.*/
+ struct hlist_node is_hlist;
+ /** List of uprobes for multi-handler support.*/
+ struct list_head list;
+ /** Location of the probe point. */
+ uprobe_opcode_t *addr;
+ /** Called before addr is executed.*/
+ uprobe_pre_handler_t pre_handler;
+ /** Called after addr is executed, unless...*/
+ uprobe_post_handler_t post_handler;
+ /** ... called if executing addr causes a fault (eg. page fault).*/
+ uprobe_fault_handler_t fault_handler;
+ /** Return 1 if it handled fault, otherwise kernel will see it.*/
+ uprobe_break_handler_t break_handler;
+ /** Saved opcode (which has been replaced with breakpoint).*/
+ uprobe_opcode_t opcode;
+ /** Override single-step target address, may be used to redirect
+ * control-flow to arbitrary address after probe point without
+ * invocation of original instruction; useful for functions
+ * replacement. If jprobe.entry should return address of function or
+ * NULL if original function should be called.
+ * Not supported for X86, not tested for MIPS. */
+ uprobe_opcode_t *ss_addr[NR_CPUS];
+#ifdef CONFIG_ARM
+ /** Safe/unsafe to use probe on ARM.*/
+ unsigned safe_arm:1;
+ /** Safe/unsafe to use probe on Thumb.*/
+ unsigned safe_thumb:1;
+#endif
+ struct arch_insn ainsn; /**< Copy of the original instruction.*/
+ struct arch_tramp atramp; /**< Stores trampoline */
+ struct task_struct *task; /**< Pointer to the task struct */
+ struct slot_manager *sm; /**< Pointer to slot manager */
+ bool atomic_ctx; /**< Handler context */
};
+ struct uinst_info {
+ struct hlist_node hlist;
+
+ unsigned long vaddr;
+ kprobe_opcode_t opcode;
+ };
+
+ struct urinst_info {
+ struct hlist_node hlist;
+
+ struct task_struct *task;
+ unsigned long sp;
+ unsigned long tramp;
+ unsigned long ret_addr;
+ };
+
+ struct uinst_info *uinst_info_create(unsigned long vaddr,
+ kprobe_opcode_t opcode);
+ void uinst_info_destroy(struct uinst_info *uinst);
+ void uinst_info_disarm(struct uinst_info *uinst, struct task_struct *task);
+
+
+ void urinst_info_get_current_hlist(struct hlist_head *head, bool recycle);
+ void urinst_info_put_current_hlist(struct hlist_head *head,
+ struct task_struct *task);
+
+
/**
* @brief Uprobe pre-entry handler.
*/
void __swap_unregister_uretprobe(struct uretprobe *rp, int disarm);
void swap_unregister_all_uprobes(struct task_struct *task);
- void swap_discard_pending_uretprobes(struct task_struct *task);
void swap_ujprobe_return(void);
-struct kprobe *get_ukprobe(void *addr, pid_t tgid);
-struct kprobe *get_ukprobe_by_insn_slot(void *addr,
+struct uprobe *get_uprobe(void *addr, pid_t tgid);
+struct uprobe *get_uprobe_by_insn_slot(void *addr,
pid_t tgid,
struct pt_regs *regs);
* copy_process() *
******************************************************************************
*/
- static atomic_t copy_process_cnt = ATOMIC_INIT(0);
+ static void func_uinst_creare(struct us_ip *ip, void *data)
+ {
+ struct hlist_head *head = (struct hlist_head *)data;
+ struct uprobe *up;
- static void recover_child(struct task_struct *child_task,
- struct sspt_proc *proc)
- up = probe_info_get_uprobe(ip->info, ip);
++ up = probe_info_get_uprobe(ip->desc->type, ip);
+ if (up) {
+ struct uinst_info *uinst;
- unsigned long vaddr = (unsigned long)up->kp.addr;
++ unsigned long vaddr = (unsigned long)up->addr;
+
- uinst = uinst_info_create(vaddr, up->kp.opcode);
++ uinst = uinst_info_create(vaddr, up->opcode);
+ if (uinst)
+ hlist_add_head(&uinst->hlist, head);
+ }
+ }
+
+ static void disarm_for_task(struct task_struct *child, struct hlist_head *head)
{
- sspt_proc_uninstall(proc, child_task, US_DISARM);
- swap_disarm_urp_inst_for_task(current, child_task);
+ struct uinst_info *uinst;
+ struct hlist_node *tmp;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ swap_hlist_for_each_entry_safe(uinst, node, tmp, head, hlist) {
+ uinst_info_disarm(uinst, child);
+ hlist_del(&uinst->hlist);
+ uinst_info_destroy(uinst);
+ }
}
- static void rm_uprobes_child(struct task_struct *task)
+ struct clean_data {
+ struct task_struct *task;
+
+ struct hlist_head head;
+ struct hlist_head rhead;
+ };
+
+ static atomic_t rm_uprobes_child_cnt = ATOMIC_INIT(0);
+
+ static unsigned long cb_clean_child(void *data)
+ {
+ struct clean_data *cdata = (struct clean_data *)data;
+ struct task_struct *child = cdata->task;
+
+ /* disarm up for child */
+ disarm_for_task(child, &cdata->head);
+
+ /* disarm urp for child */
+ urinst_info_put_current_hlist(&cdata->rhead, child);
+
+ atomic_dec(&rm_uprobes_child_cnt);
+ return 0;
+ }
+
+ static void rm_uprobes_child(struct kretprobe_instance *ri,
+ struct pt_regs *regs, struct task_struct *child)
{
struct sspt_proc *proc;
+ struct clean_data cdata = {
+ .task = child,
+ .head = HLIST_HEAD_INIT,
+ .rhead = HLIST_HEAD_INIT
+ };
sspt_proc_write_lock();
+ proc = sspt_proc_get_by_task_no_lock(current);
+ if (proc) {
+ sspt_proc_on_each_ip(proc, func_uinst_creare, (void *)&cdata.head);
+ urinst_info_get_current_hlist(&cdata.rhead, false);
+ }
+ sspt_proc_write_unlock();
- proc = sspt_proc_get_by_task(current);
- if (proc)
- recover_child(task, proc);
+ if (proc) {
+ int ret;
- sspt_proc_write_unlock();
+ /* set jumper */
+ ret = set_jump_cb((unsigned long)ri->ret_addr, regs,
+ cb_clean_child, &cdata, sizeof(cdata));
+ if (ret == 0) {
+ atomic_inc(&rm_uprobes_child_cnt);
+ ri->ret_addr = (unsigned long *)get_jump_addr();
+ }
+ }
}
- static int entry_handler_cp(struct kretprobe_instance *ri, struct pt_regs *regs)
- {
- atomic_inc(©_process_cnt);
+ static atomic_t pre_handler_cp_cnt = ATOMIC_INIT(0);
+
+ static unsigned long cp_cb(void *data)
+ {
if (atomic_read(&stop_flag))
call_mm_release(current);
return 0;
}
- ip = create_img_ip(addr, probe_i);
+ ip = create_img_ip(addr, pd);
+ if (ip == NULL)
+ return -ENOMEM;
-
img_add_ip_by_list(file, ip);
return 0;
read_lock(&i_proc->rwlock);
list_for_each_entry(i_file, &i_proc->file_list, list) {
file = sspt_proc_find_file_or_new(proc, i_file->dentry);
-
+ if (file) {
+ struct img_ip *i_ip;
- list_for_each_entry(i_ip, &i_file->ip_list, list)
- sspt_file_add_ip(file, i_ip);
+ list_for_each_entry(i_ip, &i_file->ip_list, list)
- sspt_file_add_ip(file, i_ip->addr, i_ip->info);
++ sspt_file_add_ip(file, i_ip);
+ }
}
read_unlock(&i_proc->rwlock);
}
return -EINVAL;
}
- up->kp.addr = (kprobe_opcode_t *)ip->orig_addr;
+ up->addr = (kprobe_opcode_t *)ip->orig_addr;
up->task = ip->page->file->proc->task;
up->sm = ip->page->file->proc->sm;
+ up->atomic_ctx = true;
- ret = probe_info_register(ip->info, ip);
+ ret = probe_info_register(ip->desc->type, ip);
if (ret) {
struct sspt_file *file = ip->page->file;
char *name = file->dentry->d_iname;
switch (flag) {
case US_UNREGS_PROBE:
- probe_info_unregister(ip->info, ip, 1);
+ probe_info_unregister(ip->desc->type, ip, 1);
break;
case US_DISARM:
- up = probe_info_get_uprobe(ip->info, ip);
+ up = probe_info_get_uprobe(ip->desc->type, ip);
- disarm_uprobe(up, task);
+ if (up)
- disarm_uprobe(&up->kp, task);
++ disarm_uprobe(up, task);
break;
case US_UNINSTALL:
- probe_info_unregister(ip->info, ip, 0);
+ probe_info_unregister(ip->desc->type, ip, 0);
break;
default:
panic("incorrect value flag=%d", flag);
* @param ret_type Return type
* @return Void
*/
-void sspt_file_add_ip(struct sspt_file *file, unsigned long offset,
- struct probe_info *probe_i)
+void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip)
{
- struct sspt_page *page =
- sspt_find_page_or_new(file, offset & PAGE_MASK);
+ unsigned long offset = 0;
+ struct sspt_page *page = NULL;
+ struct us_ip *ip = NULL;
+
+ offset = img_ip->addr & PAGE_MASK;
+ page = sspt_find_page_or_new(file, offset);
/* FIXME: delete ip */
- struct us_ip *ip = create_ip(offset, probe_i, page);
+ ip = create_ip(img_ip);
+ if (!ip)
+ return;
- if (page && ip)
- sspt_add_ip(page, ip);
+ sspt_add_ip(page, ip);
+ probe_info_init(ip->desc->type, ip);
}
+ void sspt_file_on_each_ip(struct sspt_file *file,
+ void (*func)(struct us_ip *, void *), void *data)
+ {
+ int i;
+ const int table_size = (1 << file->page_probes_hash_bits);
+ struct sspt_page *page;
+ struct hlist_head *head;
+ DECLARE_NODE_PTR_FOR_HLIST(node);
+
+ for (i = 0; i < table_size; ++i) {
+ head = &file->page_probes_table[i];
+ swap_hlist_for_each_entry(page, node, head, hlist)
+ sspt_page_on_each_ip(page, func, data);
+ }
+ }
+
/**
* @brief Get sspt_page from sspt_file (look)
*
struct sspt_page *sspt_find_page_mapped(struct sspt_file *file,
unsigned long page);
-void sspt_file_add_ip(struct sspt_file *file, unsigned long offset,
- struct probe_info *probe_i);
+void sspt_file_add_ip(struct sspt_file *file, struct img_ip *img_ip);
+ void sspt_file_on_each_ip(struct sspt_file *file,
+ void (*func)(struct us_ip *, void *), void *data);
+
struct sspt_page *sspt_get_page(struct sspt_file *file,
unsigned long offset_addr);
void sspt_put_page(struct sspt_page *page);