Skip to content

Commit 7df482e

Browse files
author
Alexei Starovoitov
committed
Merge branch 'kprobes: rethook: x86: Replace kretprobe trampoline with rethook'
Masami Hiramatsu says: ==================== Here are the 3rd version for generic kretprobe and kretprobe on x86 for replacing the kretprobe trampoline with rethook. The previous version is here[1] [1] https://lore.kernel.org/all/164821817332.2373735.12048266953420821089.stgit@devnote2/T/#u This version fixed typo and build issues for bpf-next and CONFIG_RETHOOK=y error. I also add temporary mitigation lines for ANNOTATE_NOENDBR macro issue for bpf-next tree [2/4]. This will be removed after merging kernel IBT series. Background: This rethook came from Jiri's request of multiple kprobe for bpf[2]. He tried to solve an issue that starting bpf with multiple kprobe will take a long time because bpf-kprobe will wait for RCU grace period for sync rcu events. Jiri wanted to attach a single bpf handler to multiple kprobes and he tried to introduce multiple-probe interface to kprobe. So I asked him to use ftrace and kretprobe-like hook if it is only for the function entry and exit, instead of adding ad-hoc interface to kprobes. For this purpose, I introduced the fprobe (kprobe like interface for ftrace) with the rethook (this is a generic return hook feature for fprobe exit handler)[3]. [2] https://lore.kernel.org/all/20220104080943.113249-1-jolsa@kernel.org/T/#u [3] https://lore.kernel.org/all/164191321766.806991.7930388561276940676.stgit@devnote2/T/#u The rethook is basically same as the kretprobe trampoline. I just made it decoupled from kprobes. Eventually, the all arch dependent kretprobe trampolines will be replaced with the rethook trampoline instead of cloning and set HAVE_RETHOOK=y. When I port the rethook for all arch which supports kretprobe, the legacy kretprobe specific code (which is for CONFIG_KRETPROBE_ON_RETHOOK=n) will be removed eventually. ==================== Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents ef8a257 + 45c23bf commit 7df482e

13 files changed

Lines changed: 325 additions & 158 deletions

File tree

arch/Kconfig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,13 @@ config ARCH_USE_BUILTIN_BSWAP
164164

165165
config KRETPROBES
166166
def_bool y
167-
depends on KPROBES && HAVE_KRETPROBES
167+
depends on KPROBES && (HAVE_KRETPROBES || HAVE_RETHOOK)
168+
169+
config KRETPROBE_ON_RETHOOK
170+
def_bool y
171+
depends on HAVE_RETHOOK
172+
depends on KRETPROBES
173+
select RETHOOK
168174

169175
config USER_RETURN_NOTIFIER
170176
bool

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ config X86
224224
select HAVE_KPROBES_ON_FTRACE
225225
select HAVE_FUNCTION_ERROR_INJECTION
226226
select HAVE_KRETPROBES
227+
select HAVE_RETHOOK
227228
select HAVE_KVM
228229
select HAVE_LIVEPATCH if X86_64
229230
select HAVE_MIXED_BREAKPOINTS_REGS

arch/x86/include/asm/unwind.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#include <linux/sched.h>
66
#include <linux/ftrace.h>
7-
#include <linux/kprobes.h>
7+
#include <linux/rethook.h>
88
#include <asm/ptrace.h>
99
#include <asm/stacktrace.h>
1010

@@ -16,7 +16,7 @@ struct unwind_state {
1616
unsigned long stack_mask;
1717
struct task_struct *task;
1818
int graph_idx;
19-
#ifdef CONFIG_KRETPROBES
19+
#if defined(CONFIG_RETHOOK)
2020
struct llist_node *kr_cur;
2121
#endif
2222
bool error;
@@ -104,19 +104,18 @@ void unwind_module_init(struct module *mod, void *orc_ip, size_t orc_ip_size,
104104
#endif
105105

106106
static inline
107-
unsigned long unwind_recover_kretprobe(struct unwind_state *state,
108-
unsigned long addr, unsigned long *addr_p)
107+
unsigned long unwind_recover_rethook(struct unwind_state *state,
108+
unsigned long addr, unsigned long *addr_p)
109109
{
110-
#ifdef CONFIG_KRETPROBES
111-
return is_kretprobe_trampoline(addr) ?
112-
kretprobe_find_ret_addr(state->task, addr_p, &state->kr_cur) :
113-
addr;
114-
#else
115-
return addr;
110+
#ifdef CONFIG_RETHOOK
111+
if (is_rethook_trampoline(addr))
112+
return rethook_find_ret_addr(state->task, (unsigned long)addr_p,
113+
&state->kr_cur);
116114
#endif
115+
return addr;
117116
}
118117

119-
/* Recover the return address modified by kretprobe and ftrace_graph. */
118+
/* Recover the return address modified by rethook and ftrace_graph. */
120119
static inline
121120
unsigned long unwind_recover_ret_addr(struct unwind_state *state,
122121
unsigned long addr, unsigned long *addr_p)
@@ -125,7 +124,7 @@ unsigned long unwind_recover_ret_addr(struct unwind_state *state,
125124

126125
ret = ftrace_graph_ret_addr(state->task, &state->graph_idx,
127126
addr, addr_p);
128-
return unwind_recover_kretprobe(state, ret, addr_p);
127+
return unwind_recover_rethook(state, ret, addr_p);
129128
}
130129

131130
/*

arch/x86/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
103103
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
104104
obj-$(CONFIG_X86_TSC) += trace_clock.o
105105
obj-$(CONFIG_TRACING) += trace.o
106+
obj-$(CONFIG_RETHOOK) += rethook.o
106107
obj-$(CONFIG_CRASH_CORE) += crash_core_$(BITS).o
107108
obj-$(CONFIG_KEXEC_CORE) += machine_kexec_$(BITS).o
108109
obj-$(CONFIG_KEXEC_CORE) += relocate_kernel_$(BITS).o crash.o

arch/x86/kernel/kprobes/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <asm/asm.h>
88
#include <asm/frame.h>
9+
#include <asm/insn.h>
910

1011
#ifdef CONFIG_X86_64
1112

arch/x86/kernel/kprobes/core.c

Lines changed: 0 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -811,18 +811,6 @@ set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
811811
= (regs->flags & X86_EFLAGS_IF);
812812
}
813813

814-
void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
815-
{
816-
unsigned long *sara = stack_addr(regs);
817-
818-
ri->ret_addr = (kprobe_opcode_t *) *sara;
819-
ri->fp = sara;
820-
821-
/* Replace the return addr with trampoline addr */
822-
*sara = (unsigned long) &__kretprobe_trampoline;
823-
}
824-
NOKPROBE_SYMBOL(arch_prepare_kretprobe);
825-
826814
static void kprobe_post_process(struct kprobe *cur, struct pt_regs *regs,
827815
struct kprobe_ctlblk *kcb)
828816
{
@@ -1023,101 +1011,6 @@ int kprobe_int3_handler(struct pt_regs *regs)
10231011
}
10241012
NOKPROBE_SYMBOL(kprobe_int3_handler);
10251013

1026-
/*
1027-
* When a retprobed function returns, this code saves registers and
1028-
* calls trampoline_handler() runs, which calls the kretprobe's handler.
1029-
*/
1030-
asm(
1031-
".text\n"
1032-
".global __kretprobe_trampoline\n"
1033-
".type __kretprobe_trampoline, @function\n"
1034-
"__kretprobe_trampoline:\n"
1035-
#ifdef CONFIG_X86_64
1036-
ANNOTATE_NOENDBR
1037-
/* Push a fake return address to tell the unwinder it's a kretprobe. */
1038-
" pushq $__kretprobe_trampoline\n"
1039-
UNWIND_HINT_FUNC
1040-
/* Save the 'sp - 8', this will be fixed later. */
1041-
" pushq %rsp\n"
1042-
" pushfq\n"
1043-
SAVE_REGS_STRING
1044-
" movq %rsp, %rdi\n"
1045-
" call trampoline_handler\n"
1046-
RESTORE_REGS_STRING
1047-
/* In trampoline_handler(), 'regs->flags' is copied to 'regs->sp'. */
1048-
" addq $8, %rsp\n"
1049-
" popfq\n"
1050-
#else
1051-
/* Push a fake return address to tell the unwinder it's a kretprobe. */
1052-
" pushl $__kretprobe_trampoline\n"
1053-
UNWIND_HINT_FUNC
1054-
/* Save the 'sp - 4', this will be fixed later. */
1055-
" pushl %esp\n"
1056-
" pushfl\n"
1057-
SAVE_REGS_STRING
1058-
" movl %esp, %eax\n"
1059-
" call trampoline_handler\n"
1060-
RESTORE_REGS_STRING
1061-
/* In trampoline_handler(), 'regs->flags' is copied to 'regs->sp'. */
1062-
" addl $4, %esp\n"
1063-
" popfl\n"
1064-
#endif
1065-
ASM_RET
1066-
".size __kretprobe_trampoline, .-__kretprobe_trampoline\n"
1067-
);
1068-
NOKPROBE_SYMBOL(__kretprobe_trampoline);
1069-
/*
1070-
* __kretprobe_trampoline() skips updating frame pointer. The frame pointer
1071-
* saved in trampoline_handler() points to the real caller function's
1072-
* frame pointer. Thus the __kretprobe_trampoline() doesn't have a
1073-
* standard stack frame with CONFIG_FRAME_POINTER=y.
1074-
* Let's mark it non-standard function. Anyway, FP unwinder can correctly
1075-
* unwind without the hint.
1076-
*/
1077-
STACK_FRAME_NON_STANDARD_FP(__kretprobe_trampoline);
1078-
1079-
/* This is called from kretprobe_trampoline_handler(). */
1080-
void arch_kretprobe_fixup_return(struct pt_regs *regs,
1081-
kprobe_opcode_t *correct_ret_addr)
1082-
{
1083-
unsigned long *frame_pointer = &regs->sp + 1;
1084-
1085-
/* Replace fake return address with real one. */
1086-
*frame_pointer = (unsigned long)correct_ret_addr;
1087-
}
1088-
1089-
/*
1090-
* Called from __kretprobe_trampoline
1091-
*/
1092-
__used __visible void trampoline_handler(struct pt_regs *regs)
1093-
{
1094-
unsigned long *frame_pointer;
1095-
1096-
/* fixup registers */
1097-
regs->cs = __KERNEL_CS;
1098-
#ifdef CONFIG_X86_32
1099-
regs->gs = 0;
1100-
#endif
1101-
regs->ip = (unsigned long)&__kretprobe_trampoline;
1102-
regs->orig_ax = ~0UL;
1103-
regs->sp += sizeof(long);
1104-
frame_pointer = &regs->sp + 1;
1105-
1106-
/*
1107-
* The return address at 'frame_pointer' is recovered by the
1108-
* arch_kretprobe_fixup_return() which called from the
1109-
* kretprobe_trampoline_handler().
1110-
*/
1111-
kretprobe_trampoline_handler(regs, frame_pointer);
1112-
1113-
/*
1114-
* Copy FLAGS to 'pt_regs::sp' so that __kretprobe_trapmoline()
1115-
* can do RET right after POPF.
1116-
*/
1117-
regs->sp = regs->flags;
1118-
}
1119-
NOKPROBE_SYMBOL(trampoline_handler);
1120-
11211014
int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
11221015
{
11231016
struct kprobe *cur = kprobe_running();

arch/x86/kernel/kprobes/opt.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ asm (
106106
".global optprobe_template_entry\n"
107107
"optprobe_template_entry:\n"
108108
#ifdef CONFIG_X86_64
109-
/* We don't bother saving the ss register */
109+
" pushq $" __stringify(__KERNEL_DS) "\n"
110+
/* Save the 'sp - 8', this will be fixed later. */
110111
" pushq %rsp\n"
111112
" pushfq\n"
112113
".global optprobe_template_clac\n"
@@ -121,14 +122,17 @@ asm (
121122
".global optprobe_template_call\n"
122123
"optprobe_template_call:\n"
123124
ASM_NOP5
124-
/* Move flags to rsp */
125+
/* Copy 'regs->flags' into 'regs->ss'. */
125126
" movq 18*8(%rsp), %rdx\n"
126-
" movq %rdx, 19*8(%rsp)\n"
127+
" movq %rdx, 20*8(%rsp)\n"
127128
RESTORE_REGS_STRING
128-
/* Skip flags entry */
129-
" addq $8, %rsp\n"
129+
/* Skip 'regs->flags' and 'regs->sp'. */
130+
" addq $16, %rsp\n"
131+
/* And pop flags register from 'regs->ss'. */
130132
" popfq\n"
131133
#else /* CONFIG_X86_32 */
134+
" pushl %ss\n"
135+
/* Save the 'sp - 4', this will be fixed later. */
132136
" pushl %esp\n"
133137
" pushfl\n"
134138
".global optprobe_template_clac\n"
@@ -142,12 +146,13 @@ asm (
142146
".global optprobe_template_call\n"
143147
"optprobe_template_call:\n"
144148
ASM_NOP5
145-
/* Move flags into esp */
149+
/* Copy 'regs->flags' into 'regs->ss'. */
146150
" movl 14*4(%esp), %edx\n"
147-
" movl %edx, 15*4(%esp)\n"
151+
" movl %edx, 16*4(%esp)\n"
148152
RESTORE_REGS_STRING
149-
/* Skip flags entry */
150-
" addl $4, %esp\n"
153+
/* Skip 'regs->flags' and 'regs->sp'. */
154+
" addl $8, %esp\n"
155+
/* And pop flags register from 'regs->ss'. */
151156
" popfl\n"
152157
#endif
153158
".global optprobe_template_end\n"
@@ -179,6 +184,8 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
179184
kprobes_inc_nmissed_count(&op->kp);
180185
} else {
181186
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
187+
/* Adjust stack pointer */
188+
regs->sp += sizeof(long);
182189
/* Save skipped registers */
183190
regs->cs = __KERNEL_CS;
184191
#ifdef CONFIG_X86_32

0 commit comments

Comments
 (0)