summaryrefslogtreecommitdiff
path: root/scripts/basic
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2026-10-28 21:23:44 +0100
committerAlexei Starovoitov <ast@kernel.org>2026-10-30 21:21:46 +0000
commit31ac959c915cc7f379d84cf9dadf3664cbcace02 (patch)
treead3730fa9b27ae76b065293fbb26fc8a4a2ad0ce /scripts/basic
parentd89e1d186ec9271bf78e62771779e9fc03bf0fc3 (diff)
downloadlinux-31ac959c915cc7f379d84cf9dadf3664cbcace02.tar.gz
linux-31ac959c915cc7f379d84cf9dadf3664cbcace02.zip
x86/ftrace: Mark jmp mode by clearing bit 63 of trampoline address
Since commit 0c3772a8db1f ("x86/ftrace: Implement DYNAMIC_FTRACE_WITH_JMP") ftrace_call_replace() emits a jmp instead of a call when bit 0 of the target is set, the mark of a direct trampoline that is entered by a jmp. ftrace_update_ftrace_func() and arch_ftrace_update_trampoline() use it too, for the call of the ftrace callback in ftrace_caller, ftrace_regs_caller and the ftrace trampolines, but a callback is a C function and can start at an odd address. It does with gcc13 and CONFIG_CC_OPTIMIZE_FOR_SIZE=y: gcc13 has no -fmin-function-alignment, so CONFIG_CC_HAS_SANE_FUNCTION_ALIGNMENT is not set, and the -falign-functions=16 used in its place does not apply to functions optimized for size. Here ftrace_ops_list_func() starts at: ffffffff810e0147 T ftrace_ops_list_func ftrace_regs_call then becomes a jmp to the byte before it: ftrace_regs_call ffffffff810374f3: e9 4e 8c 0a 00 That byte is the last one of the preceding function's tail jump, and with the first two of ftrace_ops_list_func() it decodes as an inc DWORD PTR [rcx+0x57], which swallows the push %r15 of the prologue. Entered without a return address and one push short, the list function returns through the pt_regs that ftrace_regs_caller saved, here to the saved R14: BUG: unable to handle page fault for address: ffff888116c29f40 #PF: supervisor instruction fetch in kernel mode RIP: 0010:0xffff888116c29f40 R13: 0000000000000000 R14: ffff888116c29f40 R15: 0000000000000000 Call Trace: ? bpf_trampoline_6442499918+0x1/0x149 ? bpf_lsm_file_open+0x5/0x8 ? bpf_lsm_file_open+0x5/0x8 ? security_file_open+0x19/0x46 ? do_dentry_open+0x16c/0x32a The list function is installed whenever two ftrace_ops are registered, and a function that two of them trace calls it. A kprobe next to a BPF trampoline is one such case, and with the single ftrace_ops of the BPF trampolines every update_ftrace_direct_mod() is another, as it registers a stub ops for the sites it modifies. Here, detaching one of several BPF LSM programs from a busy hook crashed the tasks that ran the hook during the update. Bit 0 is free only in the direct trampolines that a dyn_ftrace record can point to, but bit 63 is set in every kernel text address on x86_64. Thus, mark jmp mode by clearing bit 63 instead, so that ftrace_is_jmp() is false for any callback, whatever its alignment, and ftrace_regs_caller sets the bit again where it cleared bit 0 before. A marked address is not canonical, so a return or indirect branch to one that missed ftrace_jmp_get() faults instead of running from a byte before its target. Fixes: 0c3772a8db1f ("x86/ftrace: Implement DYNAMIC_FTRACE_WITH_JMP") Suggested-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Reviewed-by: Jiri Olsa <jolsa@kernel.org> Reviewed-by: Steven Rostedt <rostedt@goodmis.org> Link: https://patch.msgid.link/20261028202345.1215859-1-daniel@iogearbox.net
Diffstat (limited to 'scripts/basic')
0 files changed, 0 insertions, 0 deletions