lkml.org 
[lkml]   [2016]   [Feb]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v10 8/9] arm64: Add kernel return probes support (kretprobes)
    Date
    From: Sandeepa Prabhu <sandeepa.s.prabhu@gmail.com>

    The pre-handler of this special 'trampoline' kprobe executes the return
    probe handler functions and restores original return address in ELR_EL1.
    This way the saved pt_regs still hold the original register context to be
    carried back to the probed kernel function.

    Signed-off-by: Sandeepa Prabhu <sandeepa.s.prabhu@gmail.com>
    Signed-off-by: David A. Long <dave.long@linaro.org>
    ---
    arch/arm64/Kconfig | 1 +
    arch/arm64/kernel/kprobes.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-
    2 files changed, 75 insertions(+), 1 deletion(-)

    diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
    index c395386..72412de 100644
    --- a/arch/arm64/Kconfig
    +++ b/arch/arm64/Kconfig
    @@ -82,6 +82,7 @@ config ARM64
    select HAVE_RCU_TABLE_FREE
    select HAVE_SYSCALL_TRACEPOINTS
    select HAVE_KPROBES
    + select HAVE_KRETPROBES if HAVE_KPROBES
    select IOMMU_DMA if IOMMU_SUPPORT
    select IRQ_DOMAIN
    select IRQ_FORCED_THREADING
    diff --git a/arch/arm64/kernel/kprobes.c b/arch/arm64/kernel/kprobes.c
    index 98f4fe5..13d3333 100644
    --- a/arch/arm64/kernel/kprobes.c
    +++ b/arch/arm64/kernel/kprobes.c
    @@ -534,7 +534,80 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)

    void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs)
    {
    - return (void *) 0;
    + struct kretprobe_instance *ri = NULL;
    + struct hlist_head *head, empty_rp;
    + struct hlist_node *tmp;
    + unsigned long flags, orig_ret_addr = 0;
    + unsigned long trampoline_address =
    + (unsigned long)&kretprobe_trampoline;
    +
    + INIT_HLIST_HEAD(&empty_rp);
    + kretprobe_hash_lock(current, &head, &flags);
    +
    + /*
    + * It is possible to have multiple instances associated with a given
    + * task either because multiple functions in the call path have
    + * a return probe installed on them, and/or more than one return
    + * probe was registered for a target function.
    + *
    + * We can handle this because:
    + * - instances are always inserted at the head of the list
    + * - when multiple return probes are registered for the same
    + * function, the first instance's ret_addr will point to the
    + * real return address, and all the rest will point to
    + * kretprobe_trampoline
    + */
    + hlist_for_each_entry_safe(ri, tmp, head, hlist) {
    + if (ri->task != current)
    + /* another task is sharing our hash bucket */
    + continue;
    +
    + if (ri->rp && ri->rp->handler) {
    + __this_cpu_write(current_kprobe, &ri->rp->kp);
    + get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
    + ri->rp->handler(ri, regs);
    + __this_cpu_write(current_kprobe, NULL);
    + }
    +
    + orig_ret_addr = (unsigned long)ri->ret_addr;
    + recycle_rp_inst(ri, &empty_rp);
    +
    + if (orig_ret_addr != trampoline_address)
    + /*
    + * This is the real return address. Any other
    + * instances associated with this task are for
    + * other calls deeper on the call stack
    + */
    + break;
    + }
    +
    + kretprobe_assert(ri, orig_ret_addr, trampoline_address);
    + /* restore the original return address */
    + instruction_pointer(regs) = orig_ret_addr;
    + reset_current_kprobe();
    + kretprobe_hash_unlock(current, &flags);
    +
    + hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
    + hlist_del(&ri->hlist);
    + kfree(ri);
    + }
    +
    + /* return 1 so that post handlers not called */
    + return (void *) orig_ret_addr;
    +}
    +
    +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
    + struct pt_regs *regs)
    +{
    + ri->ret_addr = (kprobe_opcode_t *)regs->regs[30];
    +
    + /* replace return addr (x30) with trampoline */
    + regs->regs[30] = (long)&kretprobe_trampoline;
    +}
    +
    +int __kprobes arch_trampoline_kprobe(struct kprobe *p)
    +{
    + return 0;
    }

    int __init arch_init_kprobes(void)
    --
    2.5.0
    \
     
     \ /
      Last update: 2016-03-01 04:41    [W:3.405 / U:1.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site