diff options
| author | Anton Arapov <anton@redhat.com> | 2013-04-03 12:00:33 -0400 |
|---|---|---|
| committer | Oleg Nesterov <oleg@redhat.com> | 2013-04-13 09:31:55 -0400 |
| commit | 791eca10107f2886c1915d91c99a3b022a75909c (patch) | |
| tree | eb36692870d6e8e293ca27695b6980dbb6a11394 | |
| parent | e78aebfd27256ca59ccd3e6cf62cfad2a80e02d3 (diff) | |
uretprobes/x86: Hijack return address
Hijack the return address and replace it with a trampoline address.
Signed-off-by: Anton Arapov <anton@redhat.com>
Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
| -rw-r--r-- | arch/x86/include/asm/uprobes.h | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/uprobes.c | 29 |
2 files changed, 30 insertions, 0 deletions
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 8ff8be7835ab..6e5197910fd8 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h | |||
| @@ -55,4 +55,5 @@ extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); | |||
| 55 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); | 55 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); |
| 56 | extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); | 56 | extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); |
| 57 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); | 57 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); |
| 58 | extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); | ||
| 58 | #endif /* _ASM_UPROBES_H */ | 59 | #endif /* _ASM_UPROBES_H */ |
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 0ba4cfb4f412..2ed845928b5f 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c | |||
| @@ -697,3 +697,32 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
| 697 | send_sig(SIGTRAP, current, 0); | 697 | send_sig(SIGTRAP, current, 0); |
| 698 | return ret; | 698 | return ret; |
| 699 | } | 699 | } |
| 700 | |||
| 701 | unsigned long | ||
| 702 | arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs) | ||
| 703 | { | ||
| 704 | int rasize, ncopied; | ||
| 705 | unsigned long orig_ret_vaddr = 0; /* clear high bits for 32-bit apps */ | ||
| 706 | |||
| 707 | rasize = is_ia32_task() ? 4 : 8; | ||
| 708 | ncopied = copy_from_user(&orig_ret_vaddr, (void __user *)regs->sp, rasize); | ||
| 709 | if (unlikely(ncopied)) | ||
| 710 | return -1; | ||
| 711 | |||
| 712 | /* check whether address has been already hijacked */ | ||
| 713 | if (orig_ret_vaddr == trampoline_vaddr) | ||
| 714 | return orig_ret_vaddr; | ||
| 715 | |||
| 716 | ncopied = copy_to_user((void __user *)regs->sp, &trampoline_vaddr, rasize); | ||
| 717 | if (likely(!ncopied)) | ||
| 718 | return orig_ret_vaddr; | ||
| 719 | |||
| 720 | if (ncopied != rasize) { | ||
| 721 | pr_err("uprobe: return address clobbered: pid=%d, %%sp=%#lx, " | ||
| 722 | "%%ip=%#lx\n", current->pid, regs->sp, regs->ip); | ||
| 723 | |||
| 724 | force_sig_info(SIGSEGV, SEND_SIG_FORCED, current); | ||
| 725 | } | ||
| 726 | |||
| 727 | return -1; | ||
| 728 | } | ||
