mirror-linux/arch/arm64/kernel
Mark Rutland c6d3cd32fd arm64: ftrace: use HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
When CONFIG_FUNCTION_GRAPH_TRACER is selected and the function graph
tracer is in use, unwind_frame() may erroneously associate a traced
function with an incorrect return address. This can happen when starting
an unwind from a pt_regs, or when unwinding across an exception
boundary.

This can be seen when recording with perf while the function graph
tracer is in use. For example:

| # echo function_graph > /sys/kernel/debug/tracing/current_tracer
| # perf record -g -e raw_syscalls:sys_enter:k /bin/true
| # perf report

... reports the callchain erroneously as:

| el0t_64_sync
| el0t_64_sync_handler
| el0_svc_common.constprop.0
| perf_callchain
| get_perf_callchain
| syscall_trace_enter
| syscall_trace_enter

... whereas when the function graph tracer is not in use, it reports:

| el0t_64_sync
| el0t_64_sync_handler
| el0_svc
| do_el0_svc
| el0_svc_common.constprop.0
| syscall_trace_enter
| syscall_trace_enter

The underlying problem is that ftrace_graph_get_ret_stack() takes an
index offset from the most recent entry added to the fgraph return
stack. We start an unwind at offset 0, and increment the offset each
time we encounter a rewritten return address (i.e. when we see
`return_to_handler`). This is broken in two cases:

1) Between creating a pt_regs and starting the unwind, function calls
   may place entries on the stack, leaving an arbitrary offset which we
   can only determine by performing a full unwind from the caller of the
   unwind code (and relying on none of the unwind code being
   instrumented).

   This can result in erroneous entries being reported in a backtrace
   recorded by perf or kfence when the function graph tracer is in use.
   Currently show_regs() is unaffected as dump_backtrace() performs an
   initial unwind.

2) When unwinding across an exception boundary (whether continuing an
   unwind or starting a new unwind from regs), we currently always skip
   the LR of the interrupted context. Where this was live and contained
   a rewritten address, we won't consume the corresponding fgraph ret
   stack entry, leaving subsequent entries off-by-one.

   This can result in erroneous entries being reported in a backtrace
   performed by any in-kernel unwinder when that backtrace crosses an
   exception boundary, with entries after the boundary being reported
   incorrectly. This includes perf, kfence, show_regs(), panic(), etc.

To fix this, we need to be able to uniquely identify each rewritten
return address such that we can map this back to the original return
address. We can use HAVE_FUNCTION_GRAPH_RET_ADDR_PTR to associate
each rewritten return address with a unique location on the stack. As
the return address is passed in the LR (and so is not guaranteed a
unique location in memory), we use the FP upon entry to the function
(i.e. the address of the caller's frame record) as the return address
pointer. Any nested call will have a different FP value as the caller
must create its own frame record and update FP to point to this.

Since ftrace_graph_ret_addr() requires the return address with the PAC
stripped, the stripping of the PAC is moved before the fixup of the
rewritten address. As we would unconditionally strip the PAC, moving
this earlier is not harmful, and we can avoid a redundant strip in the
return address fixup code.

I've tested this with the perf case above, the ftrace selftests, and
a number of ad-hoc unwinder tests. The tests all pass, and I have seen
no unexpected behaviour as a result of this change. I've tested with
pointer authentication under QEMU TCG where magic-sysrq+l correctly
recovers the original return addresses.

Note that this doesn't fix the issue of skipping a live LR at an
exception boundary, which is a more general problem and requires more
substantial rework. Were we to consume the LR in all cases this would
result in warnings where the interrupted context's LR contains
`return_to_handler`, but the FP has been altered, e.g.

| func:
|	<--- ftrace entry ---> 	// logs FP & LR, rewrites LR
| 	STP	FP, LR, [SP, #-16]!
| 	MOV	FP, SP
| 	<--- INTERRUPT --->

... as ftrace_graph_get_ret_stack() fill not find a matching entry,
triggering the WARN_ON_ONCE() in unwind_frame().

Link: https://lore.kernel.org/r/20211025164925.GB2001@C02TD0UTHF1T.local
Link: https://lore.kernel.org/r/20211027132529.30027-1-mark.rutland@arm.com
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20211029162245.39761-1-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-11-16 09:47:54 +00:00
..
probes arm64: kprobes: Make a frame pointer on __kretprobe_trampoline 2021-10-22 12:16:53 -04:00
vdso arm64: vdso: remove -nostdlib compiler flag 2021-11-08 10:02:57 +00:00
vdso32 arm64: vdso: remove -nostdlib compiler flag 2021-11-08 10:02:57 +00:00
.gitignore
Makefile kbuild: use more subdir- for visiting subdirectories while cleaning 2021-10-24 13:49:46 +09:00
acpi.c Revert "ACPI: Add memory semantics to acpi_os_map_memory()" 2021-09-23 20:39:36 +02:00
acpi_numa.c
acpi_parking_protocol.c arm64: use function_nocfi with __pa_symbol 2021-04-08 16:04:22 -07:00
alternative.c arm64: Rename arm64-internal cache maintenance functions 2021-05-25 19:27:49 +01:00
armv8_deprecated.c arm64: extable: add a dedicated uaccess handler 2021-10-21 10:45:22 +01:00
asm-offsets.c cpu-to-thread_info update for v5.16-rc1 2021-11-01 17:00:05 -07:00
cacheinfo.c drivers: base: cacheinfo: Get rid of DEFINE_SMP_CALL_CACHE_FUNCTION() 2021-09-01 10:29:10 +02:00
cpu-reset.S arm64: kexec: remove cpu-reset.h 2021-10-01 13:31:00 +01:00
cpu_errata.c arm64: errata: Add detection for TRBE write to out-of-range 2021-10-21 17:49:17 +01:00
cpu_ops.c
cpufeature.c Char/Misc fix for 5.16-rc1 2021-11-11 09:44:29 -08:00
cpuidle.c arm64: kernel: Update the stale comment 2021-05-06 12:26:26 +01:00
cpuinfo.c arm64: Add HWCAP for self-synchronising virtual counter 2021-10-19 10:56:20 +01:00
crash_core.c
crash_dump.c
debug-monitors.c
efi-entry.S arm64: Rename arm64-internal cache maintenance functions 2021-05-25 19:27:49 +01:00
efi-header.S
efi-rt-wrapper.S
efi.c arm64: efi: Export screen_info 2021-08-04 16:54:36 +00:00
entry-common.c irq: arm64: perform irqentry in entry code 2021-10-26 10:12:53 +01:00
entry-fpsimd.S arm64/sve: Explicitly load vector length when restoring SVE state 2021-10-21 10:18:17 +01:00
entry-ftrace.S
entry.S arm64: kasan: mte: move GCR_EL1 switch to task switch when KASAN disabled 2021-09-29 17:18:57 +01:00
fpsimd.c arm64/sve: Fix warnings when SVE is disabled 2021-10-26 10:15:46 +01:00
ftrace.c arm64: ftrace: use HAVE_FUNCTION_GRAPH_RET_ADDR_PTR 2021-11-16 09:47:54 +00:00
head.S sched: move CPU field back into thread_info if THREAD_INFO_IN_TASK=y 2021-09-30 16:13:10 +02:00
hibernate-asm.S arm64: kexec: install a copy of the linear-map 2021-10-01 13:31:00 +01:00
hibernate.c arm64: hibernate: abstract ttrb0 setup function 2021-10-01 13:30:59 +01:00
hw_breakpoint.c
hyp-stub.S Merge remote-tracking branch 'arm64/for-next/vhe-only' into kvmarm-master/next 2021-04-13 15:42:40 +01:00
idle.c Merge branch 'for-next/entry' into for-next/core 2021-06-24 14:01:55 +01:00
idreg-override.c arm64/cpufeature: Optionally disable MTE via command-line 2021-08-03 15:48:01 +01:00
image-vars.h arm64: Rename arm64-internal cache maintenance functions 2021-05-25 19:27:49 +01:00
image.h
io.c
irq.c
jump_label.c arm64: insn: decouple patching from insn code 2021-06-11 11:19:27 +01:00
kaslr.c arm64: fix the doc of RANDOMIZE_MODULE_REGION_FULL 2021-08-03 10:36:42 +01:00
kexec_image.c
kgdb.c arm64: insn: decouple patching from insn code 2021-06-11 11:19:27 +01:00
kuser32.S
machine_kexec.c arm64: kexec: remove cpu-reset.h 2021-10-01 13:31:00 +01:00
machine_kexec_file.c
module-plts.c
module.c
mte.c arm64: mte: Add asymmetric mode support 2021-10-07 09:30:24 +01:00
paravirt.c
patching.c Merge branch 'for-next/insn' into for-next/core 2021-06-24 14:03:24 +01:00
pci.c arm64: PCI: Support root bridge preparation for Hyper-V 2021-08-23 10:59:26 +01:00
perf_callchain.c arm64: stacktrace: Relax frame record alignment requirement to 8 bytes 2021-05-26 20:01:17 +01:00
perf_event.c arm64/perf: Replace '0xf' instances with ID_AA64DFR0_PMUVER_IMP_DEF 2021-08-11 11:02:12 +01:00
perf_regs.c
pointer_auth.c arm64: move preemption disablement to prctl handlers 2021-07-28 18:33:49 +01:00
process.c sched: Add wrapper for get_wchan() to keep task blocked 2021-10-15 11:25:14 +02:00
proton-pack.c
psci.c arm64: use function_nocfi with __pa_symbol 2021-04-08 16:04:22 -07:00
ptrace.c arm64/sve: Put system wide vector length information into structs 2021-10-21 10:18:17 +01:00
reloc_test_core.c
reloc_test_syms.S
relocate_kernel.S arm64: kexec: keep MMU enabled during kexec relocation 2021-10-01 13:31:00 +01:00
return_address.c
sdei.c arm64: kernel: add helper for booted at EL2 and not VHE 2021-10-01 13:30:59 +01:00
setup.c arm64: convert to setup_initial_init_mm() 2021-07-08 11:48:21 -07:00
signal.c arm64/sve: Put system wide vector length information into structs 2021-10-21 10:18:17 +01:00
signal32.c arm64 updates for 5.15: 2021-09-01 15:04:29 -07:00
sigreturn32.S
sleep.S kasan: remove redundant config option 2021-04-16 16:10:36 -07:00
smccc-call.S arm64: smccc: Save lr before calling __arm_smccc_sve_check() 2021-07-21 11:23:25 +01:00
smp.c Devicetree updates for v5.16: 2021-11-02 22:22:13 -07:00
smp_spin_table.c arm64: Rename arm64-internal cache maintenance functions 2021-05-25 19:27:49 +01:00
stacktrace.c arm64: ftrace: use HAVE_FUNCTION_GRAPH_RET_ADDR_PTR 2021-11-16 09:47:54 +00:00
suspend.c arm64: kasan: mte: use a constant kernel GCR_EL1 value 2021-08-02 18:14:21 +01:00
sys.c
sys32.c
sys_compat.c arm64: Rename arm64-internal cache maintenance functions 2021-05-25 19:27:49 +01:00
syscall.c arm64: fix compat syscall return truncation 2021-08-03 10:35:03 +01:00
time.c
topology.c topology: Represent clusters of CPUs within a die 2021-10-15 11:25:15 +02:00
trace-events-emulation.h
traps.c Merge branch 'for-next/misc' into for-next/core 2021-10-29 12:24:59 +01:00
vdso-wrap.S
vdso.c Merge branches 'for-next/misc', 'for-next/kselftest', 'for-next/xntable', 'for-next/vdso', 'for-next/fiq', 'for-next/epan', 'for-next/kasan-vmalloc', 'for-next/fgt-boot-init', 'for-next/vhe-only' and 'for-next/neon-softirqs-disabled', remote-tracking branch 'arm64/for-next/perf' into for-next/core 2021-04-15 14:00:38 +01:00
vdso32-wrap.S
vmlinux.lds.S Merge branch 'for-next/kexec' into for-next/core 2021-10-29 12:24:47 +01:00