Skip to content

Commit bf094cf

Browse files
mhiramatrostedt
authored andcommitted
x86/kprobes: Fixup return address in generic trampoline handler
In x86, the fake return address on the stack saved by __kretprobe_trampoline() will be replaced with the real return address after returning from trampoline_handler(). Before fixing the return address, the real return address can be found in the 'current->kretprobe_instances'. However, since there is a window between updating the 'current->kretprobe_instances' and fixing the address on the stack, if an interrupt happens at that timing and the interrupt handler does stacktrace, it may fail to unwind because it can not get the correct return address from 'current->kretprobe_instances'. This will eliminate that window by fixing the return address right before updating 'current->kretprobe_instances'. Link: https://lkml.kernel.org/r/163163057094.489837.9044470370440745866.stgit@devnote2 Signed-off-by: Masami Hiramatsu <[email protected]> Tested-by: Andrii Nakryiko <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 7da8949 commit bf094cf

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

arch/x86/kernel/kprobes/core.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,16 @@ NOKPROBE_SYMBOL(__kretprobe_trampoline);
10651065
*/
10661066
STACK_FRAME_NON_STANDARD_FP(__kretprobe_trampoline);
10671067

1068+
/* This is called from kretprobe_trampoline_handler(). */
1069+
void arch_kretprobe_fixup_return(struct pt_regs *regs,
1070+
kprobe_opcode_t *correct_ret_addr)
1071+
{
1072+
unsigned long *frame_pointer = &regs->sp + 1;
1073+
1074+
/* Replace fake return address with real one. */
1075+
*frame_pointer = (unsigned long)correct_ret_addr;
1076+
}
1077+
10681078
/*
10691079
* Called from __kretprobe_trampoline
10701080
*/
@@ -1082,8 +1092,12 @@ __used __visible void trampoline_handler(struct pt_regs *regs)
10821092
regs->sp += sizeof(long);
10831093
frame_pointer = &regs->sp + 1;
10841094

1085-
/* Replace fake return address with real one. */
1086-
*frame_pointer = kretprobe_trampoline_handler(regs, frame_pointer);
1095+
/*
1096+
* The return address at 'frame_pointer' is recovered by the
1097+
* arch_kretprobe_fixup_return() which called from the
1098+
* kretprobe_trampoline_handler().
1099+
*/
1100+
kretprobe_trampoline_handler(regs, frame_pointer);
10871101

10881102
/*
10891103
* Copy FLAGS to 'pt_regs::sp' so that __kretprobe_trapmoline()

include/linux/kprobes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ extern void arch_prepare_kretprobe(struct kretprobe_instance *ri,
188188
struct pt_regs *regs);
189189
extern int arch_trampoline_kprobe(struct kprobe *p);
190190

191+
void arch_kretprobe_fixup_return(struct pt_regs *regs,
192+
kprobe_opcode_t *correct_ret_addr);
193+
191194
void __kretprobe_trampoline(void);
192195
/*
193196
* Since some architecture uses structured function pointer,

kernel/kprobes.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,6 +1922,15 @@ unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
19221922
}
19231923
NOKPROBE_SYMBOL(kretprobe_find_ret_addr);
19241924

1925+
void __weak arch_kretprobe_fixup_return(struct pt_regs *regs,
1926+
kprobe_opcode_t *correct_ret_addr)
1927+
{
1928+
/*
1929+
* Do nothing by default. Please fill this to update the fake return
1930+
* address on the stack with the correct one on each arch if possible.
1931+
*/
1932+
}
1933+
19251934
unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs,
19261935
void *frame_pointer)
19271936
{
@@ -1967,6 +1976,8 @@ unsigned long __kretprobe_trampoline_handler(struct pt_regs *regs,
19671976
first = first->next;
19681977
}
19691978

1979+
arch_kretprobe_fixup_return(regs, correct_ret_addr);
1980+
19701981
/* Unlink all nodes for this frame. */
19711982
first = current->kretprobe_instances.first;
19721983
current->kretprobe_instances.first = node->next;

0 commit comments

Comments
 (0)