1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * test_fprobe.c - simple sanity test for fprobe
4 */
5
6#include <linux/kernel.h>
7#include <linux/fprobe.h>
8#include <linux/random.h>
9#include <kunit/test.h>
10
11#define div_factor 3
12
13static struct kunit *current_test;
14
15static u32 rand1, entry_val, exit_val;
16
17/* Use indirect calls to avoid inlining the target functions */
18static u32 (*target)(u32 value);
19static u32 (*target2)(u32 value);
20static u32 (*target_nest)(u32 value, u32 (*nest)(u32));
21static unsigned long target_ip;
22static unsigned long target2_ip;
23static unsigned long target_nest_ip;
24static int entry_return_value;
25
26static noinline u32 fprobe_selftest_target(u32 value)
27{
28	return (value / div_factor);
29}
30
31static noinline u32 fprobe_selftest_target2(u32 value)
32{
33	return (value / div_factor) + 1;
34}
35
36static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
37{
38	return nest(value + 2);
39}
40
41static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
42				    unsigned long ret_ip,
43				    struct pt_regs *regs, void *data)
44{
45	KUNIT_EXPECT_FALSE(current_test, preemptible());
46	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
47	if (ip != target_ip)
48		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
49	entry_val = (rand1 / div_factor);
50	if (fp->entry_data_size) {
51		KUNIT_EXPECT_NOT_NULL(current_test, data);
52		if (data)
53			*(u32 *)data = entry_val;
54	} else
55		KUNIT_EXPECT_NULL(current_test, data);
56
57	return entry_return_value;
58}
59
60static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
61				    unsigned long ret_ip,
62				    struct pt_regs *regs, void *data)
63{
64	unsigned long ret = regs_return_value(regs);
65
66	KUNIT_EXPECT_FALSE(current_test, preemptible());
67	if (ip != target_ip) {
68		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
69		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1);
70	} else
71		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor));
72	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));
73	exit_val = entry_val + div_factor;
74	if (fp->entry_data_size) {
75		KUNIT_EXPECT_NOT_NULL(current_test, data);
76		if (data)
77			KUNIT_EXPECT_EQ(current_test, *(u32 *)data, entry_val);
78	} else
79		KUNIT_EXPECT_NULL(current_test, data);
80}
81
82static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
83				      unsigned long ret_ip,
84				      struct pt_regs *regs, void *data)
85{
86	KUNIT_EXPECT_FALSE(current_test, preemptible());
87	return 0;
88}
89
90static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
91				      unsigned long ret_ip,
92				      struct pt_regs *regs, void *data)
93{
94	KUNIT_EXPECT_FALSE(current_test, preemptible());
95	KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
96}
97
98/* Test entry only (no rethook) */
99static void test_fprobe_entry(struct kunit *test)
100{
101	struct fprobe fp_entry = {
102		.entry_handler = fp_entry_handler,
103	};
104
105	current_test = test;
106
107	/* Before register, unregister should be failed. */
108	KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry));
109	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL));
110
111	entry_val = 0;
112	exit_val = 0;
113	target(rand1);
114	KUNIT_EXPECT_NE(test, 0, entry_val);
115	KUNIT_EXPECT_EQ(test, 0, exit_val);
116
117	entry_val = 0;
118	exit_val = 0;
119	target2(rand1);
120	KUNIT_EXPECT_NE(test, 0, entry_val);
121	KUNIT_EXPECT_EQ(test, 0, exit_val);
122
123	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry));
124}
125
126static void test_fprobe(struct kunit *test)
127{
128	struct fprobe fp = {
129		.entry_handler = fp_entry_handler,
130		.exit_handler = fp_exit_handler,
131	};
132
133	current_test = test;
134	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL));
135
136	entry_val = 0;
137	exit_val = 0;
138	target(rand1);
139	KUNIT_EXPECT_NE(test, 0, entry_val);
140	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
141
142	entry_val = 0;
143	exit_val = 0;
144	target2(rand1);
145	KUNIT_EXPECT_NE(test, 0, entry_val);
146	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
147
148	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
149}
150
151static void test_fprobe_syms(struct kunit *test)
152{
153	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"};
154	struct fprobe fp = {
155		.entry_handler = fp_entry_handler,
156		.exit_handler = fp_exit_handler,
157	};
158
159	current_test = test;
160	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
161
162	entry_val = 0;
163	exit_val = 0;
164	target(rand1);
165	KUNIT_EXPECT_NE(test, 0, entry_val);
166	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
167
168	entry_val = 0;
169	exit_val = 0;
170	target2(rand1);
171	KUNIT_EXPECT_NE(test, 0, entry_val);
172	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
173
174	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
175}
176
177/* Test private entry_data */
178static void test_fprobe_data(struct kunit *test)
179{
180	struct fprobe fp = {
181		.entry_handler = fp_entry_handler,
182		.exit_handler = fp_exit_handler,
183		.entry_data_size = sizeof(u32),
184	};
185
186	current_test = test;
187	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
188
189	target(rand1);
190
191	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
192}
193
194/* Test nr_maxactive */
195static void test_fprobe_nest(struct kunit *test)
196{
197	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"};
198	struct fprobe fp = {
199		.entry_handler = nest_entry_handler,
200		.exit_handler = nest_exit_handler,
201		.nr_maxactive = 1,
202	};
203
204	current_test = test;
205	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
206
207	target_nest(rand1, target);
208	KUNIT_EXPECT_EQ(test, 1, fp.nmissed);
209
210	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
211}
212
213static void test_fprobe_skip(struct kunit *test)
214{
215	struct fprobe fp = {
216		.entry_handler = fp_entry_handler,
217		.exit_handler = fp_exit_handler,
218	};
219
220	current_test = test;
221	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
222
223	entry_return_value = 1;
224	entry_val = 0;
225	exit_val = 0;
226	target(rand1);
227	KUNIT_EXPECT_NE(test, 0, entry_val);
228	KUNIT_EXPECT_EQ(test, 0, exit_val);
229	KUNIT_EXPECT_EQ(test, 0, fp.nmissed);
230	entry_return_value = 0;
231
232	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
233}
234
235static unsigned long get_ftrace_location(void *func)
236{
237	unsigned long size, addr = (unsigned long)func;
238
239	if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
240		return 0;
241
242	return ftrace_location_range(addr, addr + size - 1);
243}
244
245static int fprobe_test_init(struct kunit *test)
246{
247	rand1 = get_random_u32_above(div_factor);
248	target = fprobe_selftest_target;
249	target2 = fprobe_selftest_target2;
250	target_nest = fprobe_selftest_nest_target;
251	target_ip = get_ftrace_location(target);
252	target2_ip = get_ftrace_location(target2);
253	target_nest_ip = get_ftrace_location(target_nest);
254
255	return 0;
256}
257
258static struct kunit_case fprobe_testcases[] = {
259	KUNIT_CASE(test_fprobe_entry),
260	KUNIT_CASE(test_fprobe),
261	KUNIT_CASE(test_fprobe_syms),
262	KUNIT_CASE(test_fprobe_data),
263	KUNIT_CASE(test_fprobe_nest),
264	KUNIT_CASE(test_fprobe_skip),
265	{}
266};
267
268static struct kunit_suite fprobe_test_suite = {
269	.name = "fprobe_test",
270	.init = fprobe_test_init,
271	.test_cases = fprobe_testcases,
272};
273
274kunit_test_suites(&fprobe_test_suite);
275
276