Add kretprobe_find_ret_addr() for searching correct return address
from kretprobe instance list.

Signed-off-by: Masami Hiramatsu <mhira...@kernel.org>
---
 Changes in v3:
  - Remove generic stacktrace fixup. Instead, it should be solved in
    each unwinder. This just provide the generic interface.
 Changes in v2:
  - Add is_kretprobe_trampoline() for checking address outside of
    kretprobe_find_ret_addr()
  - Remove unneeded addr from kretprobe_find_ret_addr()
  - Rename fixup_kretprobe_tramp_addr() to fixup_kretprobe_trampoline()
---
 include/linux/kprobes.h |   22 +++++++++++
 kernel/kprobes.c        |   90 +++++++++++++++++++++++++++++++++--------------
 2 files changed, 86 insertions(+), 26 deletions(-)

diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 65dadd4238a2..f530f82a046d 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -215,6 +215,14 @@ static nokprobe_inline void 
*kretprobe_trampoline_addr(void)
        return dereference_function_descriptor(kretprobe_trampoline);
 }
 
+static nokprobe_inline bool is_kretprobe_trampoline(unsigned long addr)
+{
+       return (void *)addr == kretprobe_trampoline_addr();
+}
+
+unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
+                                     struct llist_node **cur);
+
 /* If the trampoline handler called from a kprobe, use this version */
 unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs,
                                             void *frame_pointer);
@@ -514,6 +522,20 @@ static inline bool is_kprobe_optinsn_slot(unsigned long 
addr)
 }
 #endif
 
+#if !defined(CONFIG_KRETPROBES)
+static nokprobe_inline bool is_kretprobe_trampoline(unsigned long addr)
+{
+       return false;
+}
+
+static nokprobe_inline
+unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
+                                     struct llist_node **cur)
+{
+       return 0;
+}
+#endif
+
 /* Returns true if kprobes handled the fault */
 static nokprobe_inline bool kprobe_page_fault(struct pt_regs *regs,
                                              unsigned int trap)
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 75c0a58c19c2..cf19edc038e4 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1858,45 +1858,68 @@ static struct notifier_block kprobe_exceptions_nb = {
 
 #ifdef CONFIG_KRETPROBES
 
-unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs,
-                                            void *frame_pointer)
+/* This assumes the tsk is current or the task which is not running. */
+static unsigned long __kretprobe_find_ret_addr(struct task_struct *tsk,
+                                              struct llist_node **cur)
 {
-       kprobe_opcode_t *correct_ret_addr = NULL;
        struct kretprobe_instance *ri = NULL;
-       struct llist_node *first, *node;
-       struct kretprobe *rp;
+       struct llist_node *node = *cur;
+
+       if (!node)
+               node = tsk->kretprobe_instances.first;
+       else
+               node = node->next;
 
-       /* Find all nodes for this frame. */
-       first = node = current->kretprobe_instances.first;
        while (node) {
                ri = container_of(node, struct kretprobe_instance, llist);
-
-               BUG_ON(ri->fp != frame_pointer);
-
                if (ri->ret_addr != kretprobe_trampoline_addr()) {
-                       correct_ret_addr = ri->ret_addr;
-                       /*
-                        * This is the real return address. Any other
-                        * instances associated with this task are for
-                        * other calls deeper on the call stack
-                        */
-                       goto found;
+                       *cur = node;
+                       return (unsigned long)ri->ret_addr;
                }
-
                node = node->next;
        }
-       pr_err("Oops! Kretprobe fails to find correct return address.\n");
-       BUG_ON(1);
+       return 0;
+}
+NOKPROBE_SYMBOL(__kretprobe_find_ret_addr);
 
-found:
-       /* Unlink all nodes for this frame. */
-       current->kretprobe_instances.first = node->next;
-       node->next = NULL;
+unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
+                                     struct llist_node **cur)
+{
+       struct kretprobe_instance *ri = NULL;
+       unsigned long ret;
+
+       do {
+               ret = __kretprobe_find_ret_addr(tsk, cur);
+               if (!ret)
+                       return ret;
+               ri = container_of(*cur, struct kretprobe_instance, llist);
+       } while (ri->fp != fp);
+
+       return ret;
+}
+NOKPROBE_SYMBOL(kretprobe_find_ret_addr);
 
-       /* Run them..  */
+unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs,
+                                            void *frame_pointer)
+{
+       kprobe_opcode_t *correct_ret_addr = NULL;
+       struct kretprobe_instance *ri = NULL;
+       struct llist_node *first, *node = NULL;
+       struct kretprobe *rp;
+
+       /* Find correct address and all nodes for this frame. */
+       correct_ret_addr = (void *)__kretprobe_find_ret_addr(current, &node);
+       if (!correct_ret_addr) {
+               pr_err("Oops! Kretprobe fails to find correct return 
address.\n");
+               BUG_ON(1);
+       }
+
+       /* Run them. */
+       first = current->kretprobe_instances.first;
        while (first) {
                ri = container_of(first, struct kretprobe_instance, llist);
-               first = first->next;
+
+               BUG_ON(ri->fp != frame_pointer);
 
                rp = get_kretprobe(ri);
                if (rp && rp->handler) {
@@ -1907,6 +1930,21 @@ unsigned long __kretprobe_trampoline_handler(struct 
pt_regs *regs,
                        rp->handler(ri, regs);
                        __this_cpu_write(current_kprobe, prev);
                }
+               if (first == node)
+                       break;
+
+               first = first->next;
+       }
+
+       /* Unlink all nodes for this frame. */
+       first = current->kretprobe_instances.first;
+       current->kretprobe_instances.first = node->next;
+       node->next = NULL;
+
+       /* Recycle them.  */
+       while (first) {
+               ri = container_of(first, struct kretprobe_instance, llist);
+               first = first->next;
 
                recycle_rp_inst(ri);
        }

Reply via email to