diff options
Diffstat (limited to 'arch/arm64/kernel/probes/kprobes.c')
| -rw-r--r-- | arch/arm64/kernel/probes/kprobes.c | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c index be1f074b5736..9c70e8812ea9 100644 --- a/arch/arm64/kernel/probes/kprobes.c +++ b/arch/arm64/kernel/probes/kprobes.c | |||
| @@ -578,7 +578,95 @@ bool arch_within_kprobe_blacklist(unsigned long addr) | |||
| 578 | 578 | ||
| 579 | void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs) | 579 | void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs) |
| 580 | { | 580 | { |
| 581 | return NULL; | 581 | struct kretprobe_instance *ri = NULL; |
| 582 | struct hlist_head *head, empty_rp; | ||
| 583 | struct hlist_node *tmp; | ||
| 584 | unsigned long flags, orig_ret_address = 0; | ||
| 585 | unsigned long trampoline_address = | ||
| 586 | (unsigned long)&kretprobe_trampoline; | ||
| 587 | kprobe_opcode_t *correct_ret_addr = NULL; | ||
| 588 | |||
| 589 | INIT_HLIST_HEAD(&empty_rp); | ||
| 590 | kretprobe_hash_lock(current, &head, &flags); | ||
| 591 | |||
| 592 | /* | ||
| 593 | * It is possible to have multiple instances associated with a given | ||
| 594 | * task either because multiple functions in the call path have | ||
| 595 | * return probes installed on them, and/or more than one | ||
| 596 | * return probe was registered for a target function. | ||
| 597 | * | ||
| 598 | * We can handle this because: | ||
| 599 | * - instances are always pushed into the head of the list | ||
| 600 | * - when multiple return probes are registered for the same | ||
| 601 | * function, the (chronologically) first instance's ret_addr | ||
| 602 | * will be the real return address, and all the rest will | ||
| 603 | * point to kretprobe_trampoline. | ||
| 604 | */ | ||
| 605 | hlist_for_each_entry_safe(ri, tmp, head, hlist) { | ||
| 606 | if (ri->task != current) | ||
| 607 | /* another task is sharing our hash bucket */ | ||
| 608 | continue; | ||
| 609 | |||
| 610 | orig_ret_address = (unsigned long)ri->ret_addr; | ||
| 611 | |||
| 612 | if (orig_ret_address != trampoline_address) | ||
| 613 | /* | ||
| 614 | * This is the real return address. Any other | ||
| 615 | * instances associated with this task are for | ||
| 616 | * other calls deeper on the call stack | ||
| 617 | */ | ||
| 618 | break; | ||
| 619 | } | ||
| 620 | |||
| 621 | kretprobe_assert(ri, orig_ret_address, trampoline_address); | ||
| 622 | |||
| 623 | correct_ret_addr = ri->ret_addr; | ||
| 624 | hlist_for_each_entry_safe(ri, tmp, head, hlist) { | ||
| 625 | if (ri->task != current) | ||
| 626 | /* another task is sharing our hash bucket */ | ||
| 627 | continue; | ||
| 628 | |||
| 629 | orig_ret_address = (unsigned long)ri->ret_addr; | ||
| 630 | if (ri->rp && ri->rp->handler) { | ||
| 631 | __this_cpu_write(current_kprobe, &ri->rp->kp); | ||
| 632 | get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; | ||
| 633 | ri->ret_addr = correct_ret_addr; | ||
| 634 | ri->rp->handler(ri, regs); | ||
| 635 | __this_cpu_write(current_kprobe, NULL); | ||
| 636 | } | ||
| 637 | |||
| 638 | recycle_rp_inst(ri, &empty_rp); | ||
| 639 | |||
| 640 | if (orig_ret_address != trampoline_address) | ||
| 641 | /* | ||
| 642 | * This is the real return address. Any other | ||
| 643 | * instances associated with this task are for | ||
| 644 | * other calls deeper on the call stack | ||
| 645 | */ | ||
| 646 | break; | ||
| 647 | } | ||
| 648 | |||
| 649 | kretprobe_hash_unlock(current, &flags); | ||
| 650 | |||
| 651 | hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { | ||
| 652 | hlist_del(&ri->hlist); | ||
| 653 | kfree(ri); | ||
| 654 | } | ||
| 655 | return (void *)orig_ret_address; | ||
| 656 | } | ||
| 657 | |||
| 658 | void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, | ||
| 659 | struct pt_regs *regs) | ||
| 660 | { | ||
| 661 | ri->ret_addr = (kprobe_opcode_t *)regs->regs[30]; | ||
| 662 | |||
| 663 | /* replace return addr (x30) with trampoline */ | ||
| 664 | regs->regs[30] = (long)&kretprobe_trampoline; | ||
| 665 | } | ||
| 666 | |||
| 667 | int __kprobes arch_trampoline_kprobe(struct kprobe *p) | ||
| 668 | { | ||
| 669 | return 0; | ||
| 582 | } | 670 | } |
| 583 | 671 | ||
| 584 | int __init arch_init_kprobes(void) | 672 | int __init arch_init_kprobes(void) |
