summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrikar Dronamraju <srikar@linux.vnet.ibm.com>2008-10-28 17:07:27 -0400
committerFrank Ch. Eigler <fche@elastic.org>2008-10-28 17:07:27 -0400
commit669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf (patch)
tree7d02711c134e816b4f1503538303219ff2b35b1e
parent3cec42574e444b7f91e2d887527bea45348ffb79 (diff)
downloadsystemtap-steved-669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf.tar.gz
systemtap-steved-669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf.tar.xz
systemtap-steved-669bdbfc3aee6d85f4f1b84766a5bfdcb0cc48cf.zip
PR5274: uretprobes fixes, belated commit
-rw-r--r--runtime/uprobes/uprobes.c45
-rw-r--r--runtime/uprobes/uprobes.h2
-rw-r--r--runtime/uprobes/uprobes_i386.c7
-rw-r--r--runtime/uprobes/uprobes_i386.h8
-rw-r--r--runtime/uprobes/uprobes_ppc64.h12
-rw-r--r--runtime/uprobes/uprobes_x86.c10
-rw-r--r--runtime/uprobes/uprobes_x86.h11
-rw-r--r--runtime/uprobes/uprobes_x86_64.c10
-rw-r--r--runtime/uprobes/uprobes_x86_64.h11
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 */