Skip to content

Commit badbf39

Browse files
iii-iVasily Gorbik
authored andcommitted
s390/unwind: add a test for the internal API
unwind_for_each_frame can take at least 8 different sets of parameters. Add a test to make sure they all are handled in a sane way. Reviewed-by: Heiko Carstens <[email protected]> Signed-off-by: Ilya Leoshkevich <[email protected]> Co-developed-by: Vasily Gorbik <[email protected]> Signed-off-by: Vasily Gorbik <[email protected]>
1 parent adcfb8c commit badbf39

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

arch/s390/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,3 +1018,17 @@ config S390_GUEST
10181018
the KVM hypervisor.
10191019

10201020
endmenu
1021+
1022+
menu "Selftests"
1023+
1024+
config S390_UNWIND_SELFTEST
1025+
def_tristate n
1026+
prompt "Test unwind functions"
1027+
help
1028+
This option enables s390 specific stack unwinder testing kernel
1029+
module. This option is not useful for distributions or general
1030+
kernels, but only for kernel developers working on architecture code.
1031+
1032+
Say N if you are unsure.
1033+
1034+
endmenu

arch/s390/lib/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ lib-$(CONFIG_UPROBES) += probes.o
1111
# Instrumenting memory accesses to __user data (in different address space)
1212
# produce false positives
1313
KASAN_SANITIZE_uaccess.o := n
14+
15+
obj-$(CONFIG_S390_UNWIND_SELFTEST) += test_unwind.o
16+
CFLAGS_test_unwind.o += -fno-optimize-sibling-calls

arch/s390/lib/test_unwind.c

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Test module for unwind_for_each_frame
4+
*/
5+
6+
#define pr_fmt(fmt) "test_unwind: " fmt
7+
#include <asm/unwind.h>
8+
#include <linux/completion.h>
9+
#include <linux/kallsyms.h>
10+
#include <linux/kthread.h>
11+
#include <linux/module.h>
12+
#include <linux/string.h>
13+
#include <linux/wait.h>
14+
15+
#define BT_BUF_SIZE (PAGE_SIZE * 4)
16+
17+
/*
18+
* To avoid printk line limit split backtrace by lines
19+
*/
20+
static void print_backtrace(char *bt)
21+
{
22+
char *p;
23+
24+
while (true) {
25+
p = strsep(&bt, "\n");
26+
if (!p)
27+
break;
28+
pr_err("%s\n", p);
29+
}
30+
}
31+
32+
/*
33+
* Calls unwind_for_each_frame(task, regs, sp) and verifies that the result
34+
* contains unwindme_func2 followed by unwindme_func1.
35+
*/
36+
static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
37+
unsigned long sp)
38+
{
39+
int frame_count, prev_is_func2, seen_func2_func1;
40+
const int max_frames = 128;
41+
struct unwind_state state;
42+
size_t bt_pos = 0;
43+
int ret = 0;
44+
char *bt;
45+
46+
bt = kmalloc(BT_BUF_SIZE, GFP_KERNEL);
47+
if (!bt) {
48+
pr_err("failed to allocate backtrace buffer\n");
49+
return -ENOMEM;
50+
}
51+
/* Unwind. */
52+
frame_count = 0;
53+
prev_is_func2 = 0;
54+
seen_func2_func1 = 0;
55+
unwind_for_each_frame(&state, task, regs, sp) {
56+
unsigned long addr = unwind_get_return_address(&state);
57+
char sym[KSYM_SYMBOL_LEN];
58+
59+
if (!addr || frame_count == max_frames)
60+
break;
61+
sprint_symbol(sym, addr);
62+
if (bt_pos < BT_BUF_SIZE) {
63+
bt_pos += snprintf(bt + bt_pos, BT_BUF_SIZE - bt_pos, "%s\n", sym);
64+
if (bt_pos >= BT_BUF_SIZE)
65+
pr_err("backtrace buffer is too small\n");
66+
}
67+
frame_count += 1;
68+
if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
69+
seen_func2_func1 = 1;
70+
prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
71+
}
72+
73+
/* Check the results. */
74+
if (!seen_func2_func1) {
75+
pr_err("unwindme_func2 and unwindme_func1 not found\n");
76+
ret = -EINVAL;
77+
}
78+
if (frame_count == max_frames) {
79+
pr_err("Maximum number of frames exceeded\n");
80+
ret = -EINVAL;
81+
}
82+
if (ret)
83+
print_backtrace(bt);
84+
kfree(bt);
85+
return ret;
86+
}
87+
88+
/* State of the task being unwound. */
89+
struct unwindme {
90+
int flags;
91+
struct completion task_ready;
92+
wait_queue_head_t task_wq;
93+
unsigned long sp;
94+
};
95+
96+
/* Values of unwindme.flags. */
97+
#define UWM_DEFAULT 0x0
98+
#define UWM_THREAD 0x1 /* Unwind a separate task. */
99+
#define UWM_REGS 0x2 /* Pass regs to test_unwind(). */
100+
#define UWM_SP 0x4 /* Pass sp to test_unwind(). */
101+
#define UWM_CALLER 0x8 /* Unwind starting from caller. */
102+
103+
static __always_inline unsigned long get_psw_addr(void)
104+
{
105+
unsigned long psw_addr;
106+
107+
asm volatile(
108+
"basr %[psw_addr],0\n"
109+
: [psw_addr] "=d" (psw_addr));
110+
return psw_addr;
111+
}
112+
113+
/* This function may or may not appear in the backtrace. */
114+
static noinline int unwindme_func4(struct unwindme *u)
115+
{
116+
if (!(u->flags & UWM_CALLER))
117+
u->sp = current_frame_address();
118+
if (u->flags & UWM_THREAD) {
119+
complete(&u->task_ready);
120+
wait_event(u->task_wq, kthread_should_park());
121+
kthread_parkme();
122+
return 0;
123+
} else {
124+
struct pt_regs regs;
125+
126+
memset(&regs, 0, sizeof(regs));
127+
regs.psw.addr = get_psw_addr();
128+
regs.gprs[15] = current_stack_pointer();
129+
return test_unwind(NULL,
130+
(u->flags & UWM_REGS) ? &regs : NULL,
131+
(u->flags & UWM_SP) ? u->sp : 0);
132+
}
133+
}
134+
135+
/* This function may or may not appear in the backtrace. */
136+
static noinline int unwindme_func3(struct unwindme *u)
137+
{
138+
u->sp = current_frame_address();
139+
return unwindme_func4(u);
140+
}
141+
142+
/* This function must appear in the backtrace. */
143+
static noinline int unwindme_func2(struct unwindme *u)
144+
{
145+
return unwindme_func3(u);
146+
}
147+
148+
/* This function must follow unwindme_func2 in the backtrace. */
149+
static noinline int unwindme_func1(void *u)
150+
{
151+
return unwindme_func2((struct unwindme *)u);
152+
}
153+
154+
/* Spawns a task and passes it to test_unwind(). */
155+
static int test_unwind_task(struct unwindme *u)
156+
{
157+
struct task_struct *task;
158+
int ret;
159+
160+
/* Initialize thread-related fields. */
161+
init_completion(&u->task_ready);
162+
init_waitqueue_head(&u->task_wq);
163+
164+
/*
165+
* Start the task and wait until it reaches unwindme_func4() and sleeps
166+
* in (task_ready, unwind_done] range.
167+
*/
168+
task = kthread_run(unwindme_func1, u, "%s", __func__);
169+
if (IS_ERR(task)) {
170+
pr_err("kthread_run() failed\n");
171+
return PTR_ERR(task);
172+
}
173+
/*
174+
* Make sure task reaches unwindme_func4 before parking it,
175+
* we might park it before kthread function has been executed otherwise
176+
*/
177+
wait_for_completion(&u->task_ready);
178+
kthread_park(task);
179+
/* Unwind. */
180+
ret = test_unwind(task, NULL, (u->flags & UWM_SP) ? u->sp : 0);
181+
kthread_stop(task);
182+
return ret;
183+
}
184+
185+
static int test_unwind_flags(int flags)
186+
{
187+
struct unwindme u;
188+
189+
u.flags = flags;
190+
if (u.flags & UWM_THREAD)
191+
return test_unwind_task(&u);
192+
else
193+
return unwindme_func1(&u);
194+
}
195+
196+
static int test_unwind_init(void)
197+
{
198+
int ret = 0;
199+
200+
#define TEST(flags) \
201+
do { \
202+
pr_info("[ RUN ] " #flags "\n"); \
203+
if (!test_unwind_flags((flags))) { \
204+
pr_info("[ OK ] " #flags "\n"); \
205+
} else { \
206+
pr_err("[ FAILED ] " #flags "\n"); \
207+
ret = -EINVAL; \
208+
} \
209+
} while (0)
210+
211+
TEST(UWM_DEFAULT);
212+
TEST(UWM_SP);
213+
TEST(UWM_REGS);
214+
TEST(UWM_SP | UWM_REGS);
215+
TEST(UWM_CALLER | UWM_SP);
216+
TEST(UWM_CALLER | UWM_SP | UWM_REGS);
217+
TEST(UWM_THREAD);
218+
TEST(UWM_THREAD | UWM_SP);
219+
TEST(UWM_THREAD | UWM_CALLER | UWM_SP);
220+
#undef TEST
221+
222+
return ret;
223+
}
224+
225+
static void test_unwind_exit(void)
226+
{
227+
}
228+
229+
module_init(test_unwind_init);
230+
module_exit(test_unwind_exit);
231+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)