Commit c978a58b authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Stefan Bader

x86/kprobes: Verify stack frame on kretprobe

BugLink: https://bugs.launchpad.net/bugs/1828420

commit 3ff9c075 upstream.

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: default avatarAndrea Righi <righi.andrea@gmail.com>
Signed-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Acked-by: default avatarSteven 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@devboxSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: default avatarStefan Bader <stefan.bader@canonical.com>
parent 8242227e
...@@ -543,6 +543,7 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) ...@@ -543,6 +543,7 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
unsigned long *sara = stack_addr(regs); unsigned long *sara = stack_addr(regs);
ri->ret_addr = (kprobe_opcode_t *) *sara; ri->ret_addr = (kprobe_opcode_t *) *sara;
ri->fp = sara;
/* Replace the return addr with trampoline addr */ /* Replace the return addr with trampoline addr */
*sara = (unsigned long) &kretprobe_trampoline; *sara = (unsigned long) &kretprobe_trampoline;
...@@ -744,15 +745,21 @@ __visible __used void *trampoline_handler(struct pt_regs *regs) ...@@ -744,15 +745,21 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
unsigned long flags, orig_ret_address = 0; unsigned long flags, orig_ret_address = 0;
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
kprobe_opcode_t *correct_ret_addr = NULL; kprobe_opcode_t *correct_ret_addr = NULL;
void *frame_pointer;
bool skipped = false;
INIT_HLIST_HEAD(&empty_rp); INIT_HLIST_HEAD(&empty_rp);
kretprobe_hash_lock(current, &head, &flags); kretprobe_hash_lock(current, &head, &flags);
/* fixup registers */ /* fixup registers */
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
regs->cs = __KERNEL_CS; regs->cs = __KERNEL_CS;
/* On x86-64, we use pt_regs->sp for return address holder. */
frame_pointer = &regs->sp;
#else #else
regs->cs = __KERNEL_CS | get_kernel_rpl(); regs->cs = __KERNEL_CS | get_kernel_rpl();
regs->gs = 0; regs->gs = 0;
/* On x86-32, we use pt_regs->flags for return address holder. */
frame_pointer = &regs->flags;
#endif #endif
regs->ip = trampoline_address; regs->ip = trampoline_address;
regs->orig_ax = ~0UL; regs->orig_ax = ~0UL;
...@@ -774,8 +781,25 @@ __visible __used void *trampoline_handler(struct pt_regs *regs) ...@@ -774,8 +781,25 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
if (ri->task != current) if (ri->task != current)
/* another task is sharing our hash bucket */ /* another task is sharing our hash bucket */
continue; continue;
/*
* Return probes must be pushed on this hash list correct
* order (same as return order) so that it can be poped
* correctly. However, if we find it is pushed it incorrect
* order, this means we find a function which should not be
* probed, because the wrong order entry is pushed on the
* path of processing other kretprobe itself.
*/
if (ri->fp != frame_pointer) {
if (!skipped)
pr_warn("kretprobe is stacked incorrectly. Trying to fixup.\n");
skipped = true;
continue;
}
orig_ret_address = (unsigned long)ri->ret_addr; orig_ret_address = (unsigned long)ri->ret_addr;
if (skipped)
pr_warn("%ps must be blacklisted because of incorrect kretprobe order\n",
ri->rp->kp.addr);
if (orig_ret_address != trampoline_address) if (orig_ret_address != trampoline_address)
/* /*
...@@ -793,6 +817,8 @@ __visible __used void *trampoline_handler(struct pt_regs *regs) ...@@ -793,6 +817,8 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
if (ri->task != current) if (ri->task != current)
/* another task is sharing our hash bucket */ /* another task is sharing our hash bucket */
continue; continue;
if (ri->fp != frame_pointer)
continue;
orig_ret_address = (unsigned long)ri->ret_addr; orig_ret_address = (unsigned long)ri->ret_addr;
if (ri->rp && ri->rp->handler) { if (ri->rp && ri->rp->handler) {
......
...@@ -197,6 +197,7 @@ struct kretprobe_instance { ...@@ -197,6 +197,7 @@ struct kretprobe_instance {
struct kretprobe *rp; struct kretprobe *rp;
kprobe_opcode_t *ret_addr; kprobe_opcode_t *ret_addr;
struct task_struct *task; struct task_struct *task;
void *fp;
char data[0]; char data[0];
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment