|
|
@@ -569,6 +569,7 @@ void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
|
|
|
unsigned long *sara = stack_addr(regs);
|
|
|
|
|
|
ri->ret_addr = (kprobe_opcode_t *) *sara;
|
|
|
+ ri->fp = sara;
|
|
|
|
|
|
/* Replace the return addr with trampoline addr */
|
|
|
*sara = (unsigned long) &kretprobe_trampoline;
|
|
|
@@ -759,15 +760,21 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|
|
unsigned long flags, orig_ret_address = 0;
|
|
|
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
|
|
kprobe_opcode_t *correct_ret_addr = NULL;
|
|
|
+ void *frame_pointer;
|
|
|
+ bool skipped = false;
|
|
|
|
|
|
INIT_HLIST_HEAD(&empty_rp);
|
|
|
kretprobe_hash_lock(current, &head, &flags);
|
|
|
/* fixup registers */
|
|
|
#ifdef CONFIG_X86_64
|
|
|
regs->cs = __KERNEL_CS;
|
|
|
+ /* On x86-64, we use pt_regs->sp for return address holder. */
|
|
|
+ frame_pointer = ®s->sp;
|
|
|
#else
|
|
|
regs->cs = __KERNEL_CS | get_kernel_rpl();
|
|
|
regs->gs = 0;
|
|
|
+ /* On x86-32, we use pt_regs->flags for return address holder. */
|
|
|
+ frame_pointer = ®s->flags;
|
|
|
#endif
|
|
|
regs->ip = trampoline_address;
|
|
|
regs->orig_ax = ~0UL;
|
|
|
@@ -789,8 +796,25 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|
|
if (ri->task != current)
|
|
|
/* another task is sharing our hash bucket */
|
|
|
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;
|
|
|
+ if (skipped)
|
|
|
+ pr_warn("%ps must be blacklisted because of incorrect kretprobe order\n",
|
|
|
+ ri->rp->kp.addr);
|
|
|
|
|
|
if (orig_ret_address != trampoline_address)
|
|
|
/*
|
|
|
@@ -808,6 +832,8 @@ __visible __used void *trampoline_handler(struct pt_regs *regs)
|
|
|
if (ri->task != current)
|
|
|
/* another task is sharing our hash bucket */
|
|
|
continue;
|
|
|
+ if (ri->fp != frame_pointer)
|
|
|
+ continue;
|
|
|
|
|
|
orig_ret_address = (unsigned long)ri->ret_addr;
|
|
|
if (ri->rp && ri->rp->handler) {
|