Uretprobe handlers are invoked when the trampoline is hit, on completion the
trampoline is replaced with the saved return address and the uretprobe instance
deleted.

RFCv6 changes:
  - rework handle_uretprobe()

RFCv5 changes:
  - switch to simply linked list ->return_uprobes
  - rework handle_uretprobe()

RFCv4 changes:
  - check, whether utask is not NULL in handle_uretprobe()
  - get rid of area->rp_trampoline_vaddr
  - minor handle_uretprobe() fixups

RFCv3 changes:
  - protected uprobe with refcounter. See put_uprobe() in handle_uretprobe()
    that reflects increment in prepare_uretprobe()

RFCv2 changes:
  - get rid of ->return_consumers member from struct uprobe, introduce
    rp_handler() in consumer instead

Signed-off-by: Anton Arapov <an...@redhat.com>
---
 kernel/events/uprobes.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4ea3e91..91edd2c 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1623,6 +1623,56 @@ static void handler_chain(struct uprobe *uprobe, struct 
pt_regs *regs)
        up_read(&uprobe->register_rwsem);
 }
 
+static void handler_uretprobe_chain(struct uprobe *uprobe, struct pt_regs 
*regs)
+{
+       struct uprobe_consumer *uc;
+
+       down_read(&uprobe->register_rwsem);
+       for (uc = uprobe->consumers; uc; uc = uc->next) {
+               if (uc->rp_handler)
+                       uc->rp_handler(uc, regs);
+       }
+       up_read(&uprobe->register_rwsem);
+}
+
+static void handle_uretprobe(struct xol_area *area, struct pt_regs *regs)
+{
+       struct uprobe_task *utask;
+       struct return_instance *ri, *tmp;
+       unsigned long prev_ret_vaddr;
+
+       utask = get_utask();
+       if (!utask)
+               return;
+
+       ri = utask->return_instances;
+       if (!ri)
+               return;
+
+       instruction_pointer_set(regs, ri->orig_ret_vaddr);
+
+       while (ri) {
+               if (ri->uprobe->consumers)
+                       handler_uretprobe_chain(ri->uprobe, regs);
+
+               put_uprobe(ri->uprobe);
+               tmp = ri;
+               prev_ret_vaddr = tmp->orig_ret_vaddr;
+               ri = ri->next;
+               kfree(tmp);
+
+               if (!ri || ri->dirty == false) {
+                       /*
+                        * This is the first return uprobe (chronologically)
+                        * pushed for this particular instance of the probed
+                        * function.
+                        */
+                       utask->return_instances = ri;
+                       return;
+               }
+       }
+}
+
 /*
  * Run handler and ask thread to singlestep.
  * Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
@@ -1631,11 +1681,19 @@ static void handle_swbp(struct pt_regs *regs)
 {
        struct uprobe *uprobe;
        unsigned long bp_vaddr;
+       struct xol_area *area;
        int uninitialized_var(is_swbp);
 
        bp_vaddr = uprobe_get_swbp_addr(regs);
-       uprobe = find_active_uprobe(bp_vaddr, &is_swbp);
+       area = get_xol_area();
+       if (area) {
+               if (bp_vaddr == get_trampoline_vaddr(area)) {
+                       handle_uretprobe(area, regs);
+                       return;
+               }
+       }
 
+       uprobe = find_active_uprobe(bp_vaddr, &is_swbp);
        if (!uprobe) {
                if (is_swbp > 0) {
                        /* No matching uprobe; signal SIGTRAP. */
-- 
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to