Skip to content

Commit 18c07d2

Browse files
committed
csky: Fixup calltrace panic
The implementation of show_stack will panic with wrong fp: addr = *fp++; because the fp isn't checked properly. The current implementations of show_stack, wchan and stack_trace haven't been designed properly, so just deprecate them. This patch is a reference to riscv's way, all codes are modified from arm's. The patch is passed with: - cat /proc/<pid>/stack - cat /proc/<pid>/wchan - echo c > /proc/sysrq-trigger Signed-off-by: Guo Ren <[email protected]>
1 parent 229a0dd commit 18c07d2

File tree

8 files changed

+159
-119
lines changed

8 files changed

+159
-119
lines changed

arch/csky/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ config CSKY
88
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
99
select ARCH_USE_BUILTIN_BSWAP
1010
select ARCH_USE_QUEUED_RWLOCKS if NR_CPUS>2
11+
select ARCH_WANT_FRAME_POINTERS if !CPU_CK610
1112
select COMMON_CLK
1213
select CLKSRC_MMIO
1314
select CSKY_MPINTC if CPU_CK860
@@ -38,6 +39,7 @@ config CSKY
3839
select HAVE_ARCH_TRACEHOOK
3940
select HAVE_ARCH_AUDITSYSCALL
4041
select HAVE_COPY_THREAD_TLS
42+
select HAVE_DEBUG_BUGVERBOSE
4143
select HAVE_DYNAMIC_FTRACE
4244
select HAVE_DYNAMIC_FTRACE_WITH_REGS
4345
select HAVE_FUNCTION_TRACER

arch/csky/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ ifeq ($(CSKYABI),abiv2)
4747
KBUILD_CFLAGS += -mno-stack-size
4848
endif
4949

50-
ifdef CONFIG_STACKTRACE
50+
ifdef CONFIG_FRAME_POINTER
5151
KBUILD_CFLAGS += -mbacktrace
5252
endif
5353

arch/csky/include/asm/ptrace.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
5858
return regs->usp;
5959
}
6060

61+
static inline unsigned long frame_pointer(struct pt_regs *regs)
62+
{
63+
return regs->regs[4];
64+
}
65+
static inline void frame_pointer_set(struct pt_regs *regs,
66+
unsigned long val)
67+
{
68+
regs->regs[4] = val;
69+
}
70+
6171
extern int regs_query_register_offset(const char *name);
6272
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
6373
unsigned int n);

arch/csky/include/asm/thread_info.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ struct thread_info {
4040
#define thread_saved_fp(tsk) \
4141
((unsigned long)(((struct switch_stack *)(tsk->thread.ksp))->r8))
4242

43+
#define thread_saved_sp(tsk) \
44+
((unsigned long)(tsk->thread.ksp))
45+
46+
#define thread_saved_lr(tsk) \
47+
((unsigned long)(((struct switch_stack *)(tsk->thread.ksp))->r15))
48+
4349
static inline struct thread_info *current_thread_info(void)
4450
{
4551
unsigned long sp;

arch/csky/kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ extra-y := head.o vmlinux.lds
33

44
obj-y += entry.o atomic.o signal.o traps.o irq.o time.o vdso.o
55
obj-y += power.o syscall.o syscall_table.o setup.o
6-
obj-y += process.o cpu-probe.o ptrace.o dumpstack.o
6+
obj-y += process.o cpu-probe.o ptrace.o stacktrace.o
77
obj-y += probes/
88

99
obj-$(CONFIG_MODULES) += module.o

arch/csky/kernel/dumpstack.c

Lines changed: 0 additions & 49 deletions
This file was deleted.

arch/csky/kernel/process.c

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -98,37 +98,6 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *pr_regs)
9898
return 1;
9999
}
100100

101-
unsigned long get_wchan(struct task_struct *p)
102-
{
103-
unsigned long lr;
104-
unsigned long *fp, *stack_start, *stack_end;
105-
int count = 0;
106-
107-
if (!p || p == current || p->state == TASK_RUNNING)
108-
return 0;
109-
110-
stack_start = (unsigned long *)end_of_stack(p);
111-
stack_end = (unsigned long *)(task_stack_page(p) + THREAD_SIZE);
112-
113-
fp = (unsigned long *) thread_saved_fp(p);
114-
do {
115-
if (fp < stack_start || fp > stack_end)
116-
return 0;
117-
#ifdef CONFIG_STACKTRACE
118-
lr = fp[1];
119-
fp = (unsigned long *)fp[0];
120-
#else
121-
lr = *fp++;
122-
#endif
123-
if (!in_sched_functions(lr) &&
124-
__kernel_text_address(lr))
125-
return lr;
126-
} while (count++ < 16);
127-
128-
return 0;
129-
}
130-
EXPORT_SYMBOL(get_wchan);
131-
132101
#ifndef CONFIG_CPU_PM_NONE
133102
void arch_cpu_idle(void)
134103
{

arch/csky/kernel/stacktrace.c

Lines changed: 139 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,159 @@
11
// SPDX-License-Identifier: GPL-2.0
2-
/* Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. */
32

43
#include <linux/sched/debug.h>
54
#include <linux/sched/task_stack.h>
65
#include <linux/stacktrace.h>
76
#include <linux/ftrace.h>
7+
#include <linux/ptrace.h>
88

9-
void save_stack_trace(struct stack_trace *trace)
9+
#ifdef CONFIG_FRAME_POINTER
10+
11+
struct stackframe {
12+
unsigned long fp;
13+
unsigned long ra;
14+
};
15+
16+
void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
17+
bool (*fn)(unsigned long, void *), void *arg)
1018
{
11-
save_stack_trace_tsk(current, trace);
19+
unsigned long fp, sp, pc;
20+
21+
if (regs) {
22+
fp = frame_pointer(regs);
23+
sp = user_stack_pointer(regs);
24+
pc = instruction_pointer(regs);
25+
} else if (task == NULL || task == current) {
26+
const register unsigned long current_sp __asm__ ("sp");
27+
const register unsigned long current_fp __asm__ ("r8");
28+
fp = current_fp;
29+
sp = current_sp;
30+
pc = (unsigned long)walk_stackframe;
31+
} else {
32+
/* task blocked in __switch_to */
33+
fp = thread_saved_fp(task);
34+
sp = thread_saved_sp(task);
35+
pc = thread_saved_lr(task);
36+
}
37+
38+
for (;;) {
39+
unsigned long low, high;
40+
struct stackframe *frame;
41+
42+
if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
43+
break;
44+
45+
/* Validate frame pointer */
46+
low = sp;
47+
high = ALIGN(sp, THREAD_SIZE);
48+
if (unlikely(fp < low || fp > high || fp & 0x3))
49+
break;
50+
/* Unwind stack frame */
51+
frame = (struct stackframe *)fp;
52+
sp = fp;
53+
fp = frame->fp;
54+
pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
55+
(unsigned long *)(fp - 8));
56+
}
1257
}
13-
EXPORT_SYMBOL_GPL(save_stack_trace);
1458

15-
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
59+
#else /* !CONFIG_FRAME_POINTER */
60+
61+
static void notrace walk_stackframe(struct task_struct *task,
62+
struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
1663
{
17-
unsigned long *fp, *stack_start, *stack_end;
18-
unsigned long addr;
19-
int skip = trace->skip;
20-
int savesched;
21-
int graph_idx = 0;
64+
unsigned long sp, pc;
65+
unsigned long *ksp;
2266

23-
if (tsk == current) {
24-
asm volatile("mov %0, r8\n":"=r"(fp));
25-
savesched = 1;
67+
if (regs) {
68+
sp = user_stack_pointer(regs);
69+
pc = instruction_pointer(regs);
70+
} else if (task == NULL || task == current) {
71+
const register unsigned long current_sp __asm__ ("sp");
72+
sp = current_sp;
73+
pc = (unsigned long)walk_stackframe;
2674
} else {
27-
fp = (unsigned long *)thread_saved_fp(tsk);
28-
savesched = 0;
75+
/* task blocked in __switch_to */
76+
sp = thread_saved_sp(task);
77+
pc = thread_saved_lr(task);
2978
}
3079

31-
addr = (unsigned long) fp & THREAD_MASK;
32-
stack_start = (unsigned long *) addr;
33-
stack_end = (unsigned long *) (addr + THREAD_SIZE);
34-
35-
while (fp > stack_start && fp < stack_end) {
36-
unsigned long lpp, fpp;
80+
if (unlikely(sp & 0x3))
81+
return;
3782

38-
fpp = fp[0];
39-
lpp = fp[1];
40-
if (!__kernel_text_address(lpp))
83+
ksp = (unsigned long *)sp;
84+
while (!kstack_end(ksp)) {
85+
if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
4186
break;
42-
else
43-
lpp = ftrace_graph_ret_addr(tsk, &graph_idx, lpp, NULL);
44-
45-
if (savesched || !in_sched_functions(lpp)) {
46-
if (skip) {
47-
skip--;
48-
} else {
49-
trace->entries[trace->nr_entries++] = lpp;
50-
if (trace->nr_entries >= trace->max_entries)
51-
break;
52-
}
53-
}
54-
fp = (unsigned long *)fpp;
87+
pc = (*ksp++) - 0x4;
5588
}
5689
}
90+
#endif /* CONFIG_FRAME_POINTER */
91+
92+
static bool print_trace_address(unsigned long pc, void *arg)
93+
{
94+
print_ip_sym(pc);
95+
return false;
96+
}
97+
98+
void show_stack(struct task_struct *task, unsigned long *sp)
99+
{
100+
pr_cont("Call Trace:\n");
101+
walk_stackframe(task, NULL, print_trace_address, NULL);
102+
}
103+
104+
static bool save_wchan(unsigned long pc, void *arg)
105+
{
106+
if (!in_sched_functions(pc)) {
107+
unsigned long *p = arg;
108+
*p = pc;
109+
return true;
110+
}
111+
return false;
112+
}
113+
114+
unsigned long get_wchan(struct task_struct *task)
115+
{
116+
unsigned long pc = 0;
117+
118+
if (likely(task && task != current && task->state != TASK_RUNNING))
119+
walk_stackframe(task, NULL, save_wchan, &pc);
120+
return pc;
121+
}
122+
123+
#ifdef CONFIG_STACKTRACE
124+
static bool __save_trace(unsigned long pc, void *arg, bool nosched)
125+
{
126+
struct stack_trace *trace = arg;
127+
128+
if (unlikely(nosched && in_sched_functions(pc)))
129+
return false;
130+
if (unlikely(trace->skip > 0)) {
131+
trace->skip--;
132+
return false;
133+
}
134+
135+
trace->entries[trace->nr_entries++] = pc;
136+
return (trace->nr_entries >= trace->max_entries);
137+
}
138+
139+
static bool save_trace(unsigned long pc, void *arg)
140+
{
141+
return __save_trace(pc, arg, false);
142+
}
143+
144+
/*
145+
* Save stack-backtrace addresses into a stack_trace buffer.
146+
*/
147+
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
148+
{
149+
walk_stackframe(tsk, NULL, save_trace, trace);
150+
}
57151
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
152+
153+
void save_stack_trace(struct stack_trace *trace)
154+
{
155+
save_stack_trace_tsk(NULL, trace);
156+
}
157+
EXPORT_SYMBOL_GPL(save_stack_trace);
158+
159+
#endif /* CONFIG_STACKTRACE */

0 commit comments

Comments
 (0)