aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@kernel.org>2019-02-23 11:49:52 -0500
committerIngo Molnar <mingo@kernel.org>2019-04-19 08:26:05 -0400
commit3ff9c075cc767b3060bdac12da72fc94dd7da1b8 (patch)
tree68514aa6c9c1556f490fc7e729562f2b18a593f1
parent3fe3331bb285700ab2253dbb07f8e478fcea2f1b (diff)
x86/kprobes: Verify stack frame on kretprobe
Verify the stack frame pointer on kretprobe trampoline handler, If the stack frame pointer does not match, it skips the wrong entry and tries to find correct one. This can happen if user puts the kretprobe on the function which can be used in the path of ftrace user-function call. Such functions should not be probed, so this adds a warning message that reports which function should be blacklisted. Tested-by: Andrea Righi <righi.andrea@gmail.com> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Acked-by: Steven Rostedt <rostedt@goodmis.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/155094059185.6137.15527904013362842072.stgit@devbox Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/kernel/kprobes/core.c26
-rw-r--r--include/linux/kprobes.h1
2 files changed, 27 insertions, 0 deletions
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index a034cb808e7e..18fbe9be2d68 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -569,6 +569,7 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
569 unsigned long *sara = stack_addr(regs); 569 unsigned long *sara = stack_addr(regs);
570 570
571 ri->ret_addr = (kprobe_opcode_t *) *sara; 571 ri->ret_addr = (kprobe_opcode_t *) *sara;
572 ri->fp = sara;
572 573
573 /* Replace the return addr with trampoline addr */ 574 /* Replace the return addr with trampoline addr */
574 *sara = (unsigned long) &kretprobe_trampoline; 575 *sara = (unsigned long) &kretprobe_trampoline;
@@ -759,15 +760,21 @@ static __used void *trampoline_handler(struct pt_regs *regs)
759 unsigned long flags, orig_ret_address = 0; 760 unsigned long flags, orig_ret_address = 0;
760 unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; 761 unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
761 kprobe_opcode_t *correct_ret_addr = NULL; 762 kprobe_opcode_t *correct_ret_addr = NULL;
763 void *frame_pointer;
764 bool skipped = false;
762 765
763 INIT_HLIST_HEAD(&empty_rp); 766 INIT_HLIST_HEAD(&empty_rp);
764 kretprobe_hash_lock(current, &head, &flags); 767 kretprobe_hash_lock(current, &head, &flags);
765 /* fixup registers */ 768 /* fixup registers */
766#ifdef CONFIG_X86_64 769#ifdef CONFIG_X86_64
767 regs->cs = __KERNEL_CS; 770 regs->cs = __KERNEL_CS;
771 /* On x86-64, we use pt_regs->sp for return address holder. */
772 frame_pointer = &regs->sp;
768#else 773#else
769 regs->cs = __KERNEL_CS | get_kernel_rpl(); 774 regs->cs = __KERNEL_CS | get_kernel_rpl();
770 regs->gs = 0; 775 regs->gs = 0;
776 /* On x86-32, we use pt_regs->flags for return address holder. */
777 frame_pointer = &regs->flags;
771#endif 778#endif
772 regs->ip = trampoline_address; 779 regs->ip = trampoline_address;
773 regs->orig_ax = ~0UL; 780 regs->orig_ax = ~0UL;
@@ -789,8 +796,25 @@ static __used void *trampoline_handler(struct pt_regs *regs)
789 if (ri->task != current) 796 if (ri->task != current)
790 /* another task is sharing our hash bucket */ 797 /* another task is sharing our hash bucket */
791 continue; 798 continue;
799 /*
800 * Return probes must be pushed on this hash list correct
801 * order (same as return order) so that it can be poped
802 * correctly. However, if we find it is pushed it incorrect
803 * order, this means we find a function which should not be
804 * probed, because the wrong order entry is pushed on the
805 * path of processing other kretprobe itself.
806 */
807 if (ri->fp != frame_pointer) {
808 if (!skipped)
809 pr_warn("kretprobe is stacked incorrectly. Trying to fixup.\n");
810 skipped = true;
811 continue;
812 }
792 813
793 orig_ret_address = (unsigned long)ri->ret_addr; 814 orig_ret_address = (unsigned long)ri->ret_addr;
815 if (skipped)
816 pr_warn("%ps must be blacklisted because of incorrect kretprobe order\n",
817 ri->rp->kp.addr);
794 818
795 if (orig_ret_address != trampoline_address) 819 if (orig_ret_address != trampoline_address)
796 /* 820 /*
@@ -808,6 +832,8 @@ static __used void *trampoline_handler(struct pt_regs *regs)
808 if (ri->task != current) 832 if (ri->task != current)
809 /* another task is sharing our hash bucket */ 833 /* another task is sharing our hash bucket */
810 continue; 834 continue;
835 if (ri->fp != frame_pointer)
836 continue;
811 837
812 orig_ret_address = (unsigned long)ri->ret_addr; 838 orig_ret_address = (unsigned long)ri->ret_addr;
813 if (ri->rp && ri->rp->handler) { 839 if (ri->rp && ri->rp->handler) {
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 201f0f2683f2..9a897256e481 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -173,6 +173,7 @@ struct kretprobe_instance {
173 struct kretprobe *rp; 173 struct kretprobe *rp;
174 kprobe_opcode_t *ret_addr; 174 kprobe_opcode_t *ret_addr;
175 struct task_struct *task; 175 struct task_struct *task;
176 void *fp;
176 char data[0]; 177 char data[0];
177}; 178};
178 179