Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
1614b2b
Documentation
LICENSES
arch
alpha
arc
arm
arm64
boot
configs
crypto
hyperv
include
kernel
probes
vdso
vdso32
.gitignore
Makefile
acpi.c
acpi_numa.c
acpi_parking_protocol.c
alternative.c
armv8_deprecated.c
asm-offsets.c
cacheinfo.c
cpu-reset.S
cpu_errata.c
cpu_ops.c
cpufeature.c
cpuidle.c
cpuinfo.c
crash_core.c
crash_dump.c
debug-monitors.c
efi-entry.S
efi-header.S
efi-rt-wrapper.S
efi.c
entry-common.c
entry-fpsimd.S
entry-ftrace.S
entry.S
fpsimd.c
ftrace.c
head.S
hibernate-asm.S
hibernate.c
hw_breakpoint.c
hyp-stub.S
idle.c
idreg-override.c
image-vars.h
image.h
io.c
irq.c
jump_label.c
kaslr.c
kexec_image.c
kgdb.c
kuser32.S
machine_kexec.c
machine_kexec_file.c
module-plts.c
module.c
mte.c
paravirt.c
patching.c
pci.c
perf_callchain.c
perf_event.c
perf_regs.c
pointer_auth.c
process.c
proton-pack.c
psci.c
ptrace.c
reloc_test_core.c
reloc_test_syms.S
relocate_kernel.S
return_address.c
sdei.c
setup.c
signal.c
signal32.c
sigreturn32.S
sleep.S
smccc-call.S
smp.c
smp_spin_table.c
stacktrace.c
suspend.c
sys.c
sys32.c
sys_compat.c
syscall.c
time.c
topology.c
trace-events-emulation.h
traps.c
vdso-wrap.S
vdso.c
vdso32-wrap.S
vmlinux.lds.S
kvm
lib
mm
net
tools
xen
Kbuild
Kconfig
Kconfig.debug
Kconfig.platforms
Makefile
csky
h8300
hexagon
ia64
m68k
microblaze
mips
nds32
nios2
openrisc
parisc
powerpc
riscv
s390
sh
sparc
um
x86
xtensa
.gitignore
Kconfig
block
certs
crypto
drivers
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
arch
/
arm64
/
kernel
/
stacktrace.c
Copy path
Blame
Blame
Latest commit
History
History
241 lines (210 loc) · 5.92 KB
Breadcrumbs
linux
/
arch
/
arm64
/
kernel
/
stacktrace.c
Top
File metadata and controls
Code
Blame
241 lines (210 loc) · 5.92 KB
Raw
// SPDX-License-Identifier: GPL-2.0-only /* * Stack tracing support * * Copyright (C) 2012 ARM Ltd. */ #include <linux/kernel.h> #include <linux/export.h> #include <linux/ftrace.h> #include <linux/kprobes.h> #include <linux/sched.h> #include <linux/sched/debug.h> #include <linux/sched/task_stack.h> #include <linux/stacktrace.h> #include <asm/irq.h> #include <asm/pointer_auth.h> #include <asm/stack_pointer.h> #include <asm/stacktrace.h> /* * AArch64 PCS assigns the frame pointer to x29. * * A simple function prologue looks like this: * sub sp, sp, #0x10 * stp x29, x30, [sp] * mov x29, sp * * A simple function epilogue looks like this: * mov sp, x29 * ldp x29, x30, [sp] * add sp, sp, #0x10 */ void start_backtrace(struct stackframe *frame, unsigned long fp, unsigned long pc) { frame->fp = fp; frame->pc = pc; #ifdef CONFIG_KRETPROBES frame->kr_cur = NULL; #endif /* * Prime the first unwind. * * In unwind_frame() we'll check that the FP points to a valid stack, * which can't be STACK_TYPE_UNKNOWN, and the first unwind will be * treated as a transition to whichever stack that happens to be. The * prev_fp value won't be used, but we set it to 0 such that it is * definitely not an accessible stack address. */ bitmap_zero(frame->stacks_done, __NR_STACK_TYPES); frame->prev_fp = 0; frame->prev_type = STACK_TYPE_UNKNOWN; } /* * Unwind from one frame record (A) to the next frame record (B). * * We terminate early if the location of B indicates a malformed chain of frame * records (e.g. a cycle), determined based on the location and fp value of A * and the location (but not the fp value) of B. */ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame) { unsigned long fp = frame->fp; struct stack_info info; if (!tsk) tsk = current; /* Final frame; nothing to unwind */ if (fp == (unsigned long)task_pt_regs(tsk)->stackframe) return -ENOENT; if (fp & 0x7) return -EINVAL; if (!on_accessible_stack(tsk, fp, 16, &info)) return -EINVAL; if (test_bit(info.type, frame->stacks_done)) return -EINVAL; /* * As stacks grow downward, any valid record on the same stack must be * at a strictly higher address than the prior record. * * Stacks can nest in several valid orders, e.g. * * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW * * ... but the nesting itself is strict. Once we transition from one * stack to another, it's never valid to unwind back to that first * stack. */ if (info.type == frame->prev_type) { if (fp <= frame->prev_fp) return -EINVAL; } else { set_bit(frame->prev_type, frame->stacks_done); } /* * Record this frame record's values and location. The prev_fp and * prev_type are only meaningful to the next unwind_frame() invocation. */ frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp)); frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 8)); frame->prev_fp = fp; frame->prev_type = info.type; frame->pc = ptrauth_strip_insn_pac(frame->pc); #ifdef CONFIG_FUNCTION_GRAPH_TRACER if (tsk->ret_stack && (frame->pc == (unsigned long)return_to_handler)) { unsigned long orig_pc; /* * This is a case where function graph tracer has * modified a return address (LR) in a stack frame * to hook a function return. * So replace it to an original value. */ orig_pc = ftrace_graph_ret_addr(tsk, NULL, frame->pc, (void *)frame->fp); if (WARN_ON_ONCE(frame->pc == orig_pc)) return -EINVAL; frame->pc = orig_pc; } #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #ifdef CONFIG_KRETPROBES if (is_kretprobe_trampoline(frame->pc)) frame->pc = kretprobe_find_ret_addr(tsk, (void *)frame->fp, &frame->kr_cur); #endif return 0; } NOKPROBE_SYMBOL(unwind_frame); void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame, bool (*fn)(void *, unsigned long), void *data) { while (1) { int ret; if (!fn(data, frame->pc)) break; ret = unwind_frame(tsk, frame); if (ret < 0) break; } } NOKPROBE_SYMBOL(walk_stackframe); static void dump_backtrace_entry(unsigned long where, const char *loglvl) { printk("%s %pSb\n", loglvl, (void *)where); } void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, const char *loglvl) { struct stackframe frame; int skip = 0; pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); if (regs) { if (user_mode(regs)) return; skip = 1; } if (!tsk) tsk = current; if (!try_get_task_stack(tsk)) return; if (tsk == current) { start_backtrace(&frame, (unsigned long)__builtin_frame_address(0), (unsigned long)dump_backtrace); } else { /* * task blocked in __switch_to */ start_backtrace(&frame, thread_saved_fp(tsk), thread_saved_pc(tsk)); } printk("%sCall trace:\n", loglvl); do { /* skip until specified stack frame */ if (!skip) { dump_backtrace_entry(frame.pc, loglvl); } else if (frame.fp == regs->regs[29]) { skip = 0; /* * Mostly, this is the case where this function is * called in panic/abort. As exception handler's * stack frame does not contain the corresponding pc * at which an exception has taken place, use regs->pc * instead. */ dump_backtrace_entry(regs->pc, loglvl); } } while (!unwind_frame(tsk, &frame)); put_task_stack(tsk); } void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) { dump_backtrace(NULL, tsk, loglvl); barrier(); } noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, struct task_struct *task, struct pt_regs *regs) { struct stackframe frame; if (regs) start_backtrace(&frame, regs->regs[29], regs->pc); else if (task == current) start_backtrace(&frame, (unsigned long)__builtin_frame_address(1), (unsigned long)__builtin_return_address(0)); else start_backtrace(&frame, thread_saved_fp(task), thread_saved_pc(task)); walk_stackframe(task, &frame, consume_entry, cookie); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
You can’t perform that action at this time.