diff options
author | Srikar Dronamraju <srikar@linux.vnet.ibm.com> | 2008-10-28 17:07:27 -0400 |
---|---|---|
committer | Frank Ch. Eigler <fche@elastic.org> | 2008-10-28 17:07:27 -0400 |
commit | 669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf (patch) | |
tree | 7d02711c134e816b4f1503538303219ff2b35b1e | |
parent | 3cec42574e444b7f91e2d887527bea45348ffb79 (diff) | |
download | systemtap-steved-669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf.tar.gz systemtap-steved-669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf.tar.xz systemtap-steved-669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf.zip |
PR5274: uretprobes fixes, belated commit
-rw-r--r-- | runtime/uprobes/uprobes.c | 45 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.h | 2 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_i386.c | 7 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_i386.h | 8 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_ppc64.h | 12 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_x86.c | 10 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_x86.h | 11 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_x86_64.c | 10 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_x86_64.h | 11 |
9 files changed, 112 insertions, 4 deletions
diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c index 0f273e93..8c187bd1 100644 --- a/runtime/uprobes/uprobes.c +++ b/runtime/uprobes/uprobes.c @@ -2081,6 +2081,7 @@ static int uprobe_fork_uretprobe_instances(struct uprobe_task *parent_utask, return -ENOMEM; cri->rp = NULL; cri->ret_addr = pri->ret_addr; + cri->sp = pri->sp; INIT_HLIST_NODE(&cri->hlist); if (tail) hlist_add_after(tail, &cri->hlist); @@ -2309,6 +2310,44 @@ module_exit(exit_uprobes); #ifdef CONFIG_URETPROBES +/* Returns true if ri_sp lies outside the stack (beyond cursp). */ +static inline bool compare_stack_ptrs(unsigned long cursp, + unsigned long ri_sp) +{ +#ifdef CONFIG_STACK_GROWSUP + if (cursp < ri_sp) + return true; +#else + if (cursp > ri_sp) + return true; +#endif + return false; +} + +/* + * A longjmp may cause one or more uretprobed functions to terminate without + * returning. Those functions' uretprobe_instances need to be recycled. + * We detect this when any uretprobed function is subsequently called + * or returns. A bypassed uretprobe_instance's stack_ptr is beyond the + * current stack. + */ +static inline void uretprobe_bypass_instances(unsigned long cursp, + struct uprobe_task *utask) +{ + struct hlist_node *r1, *r2; + struct uretprobe_instance *ri; + struct hlist_head *head = &utask->uretprobe_instances; + + hlist_for_each_entry_safe(ri, r1, r2, head, hlist) { + if (compare_stack_ptrs(cursp, ri->sp)) { + hlist_del(&ri->hlist); + kfree(ri); + uprobe_decref_process(utask->uproc); + } else + return; + } +} + /* Called when the entry-point probe u is hit. */ static void uretprobe_handle_entry(struct uprobe *u, struct pt_regs *regs, struct uprobe_task *utask) @@ -2326,6 +2365,8 @@ static void uretprobe_handle_entry(struct uprobe *u, struct pt_regs *regs, return; ri->ret_addr = arch_hijack_uret_addr(trampoline_addr, regs, utask); if (likely(ri->ret_addr)) { + ri->sp = arch_predict_sp_at_ret(regs, utask->tsk); + uretprobe_bypass_instances(ri->sp, utask); ri->rp = container_of(u, struct uretprobe, u); INIT_HLIST_NODE(&ri->hlist); hlist_add_head(&ri->hlist, &utask->uretprobe_instances); @@ -2348,11 +2389,13 @@ static void uretprobe_handle_entry(struct uprobe *u, struct pt_regs *regs, static unsigned long uretprobe_run_handlers(struct uprobe_task *utask, struct pt_regs *regs, unsigned long trampoline_addr) { - unsigned long ret_addr; + unsigned long ret_addr, cur_sp; struct hlist_head *head = &utask->uretprobe_instances; struct uretprobe_instance *ri; struct hlist_node *r1, *r2; + cur_sp = arch_get_cur_sp(regs); + uretprobe_bypass_instances(cur_sp, utask); hlist_for_each_entry_safe(ri, r1, r2, head, hlist) { if (ri->rp && ri->rp->handler) ri->rp->handler(ri, regs); diff --git a/runtime/uprobes/uprobes.h b/runtime/uprobes/uprobes.h index 0c471474..dc970bbf 100644 --- a/runtime/uprobes/uprobes.h +++ b/runtime/uprobes/uprobes.h @@ -68,8 +68,8 @@ struct uretprobe_instance { struct uretprobe *rp; unsigned long ret_addr; struct hlist_node hlist; + unsigned long sp; unsigned long reserved1; - unsigned long reserved2; }; extern int register_uprobe(struct uprobe *u); diff --git a/runtime/uprobes/uprobes_i386.c b/runtime/uprobes/uprobes_i386.c index 51b06f79..ffa088ed 100644 --- a/runtime/uprobes/uprobes_i386.c +++ b/runtime/uprobes/uprobes_i386.c @@ -301,3 +301,10 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, } return orig_ret_addr; } + +static +unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk) +{ + return (unsigned long) (regs->esp + 4); +} diff --git a/runtime/uprobes/uprobes_i386.h b/runtime/uprobes/uprobes_i386.h index e4376b95..b5ef6363 100644 --- a/runtime/uprobes/uprobes_i386.h +++ b/runtime/uprobes/uprobes_i386.h @@ -66,7 +66,15 @@ static inline void arch_restore_uret_addr(unsigned long ret_addr, regs->eip = ret_addr; } +static unsigned long arch_get_cur_sp(struct pt_regs *regs) +{ + return (unsigned long)regs->esp; +} + static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr, struct pt_regs *regs, struct uprobe_task *utask); +static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk); + #endif /* _ASM_UPROBES_H */ diff --git a/runtime/uprobes/uprobes_ppc64.h b/runtime/uprobes/uprobes_ppc64.h index 1e274a94..56046351 100644 --- a/runtime/uprobes/uprobes_ppc64.h +++ b/runtime/uprobes/uprobes_ppc64.h @@ -67,4 +67,16 @@ static inline void arch_restore_uret_addr(unsigned long ret_addr, { regs->nip = ret_addr; } + +static unsigned long arch_get_cur_sp(struct pt_regs *regs) +{ + return (unsigned long)(regs->gpr[1]); +} + +static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk) +{ + return (unsigned long)(regs->gpr[1]); +} + #endif /* _ASM_UPROBES_H */ diff --git a/runtime/uprobes/uprobes_x86.c b/runtime/uprobes/uprobes_x86.c index 05d2a978..e3bdf8ff 100644 --- a/runtime/uprobes/uprobes_x86.c +++ b/runtime/uprobes/uprobes_x86.c @@ -716,3 +716,13 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, } return orig_ret_addr; } + +static +unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk) +{ + if (test_tsk_thread_flag(tsk, TIF_IA32)) + return (unsigned long) (REGS_SP + 4); + else + return (unsigned long) (REGS_SP + 8); +} diff --git a/runtime/uprobes/uprobes_x86.h b/runtime/uprobes/uprobes_x86.h index 30541157..041f883e 100644 --- a/runtime/uprobes/uprobes_x86.h +++ b/runtime/uprobes/uprobes_x86.h @@ -106,6 +106,15 @@ static inline void arch_restore_uret_addr(unsigned long ret_addr, REGS_IP = ret_addr; } +static unsigned long arch_get_cur_sp(struct pt_regs *regs) +{ + return (unsigned long)REGS_SP; +} + static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr, - struct pt_regs*, struct uprobe_task*); + struct pt_regs *regs, struct uprobe_task *utask); + +static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk); + #endif /* _ASM_UPROBES_H */ diff --git a/runtime/uprobes/uprobes_x86_64.c b/runtime/uprobes/uprobes_x86_64.c index a681cab5..8cf36623 100644 --- a/runtime/uprobes/uprobes_x86_64.c +++ b/runtime/uprobes/uprobes_x86_64.c @@ -701,3 +701,13 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, } return orig_ret_addr; } + +static +unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk) +{ + if (test_tsk_thread_flag(tsk, TIF_IA32)) + return (unsigned long) regs->rsp + 4; + else + return (unsigned long) regs->rsp + 8; +} diff --git a/runtime/uprobes/uprobes_x86_64.h b/runtime/uprobes/uprobes_x86_64.h index c9345073..0c70ddbe 100644 --- a/runtime/uprobes/uprobes_x86_64.h +++ b/runtime/uprobes/uprobes_x86_64.h @@ -79,6 +79,15 @@ static inline void arch_restore_uret_addr(unsigned long ret_addr, regs->rip = ret_addr; } +static unsigned long arch_get_cur_sp(struct pt_regs *regs) +{ + return (unsigned long)regs->rsp; +} + static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr, - struct pt_regs*, struct uprobe_task*); + struct pt_regs *regs, struct uprobe_task *utask); + +static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, + struct task_struct *tsk); + #endif /* _ASM_UPROBES_H */ |