Hello,
Here is the 6th version of the series to fix the stacktrace with kretprobe
on x86.
The previous version is;
https://lore.kernel.org/bpf/161676170650.330141.6214727134265514123.stgit@devnote2/
This version is rebased on the latest tip tree and add some patches for
improving stacktrace[13/13].
Changes from v5:
[02/13]:
- Use dereference_symbol_descriptor() instead of dereference_function_descriptor()
[04/13]:
- Replace BUG_ON() with WARN_ON_ONCE() in __kretprobe_trampoline_handler().
[13/13]:
- Add a new patch to fix return address in earlier stage.
With this series, unwinder can unwind stack correctly from ftrace as below;
# cd /sys/kernel/debug/tracing
# echo > trace
# echo 1 > options/sym-offset
# echo r vfs_read >> kprobe_events
# echo r full_proxy_read >> kprobe_events
# echo traceoff:1 > events/kprobes/r_vfs_read_0/trigger
# echo stacktrace:1 > events/kprobes/r_full_proxy_read_0/trigger
# echo 1 > events/kprobes/enable
# cat /sys/kernel/debug/kprobes/list
ffffffff8133b740 r full_proxy_read+0x0 [FTRACE]
ffffffff812560b0 r vfs_read+0x0 [FTRACE]
# echo 0 > events/kprobes/enable
# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 3/3 #P:8
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
<...>-134 [007] ...1 16.185877: r_full_proxy_read_0: (vfs_read+0x98/0x180 <- full_proxy_read)
<...>-134 [007] ...1 16.185901: <stack trace>
=> kretprobe_trace_func+0x209/0x300
=> kretprobe_dispatcher+0x4a/0x70
=> __kretprobe_trampoline_handler+0xd4/0x170
=> trampoline_handler+0x43/0x60
=> kretprobe_trampoline+0x2a/0x50
=> vfs_read+0x98/0x180
=> ksys_read+0x5f/0xe0
=> do_syscall_64+0x37/0x90
=> entry_SYSCALL_64_after_hwframe+0x44/0xae
<...>-134 [007] ...1 16.185902: r_vfs_read_0: (ksys_read+0x5f/0xe0 <- vfs_read)
This shows the double return probes (vfs_read and full_proxy_read) on the stack
correctly unwinded. (vfs_read will return to ksys_read+0x5f and full_proxy_read
will return to vfs_read+0x98)
This actually changes the kretprobe behavisor a bit, now the instraction pointer in
the pt_regs passed to kretprobe user handler is correctly set the real return
address. So user handlers can get it via instruction_pointer() API.
You can also get this series from
git://git.kernel.org/pub/scm/linux/kernel/git/mhiramat/linux.git kprobes/kretprobe-stackfix-v6
Thank you,
---
Josh Poimboeuf (1):
x86/kprobes: Add UNWIND_HINT_FUNC on kretprobe_trampoline code
Masami Hiramatsu (12):
ia64: kprobes: Fix to pass correct trampoline address to the handler
kprobes: treewide: Replace arch_deref_entry_point() with dereference_symbol_descriptor()
kprobes: treewide: Remove trampoline_address from kretprobe_trampoline_handler()
kprobes: Add kretprobe_find_ret_addr() for searching return address
ARC: Add instruction_pointer_set() API
ia64: Add instruction_pointer_set() API
arm: kprobes: Make a space for regs->ARM_pc at kretprobe_trampoline
kprobes: Setup instruction pointer in __kretprobe_trampoline_handler
x86/kprobes: Push a fake return address at kretprobe_trampoline
x86/unwind: Recover kretprobe trampoline entry
tracing: Show kretprobe unknown indicator only for kretprobe_trampoline
x86/kprobes: Fixup return address in generic trampoline handler
arch/arc/include/asm/ptrace.h | 5 ++
arch/arc/kernel/kprobes.c | 2 -
arch/arm/probes/kprobes/core.c | 5 +-
arch/arm64/kernel/probes/kprobes.c | 3 -
arch/csky/kernel/probes/kprobes.c | 2 -
arch/ia64/include/asm/ptrace.h | 5 ++
arch/ia64/kernel/kprobes.c | 15 ++---
arch/mips/kernel/kprobes.c | 3 -
arch/parisc/kernel/kprobes.c | 4 +
arch/powerpc/kernel/kprobes.c | 13 ----
arch/riscv/kernel/probes/kprobes.c | 2 -
arch/s390/kernel/kprobes.c | 2 -
arch/sh/kernel/kprobes.c | 2 -
arch/sparc/kernel/kprobes.c | 2 -
arch/x86/include/asm/kprobes.h | 1
arch/x86/include/asm/unwind.h | 23 +++++++
arch/x86/include/asm/unwind_hints.h | 5 ++
arch/x86/kernel/kprobes/core.c | 53 +++++++++++++++--
arch/x86/kernel/unwind_frame.c | 4 +
arch/x86/kernel/unwind_guess.c | 3 -
arch/x86/kernel/unwind_orc.c | 19 +++++-
include/linux/kprobes.h | 41 +++++++++++--
kernel/kprobes.c | 108 +++++++++++++++++++++++++----------
kernel/trace/trace_output.c | 17 +-----
lib/error-inject.c | 3 +
25 files changed, 237 insertions(+), 105 deletions(-)
--
Masami Hiramatsu (Linaro) <[email protected]>
Commit e792ff804f49 ("ia64: kprobes: Use generic kretprobe trampoline handler")
missed to pass the wrong trampoline address (it passes the descriptor address
instead of function entry address).
This fixes it to pass correct trampoline address to __kretprobe_trampoline_handler().
This also changes to use correct symbol dereference function to get the
function address from the kretprobe_trampoline.
Fixes: e792ff804f49 ("ia64: kprobes: Use generic kretprobe trampoline handler")
Signed-off-by: Masami Hiramatsu <[email protected]>
---
Changes in v5:
- Fix a compile error typo.
---
arch/ia64/kernel/kprobes.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c
index fc1ff8a4d7de..ca4b4fa45aef 100644
--- a/arch/ia64/kernel/kprobes.c
+++ b/arch/ia64/kernel/kprobes.c
@@ -398,7 +398,8 @@ static void kretprobe_trampoline(void)
int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
{
- regs->cr_iip = __kretprobe_trampoline_handler(regs, kretprobe_trampoline, NULL);
+ regs->cr_iip = __kretprobe_trampoline_handler(regs,
+ dereference_function_descriptor(kretprobe_trampoline), NULL);
/*
* By returning a non-zero value, we are telling
* kprobe_handler() that we don't want the post_handler
@@ -414,7 +415,7 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
ri->fp = NULL;
/* Replace the return addr with trampoline addr */
- regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
+ regs->b0 = (unsigned long)dereference_function_descriptor(kretprobe_trampoline);
}
/* Check the instruction in the slot is break */
@@ -918,14 +919,14 @@ static struct kprobe trampoline_p = {
int __init arch_init_kprobes(void)
{
trampoline_p.addr =
- (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip;
+ dereference_function_descriptor(kretprobe_trampoline);
return register_kprobe(&trampoline_p);
}
int __kprobes arch_trampoline_kprobe(struct kprobe *p)
{
if (p->addr ==
- (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip)
+ dereference_function_descriptor(kretprobe_trampoline))
return 1;
return 0;
From: Josh Poimboeuf <[email protected]>
Add UNWIND_HINT_FUNC on kretporbe_trampoline code so that ORC
information is generated on the kretprobe_trampoline correctly.
Note that when the CONFIG_FRAME_POINTER=y, since the
kretprobe_trampoline skips updating frame pointer, the stack frame
of the kretprobe_trampoline seems non-standard. So this marks it
is STACK_FRAME_NON_STANDARD() and undefine UNWIND_HINT_FUNC.
Anyway, with the frame pointer, FP unwinder can unwind the stack
frame correctly without that hint.
Signed-off-by: Josh Poimboeuf <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
---
Changes in v4:
- Apply UNWIND_HINT_FUNC only if CONFIG_FRAME_POINTER=n.
---
arch/x86/include/asm/unwind_hints.h | 5 +++++
arch/x86/kernel/kprobes/core.c | 17 +++++++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/unwind_hints.h b/arch/x86/include/asm/unwind_hints.h
index 8e574c0afef8..8b33674288ea 100644
--- a/arch/x86/include/asm/unwind_hints.h
+++ b/arch/x86/include/asm/unwind_hints.h
@@ -52,6 +52,11 @@
UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=8 type=UNWIND_HINT_TYPE_FUNC
.endm
+#else
+
+#define UNWIND_HINT_FUNC \
+ UNWIND_HINT(ORC_REG_SP, 8, UNWIND_HINT_TYPE_FUNC, 0)
+
#endif /* __ASSEMBLY__ */
#endif /* _ASM_X86_UNWIND_HINTS_H */
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index d32b09ca3221..9a6763fd066e 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -1019,6 +1019,19 @@ int kprobe_int3_handler(struct pt_regs *regs)
}
NOKPROBE_SYMBOL(kprobe_int3_handler);
+#ifdef CONFIG_FRAME_POINTER
+/*
+ * kretprobe_trampoline skips updating frame pointer. The frame pointer
+ * saved in trampoline_handler points to the real caller function's
+ * frame pointer. Thus the kretprobe_trampoline doesn't seems to have a
+ * standard stack frame with CONFIG_FRAME_POINTER=y.
+ * Let's mark it non-standard function. Anyway, FP unwinder can correctly
+ * unwind without the hint.
+ */
+STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
+#undef UNWIND_HINT_FUNC
+#define UNWIND_HINT_FUNC
+#endif
/*
* When a retprobed function returns, this code saves registers and
* calls trampoline_handler() runs, which calls the kretprobe's handler.
@@ -1031,6 +1044,7 @@ asm(
/* We don't bother saving the ss register */
#ifdef CONFIG_X86_64
" pushq %rsp\n"
+ UNWIND_HINT_FUNC
" pushfq\n"
SAVE_REGS_STRING
" movq %rsp, %rdi\n"
@@ -1041,6 +1055,7 @@ asm(
" popfq\n"
#else
" pushl %esp\n"
+ UNWIND_HINT_FUNC
" pushfl\n"
SAVE_REGS_STRING
" movl %esp, %eax\n"
@@ -1054,8 +1069,6 @@ asm(
".size kretprobe_trampoline, .-kretprobe_trampoline\n"
);
NOKPROBE_SYMBOL(kretprobe_trampoline);
-STACK_FRAME_NON_STANDARD(kretprobe_trampoline);
-
/*
* Called from kretprobe_trampoline
Change kretprobe_trampoline to make a space for regs->ARM_pc so that
kretprobe_trampoline_handler can call instruction_pointer_set()
safely.
Signed-off-by: Masami Hiramatsu <[email protected]>
---
arch/arm/probes/kprobes/core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c
index 1782b41df095..5f3c2b42787f 100644
--- a/arch/arm/probes/kprobes/core.c
+++ b/arch/arm/probes/kprobes/core.c
@@ -397,11 +397,13 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
void __naked __kprobes kretprobe_trampoline(void)
{
__asm__ __volatile__ (
+ "sub sp, sp, #16 \n\t"
"stmdb sp!, {r0 - r11} \n\t"
"mov r0, sp \n\t"
"bl trampoline_handler \n\t"
"mov lr, r0 \n\t"
"ldmia sp!, {r0 - r11} \n\t"
+ "add sp, sp, #16 \n\t"
#ifdef CONFIG_THUMB2_KERNEL
"bx lr \n\t"
#else
This changes x86/kretprobe stack frame on kretprobe_trampoline
a bit, which now push the kretprobe_trampoline as a fake return
address at the bottom of the stack frame. With this fix, the ORC
unwinder will see the kretprobe_trampoline as a return address.
Signed-off-by: Masami Hiramatsu <[email protected]>
Suggested-by: Josh Poimboeuf <[email protected]>
---
arch/x86/kernel/kprobes/core.c | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 9a6763fd066e..4f3567a9974f 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -1041,28 +1041,31 @@ asm(
".global kretprobe_trampoline\n"
".type kretprobe_trampoline, @function\n"
"kretprobe_trampoline:\n"
- /* We don't bother saving the ss register */
#ifdef CONFIG_X86_64
- " pushq %rsp\n"
+ /* Push fake return address to tell the unwinder it's a kretprobe */
+ " pushq $kretprobe_trampoline\n"
UNWIND_HINT_FUNC
+ /* Save the sp-8, this will be fixed later */
+ " pushq %rsp\n"
" pushfq\n"
SAVE_REGS_STRING
" movq %rsp, %rdi\n"
" call trampoline_handler\n"
- /* Replace saved sp with true return address. */
- " movq %rax, 19*8(%rsp)\n"
RESTORE_REGS_STRING
+ " addq $8, %rsp\n"
" popfq\n"
#else
- " pushl %esp\n"
+ /* Push fake return address to tell the unwinder it's a kretprobe */
+ " pushl $kretprobe_trampoline\n"
UNWIND_HINT_FUNC
+ /* Save the sp-4, this will be fixed later */
+ " pushl %esp\n"
" pushfl\n"
SAVE_REGS_STRING
" movl %esp, %eax\n"
" call trampoline_handler\n"
- /* Replace saved sp with true return address. */
- " movl %eax, 15*4(%esp)\n"
RESTORE_REGS_STRING
+ " addl $4, %esp\n"
" popfl\n"
#endif
" ret\n"
@@ -1073,8 +1076,10 @@ NOKPROBE_SYMBOL(kretprobe_trampoline);
/*
* Called from kretprobe_trampoline
*/
-__used __visible void *trampoline_handler(struct pt_regs *regs)
+__used __visible void trampoline_handler(struct pt_regs *regs)
{
+ unsigned long *frame_pointer;
+
/* fixup registers */
regs->cs = __KERNEL_CS;
#ifdef CONFIG_X86_32
@@ -1082,8 +1087,16 @@ __used __visible void *trampoline_handler(struct pt_regs *regs)
#endif
regs->ip = (unsigned long)&kretprobe_trampoline;
regs->orig_ax = ~0UL;
+ regs->sp += sizeof(long);
+ frame_pointer = ((unsigned long *)®s->sp) + 1;
- return (void *)kretprobe_trampoline_handler(regs, ®s->sp);
+ /* Replace fake return address with real one. */
+ *frame_pointer = kretprobe_trampoline_handler(regs, frame_pointer);
+ /*
+ * Move flags to sp so that kretprobe_trapmoline can return
+ * right after popf.
+ */
+ regs->sp = regs->flags;
}
NOKPROBE_SYMBOL(trampoline_handler);
ftrace shows "[unknown/kretprobe'd]" indicator all addresses in the
kretprobe_trampoline, but the modified address by kretprobe should
be only kretprobe_trampoline+0.
Signed-off-by: Masami Hiramatsu <[email protected]>
Acked-by: Steven Rostedt (VMware) <[email protected]>
---
kernel/trace/trace_output.c | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index d0368a569bfa..e46780670742 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ftrace.h>
+#include <linux/kprobes.h>
#include <linux/sched/clock.h>
#include <linux/sched/mm.h>
@@ -346,22 +347,12 @@ int trace_output_call(struct trace_iterator *iter, char *name, char *fmt, ...)
}
EXPORT_SYMBOL_GPL(trace_output_call);
-#ifdef CONFIG_KRETPROBES
-static inline const char *kretprobed(const char *name)
+static inline const char *kretprobed(const char *name, unsigned long addr)
{
- static const char tramp_name[] = "kretprobe_trampoline";
- int size = sizeof(tramp_name);
-
- if (strncmp(tramp_name, name, size) == 0)
+ if (is_kretprobe_trampoline(addr))
return "[unknown/kretprobe'd]";
return name;
}
-#else
-static inline const char *kretprobed(const char *name)
-{
- return name;
-}
-#endif /* CONFIG_KRETPROBES */
void
trace_seq_print_sym(struct trace_seq *s, unsigned long address, bool offset)
@@ -374,7 +365,7 @@ trace_seq_print_sym(struct trace_seq *s, unsigned long address, bool offset)
sprint_symbol(str, address);
else
kallsyms_lookup(address, NULL, NULL, NULL, str);
- name = kretprobed(str);
+ name = kretprobed(str, address);
if (name && strlen(name)) {
trace_seq_puts(s, name);
On Wed, May 26, 2021 at 1:02 AM Masami Hiramatsu <[email protected]> wrote:
>
> Hello,
>
> Here is the 6th version of the series to fix the stacktrace with kretprobe
> on x86.
>
> The previous version is;
>
> https://lore.kernel.org/bpf/161676170650.330141.6214727134265514123.stgit@devnote2/
>
> This version is rebased on the latest tip tree and add some patches for
> improving stacktrace[13/13].
>
> Changes from v5:
> [02/13]:
> - Use dereference_symbol_descriptor() instead of dereference_function_descriptor()
> [04/13]:
> - Replace BUG_ON() with WARN_ON_ONCE() in __kretprobe_trampoline_handler().
> [13/13]:
> - Add a new patch to fix return address in earlier stage.
>
>
> With this series, unwinder can unwind stack correctly from ftrace as below;
>
> # cd /sys/kernel/debug/tracing
> # echo > trace
> # echo 1 > options/sym-offset
> # echo r vfs_read >> kprobe_events
> # echo r full_proxy_read >> kprobe_events
> # echo traceoff:1 > events/kprobes/r_vfs_read_0/trigger
> # echo stacktrace:1 > events/kprobes/r_full_proxy_read_0/trigger
> # echo 1 > events/kprobes/enable
> # cat /sys/kernel/debug/kprobes/list
> ffffffff8133b740 r full_proxy_read+0x0 [FTRACE]
> ffffffff812560b0 r vfs_read+0x0 [FTRACE]
> # echo 0 > events/kprobes/enable
> # cat trace
> # tracer: nop
> #
> # entries-in-buffer/entries-written: 3/3 #P:8
> #
> # _-----=> irqs-off
> # / _----=> need-resched
> # | / _---=> hardirq/softirq
> # || / _--=> preempt-depth
> # ||| / delay
> # TASK-PID CPU# |||| TIMESTAMP FUNCTION
> # | | | |||| | |
> <...>-134 [007] ...1 16.185877: r_full_proxy_read_0: (vfs_read+0x98/0x180 <- full_proxy_read)
> <...>-134 [007] ...1 16.185901: <stack trace>
> => kretprobe_trace_func+0x209/0x300
> => kretprobe_dispatcher+0x4a/0x70
> => __kretprobe_trampoline_handler+0xd4/0x170
> => trampoline_handler+0x43/0x60
> => kretprobe_trampoline+0x2a/0x50
> => vfs_read+0x98/0x180
> => ksys_read+0x5f/0xe0
> => do_syscall_64+0x37/0x90
> => entry_SYSCALL_64_after_hwframe+0x44/0xae
> <...>-134 [007] ...1 16.185902: r_vfs_read_0: (ksys_read+0x5f/0xe0 <- vfs_read)
>
> This shows the double return probes (vfs_read and full_proxy_read) on the stack
> correctly unwinded. (vfs_read will return to ksys_read+0x5f and full_proxy_read
> will return to vfs_read+0x98)
>
> This actually changes the kretprobe behavisor a bit, now the instraction pointer in
> the pt_regs passed to kretprobe user handler is correctly set the real return
> address. So user handlers can get it via instruction_pointer() API.
>
> You can also get this series from
> git://git.kernel.org/pub/scm/linux/kernel/git/mhiramat/linux.git kprobes/kretprobe-stackfix-v6
>
>
> Thank you,
>
> ---
>
Thanks for following up on this! I've applied this patch set on top of
bpf-next and tested with my local BPF-based tool that uses stack
traces in kretprobes heavily. It all works now and I'm getting
meaningful and correctly looking stacktraces. Thanks a lot!
Tested-by: Andrii Nakryik <[email protected]>
> Josh Poimboeuf (1):
> x86/kprobes: Add UNWIND_HINT_FUNC on kretprobe_trampoline code
>
> Masami Hiramatsu (12):
> ia64: kprobes: Fix to pass correct trampoline address to the handler
> kprobes: treewide: Replace arch_deref_entry_point() with dereference_symbol_descriptor()
> kprobes: treewide: Remove trampoline_address from kretprobe_trampoline_handler()
> kprobes: Add kretprobe_find_ret_addr() for searching return address
> ARC: Add instruction_pointer_set() API
> ia64: Add instruction_pointer_set() API
> arm: kprobes: Make a space for regs->ARM_pc at kretprobe_trampoline
> kprobes: Setup instruction pointer in __kretprobe_trampoline_handler
> x86/kprobes: Push a fake return address at kretprobe_trampoline
> x86/unwind: Recover kretprobe trampoline entry
> tracing: Show kretprobe unknown indicator only for kretprobe_trampoline
> x86/kprobes: Fixup return address in generic trampoline handler
>
>
> arch/arc/include/asm/ptrace.h | 5 ++
> arch/arc/kernel/kprobes.c | 2 -
> arch/arm/probes/kprobes/core.c | 5 +-
> arch/arm64/kernel/probes/kprobes.c | 3 -
> arch/csky/kernel/probes/kprobes.c | 2 -
> arch/ia64/include/asm/ptrace.h | 5 ++
> arch/ia64/kernel/kprobes.c | 15 ++---
> arch/mips/kernel/kprobes.c | 3 -
> arch/parisc/kernel/kprobes.c | 4 +
> arch/powerpc/kernel/kprobes.c | 13 ----
> arch/riscv/kernel/probes/kprobes.c | 2 -
> arch/s390/kernel/kprobes.c | 2 -
> arch/sh/kernel/kprobes.c | 2 -
> arch/sparc/kernel/kprobes.c | 2 -
> arch/x86/include/asm/kprobes.h | 1
> arch/x86/include/asm/unwind.h | 23 +++++++
> arch/x86/include/asm/unwind_hints.h | 5 ++
> arch/x86/kernel/kprobes/core.c | 53 +++++++++++++++--
> arch/x86/kernel/unwind_frame.c | 4 +
> arch/x86/kernel/unwind_guess.c | 3 -
> arch/x86/kernel/unwind_orc.c | 19 +++++-
> include/linux/kprobes.h | 41 +++++++++++--
> kernel/kprobes.c | 108 +++++++++++++++++++++++++----------
> kernel/trace/trace_output.c | 17 +-----
> lib/error-inject.c | 3 +
> 25 files changed, 237 insertions(+), 105 deletions(-)
>
> --
> Masami Hiramatsu (Linaro) <[email protected]>
On Wed, 26 May 2021 10:39:57 -0700
Andrii Nakryiko <[email protected]> wrote:
> On Wed, May 26, 2021 at 1:02 AM Masami Hiramatsu <[email protected]> wrote:
> >
> > Hello,
> >
> > Here is the 6th version of the series to fix the stacktrace with kretprobe
> > on x86.
> >
> > The previous version is;
> >
> > https://lore.kernel.org/bpf/161676170650.330141.6214727134265514123.stgit@devnote2/
> >
> > This version is rebased on the latest tip tree and add some patches for
> > improving stacktrace[13/13].
> >
> > Changes from v5:
> > [02/13]:
> > - Use dereference_symbol_descriptor() instead of dereference_function_descriptor()
> > [04/13]:
> > - Replace BUG_ON() with WARN_ON_ONCE() in __kretprobe_trampoline_handler().
> > [13/13]:
> > - Add a new patch to fix return address in earlier stage.
> >
> >
> > With this series, unwinder can unwind stack correctly from ftrace as below;
> >
> > # cd /sys/kernel/debug/tracing
> > # echo > trace
> > # echo 1 > options/sym-offset
> > # echo r vfs_read >> kprobe_events
> > # echo r full_proxy_read >> kprobe_events
> > # echo traceoff:1 > events/kprobes/r_vfs_read_0/trigger
> > # echo stacktrace:1 > events/kprobes/r_full_proxy_read_0/trigger
> > # echo 1 > events/kprobes/enable
> > # cat /sys/kernel/debug/kprobes/list
> > ffffffff8133b740 r full_proxy_read+0x0 [FTRACE]
> > ffffffff812560b0 r vfs_read+0x0 [FTRACE]
> > # echo 0 > events/kprobes/enable
> > # cat trace
> > # tracer: nop
> > #
> > # entries-in-buffer/entries-written: 3/3 #P:8
> > #
> > # _-----=> irqs-off
> > # / _----=> need-resched
> > # | / _---=> hardirq/softirq
> > # || / _--=> preempt-depth
> > # ||| / delay
> > # TASK-PID CPU# |||| TIMESTAMP FUNCTION
> > # | | | |||| | |
> > <...>-134 [007] ...1 16.185877: r_full_proxy_read_0: (vfs_read+0x98/0x180 <- full_proxy_read)
> > <...>-134 [007] ...1 16.185901: <stack trace>
> > => kretprobe_trace_func+0x209/0x300
> > => kretprobe_dispatcher+0x4a/0x70
> > => __kretprobe_trampoline_handler+0xd4/0x170
> > => trampoline_handler+0x43/0x60
> > => kretprobe_trampoline+0x2a/0x50
> > => vfs_read+0x98/0x180
> > => ksys_read+0x5f/0xe0
> > => do_syscall_64+0x37/0x90
> > => entry_SYSCALL_64_after_hwframe+0x44/0xae
> > <...>-134 [007] ...1 16.185902: r_vfs_read_0: (ksys_read+0x5f/0xe0 <- vfs_read)
> >
> > This shows the double return probes (vfs_read and full_proxy_read) on the stack
> > correctly unwinded. (vfs_read will return to ksys_read+0x5f and full_proxy_read
> > will return to vfs_read+0x98)
> >
> > This actually changes the kretprobe behavisor a bit, now the instraction pointer in
> > the pt_regs passed to kretprobe user handler is correctly set the real return
> > address. So user handlers can get it via instruction_pointer() API.
> >
> > You can also get this series from
> > git://git.kernel.org/pub/scm/linux/kernel/git/mhiramat/linux.git kprobes/kretprobe-stackfix-v6
> >
> >
> > Thank you,
> >
> > ---
> >
>
> Thanks for following up on this! I've applied this patch set on top of
> bpf-next and tested with my local BPF-based tool that uses stack
> traces in kretprobes heavily. It all works now and I'm getting
> meaningful and correctly looking stacktraces. Thanks a lot!
>
> Tested-by: Andrii Nakryik <[email protected]>
Thanks for testing! I got a minor warning issue on [13/13] from kernel test
bot, which can be fixed by adding a prototype. So I will update it.
Thank you!
--
Masami Hiramatsu <[email protected]>