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 | } | ||