xref: /openbmc/linux/lib/test_fprobe.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1f4616fabSMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0-or-later
2f4616fabSMasami Hiramatsu /*
3f4616fabSMasami Hiramatsu  * test_fprobe.c - simple sanity test for fprobe
4f4616fabSMasami Hiramatsu  */
5f4616fabSMasami Hiramatsu 
6f4616fabSMasami Hiramatsu #include <linux/kernel.h>
7f4616fabSMasami Hiramatsu #include <linux/fprobe.h>
8f4616fabSMasami Hiramatsu #include <linux/random.h>
9f4616fabSMasami Hiramatsu #include <kunit/test.h>
10f4616fabSMasami Hiramatsu 
11f4616fabSMasami Hiramatsu #define div_factor 3
12f4616fabSMasami Hiramatsu 
13f4616fabSMasami Hiramatsu static struct kunit *current_test;
14f4616fabSMasami Hiramatsu 
15f4616fabSMasami Hiramatsu static u32 rand1, entry_val, exit_val;
16f4616fabSMasami Hiramatsu 
17f4616fabSMasami Hiramatsu /* Use indirect calls to avoid inlining the target functions */
18f4616fabSMasami Hiramatsu static u32 (*target)(u32 value);
19f4616fabSMasami Hiramatsu static u32 (*target2)(u32 value);
207e7ef1bfSMasami Hiramatsu (Google) static u32 (*target_nest)(u32 value, u32 (*nest)(u32));
21f4616fabSMasami Hiramatsu static unsigned long target_ip;
22f4616fabSMasami Hiramatsu static unsigned long target2_ip;
237e7ef1bfSMasami Hiramatsu (Google) static unsigned long target_nest_ip;
24*87de2163SMasami Hiramatsu (Google) static int entry_return_value;
25f4616fabSMasami Hiramatsu 
fprobe_selftest_target(u32 value)26f4616fabSMasami Hiramatsu static noinline u32 fprobe_selftest_target(u32 value)
27f4616fabSMasami Hiramatsu {
28f4616fabSMasami Hiramatsu 	return (value / div_factor);
29f4616fabSMasami Hiramatsu }
30f4616fabSMasami Hiramatsu 
fprobe_selftest_target2(u32 value)31f4616fabSMasami Hiramatsu static noinline u32 fprobe_selftest_target2(u32 value)
32f4616fabSMasami Hiramatsu {
33f4616fabSMasami Hiramatsu 	return (value / div_factor) + 1;
34f4616fabSMasami Hiramatsu }
35f4616fabSMasami Hiramatsu 
fprobe_selftest_nest_target(u32 value,u32 (* nest)(u32))367e7ef1bfSMasami Hiramatsu (Google) static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))
377e7ef1bfSMasami Hiramatsu (Google) {
387e7ef1bfSMasami Hiramatsu (Google) 	return nest(value + 2);
397e7ef1bfSMasami Hiramatsu (Google) }
407e7ef1bfSMasami Hiramatsu (Google) 
fp_entry_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct pt_regs * regs,void * data)4139d95420SMasami Hiramatsu (Google) static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,
4276d0de57SMasami Hiramatsu (Google) 				    unsigned long ret_ip,
43f4616fabSMasami Hiramatsu 				    struct pt_regs *regs, void *data)
44f4616fabSMasami Hiramatsu {
45f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_FALSE(current_test, preemptible());
46f4616fabSMasami Hiramatsu 	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
47f4616fabSMasami Hiramatsu 	if (ip != target_ip)
48f4616fabSMasami Hiramatsu 		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
4934cabf8fSMasami Hiramatsu (Google) 	entry_val = (rand1 / div_factor);
5034cabf8fSMasami Hiramatsu (Google) 	if (fp->entry_data_size) {
5134cabf8fSMasami Hiramatsu (Google) 		KUNIT_EXPECT_NOT_NULL(current_test, data);
5234cabf8fSMasami Hiramatsu (Google) 		if (data)
5334cabf8fSMasami Hiramatsu (Google) 			*(u32 *)data = entry_val;
5434cabf8fSMasami Hiramatsu (Google) 	} else
5539d95420SMasami Hiramatsu (Google) 		KUNIT_EXPECT_NULL(current_test, data);
56*87de2163SMasami Hiramatsu (Google) 
57f4616fabSMasami Hiramatsu 	return entry_return_value;
58f4616fabSMasami Hiramatsu }
5976d0de57SMasami Hiramatsu (Google) 
fp_exit_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct pt_regs * regs,void * data)6076d0de57SMasami Hiramatsu (Google) static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,
61f4616fabSMasami Hiramatsu 				    unsigned long ret_ip,
62f4616fabSMasami Hiramatsu 				    struct pt_regs *regs, void *data)
63f4616fabSMasami Hiramatsu {
64f4616fabSMasami Hiramatsu 	unsigned long ret = regs_return_value(regs);
65f4616fabSMasami Hiramatsu 
66f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_FALSE(current_test, preemptible());
67f4616fabSMasami Hiramatsu 	if (ip != target_ip) {
68f4616fabSMasami Hiramatsu 		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
69f4616fabSMasami Hiramatsu 		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1);
70f4616fabSMasami Hiramatsu 	} else
71f4616fabSMasami Hiramatsu 		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor));
7234cabf8fSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));
7334cabf8fSMasami Hiramatsu (Google) 	exit_val = entry_val + div_factor;
7434cabf8fSMasami Hiramatsu (Google) 	if (fp->entry_data_size) {
7534cabf8fSMasami Hiramatsu (Google) 		KUNIT_EXPECT_NOT_NULL(current_test, data);
7634cabf8fSMasami Hiramatsu (Google) 		if (data)
7734cabf8fSMasami Hiramatsu (Google) 			KUNIT_EXPECT_EQ(current_test, *(u32 *)data, entry_val);
78f4616fabSMasami Hiramatsu 	} else
79f4616fabSMasami Hiramatsu 		KUNIT_EXPECT_NULL(current_test, data);
8039d95420SMasami Hiramatsu (Google) }
817e7ef1bfSMasami Hiramatsu (Google) 
nest_entry_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct pt_regs * regs,void * data)827e7ef1bfSMasami Hiramatsu (Google) static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
837e7ef1bfSMasami Hiramatsu (Google) 				      unsigned long ret_ip,
8439d95420SMasami Hiramatsu (Google) 				      struct pt_regs *regs, void *data)
857e7ef1bfSMasami Hiramatsu (Google) {
867e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_FALSE(current_test, preemptible());
877e7ef1bfSMasami Hiramatsu (Google) 	return 0;
887e7ef1bfSMasami Hiramatsu (Google) }
897e7ef1bfSMasami Hiramatsu (Google) 
nest_exit_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct pt_regs * regs,void * data)907e7ef1bfSMasami Hiramatsu (Google) static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,
917e7ef1bfSMasami Hiramatsu (Google) 				      unsigned long ret_ip,
92f4616fabSMasami Hiramatsu 				      struct pt_regs *regs, void *data)
93f4616fabSMasami Hiramatsu {
94f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_FALSE(current_test, preemptible());
95f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);
96f4616fabSMasami Hiramatsu }
97f4616fabSMasami Hiramatsu 
98f4616fabSMasami Hiramatsu /* Test entry only (no rethook) */
test_fprobe_entry(struct kunit * test)99f4616fabSMasami Hiramatsu static void test_fprobe_entry(struct kunit *test)
100f4616fabSMasami Hiramatsu {
101f4616fabSMasami Hiramatsu 	struct fprobe fp_entry = {
102f4616fabSMasami Hiramatsu 		.entry_handler = fp_entry_handler,
103f4616fabSMasami Hiramatsu 	};
104f4616fabSMasami Hiramatsu 
105f4616fabSMasami Hiramatsu 	current_test = test;
106f4616fabSMasami Hiramatsu 
107f4616fabSMasami Hiramatsu 	/* Before register, unregister should be failed. */
108f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry));
109f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL));
110f4616fabSMasami Hiramatsu 
111f4616fabSMasami Hiramatsu 	entry_val = 0;
112f4616fabSMasami Hiramatsu 	exit_val = 0;
113f4616fabSMasami Hiramatsu 	target(rand1);
114f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, entry_val);
115f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, exit_val);
116f4616fabSMasami Hiramatsu 
117f4616fabSMasami Hiramatsu 	entry_val = 0;
118f4616fabSMasami Hiramatsu 	exit_val = 0;
119f4616fabSMasami Hiramatsu 	target2(rand1);
120f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, entry_val);
121f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, exit_val);
122f4616fabSMasami Hiramatsu 
123f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry));
124f4616fabSMasami Hiramatsu }
125f4616fabSMasami Hiramatsu 
test_fprobe(struct kunit * test)126f4616fabSMasami Hiramatsu static void test_fprobe(struct kunit *test)
127f4616fabSMasami Hiramatsu {
128f4616fabSMasami Hiramatsu 	struct fprobe fp = {
129f4616fabSMasami Hiramatsu 		.entry_handler = fp_entry_handler,
130f4616fabSMasami Hiramatsu 		.exit_handler = fp_exit_handler,
131f4616fabSMasami Hiramatsu 	};
132f4616fabSMasami Hiramatsu 
133f4616fabSMasami Hiramatsu 	current_test = test;
134f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL));
135f4616fabSMasami Hiramatsu 
136f4616fabSMasami Hiramatsu 	entry_val = 0;
137f4616fabSMasami Hiramatsu 	exit_val = 0;
138f4616fabSMasami Hiramatsu 	target(rand1);
139f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, entry_val);
140f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
141f4616fabSMasami Hiramatsu 
142f4616fabSMasami Hiramatsu 	entry_val = 0;
143f4616fabSMasami Hiramatsu 	exit_val = 0;
144f4616fabSMasami Hiramatsu 	target2(rand1);
145f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, entry_val);
146f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
147f4616fabSMasami Hiramatsu 
148f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
149f4616fabSMasami Hiramatsu }
150f4616fabSMasami Hiramatsu 
test_fprobe_syms(struct kunit * test)151f4616fabSMasami Hiramatsu static void test_fprobe_syms(struct kunit *test)
152f4616fabSMasami Hiramatsu {
153f4616fabSMasami Hiramatsu 	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"};
154f4616fabSMasami Hiramatsu 	struct fprobe fp = {
155f4616fabSMasami Hiramatsu 		.entry_handler = fp_entry_handler,
156f4616fabSMasami Hiramatsu 		.exit_handler = fp_exit_handler,
157f4616fabSMasami Hiramatsu 	};
158f4616fabSMasami Hiramatsu 
159f4616fabSMasami Hiramatsu 	current_test = test;
160f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
161f4616fabSMasami Hiramatsu 
162f4616fabSMasami Hiramatsu 	entry_val = 0;
163f4616fabSMasami Hiramatsu 	exit_val = 0;
164f4616fabSMasami Hiramatsu 	target(rand1);
165f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, entry_val);
166f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
167f4616fabSMasami Hiramatsu 
168f4616fabSMasami Hiramatsu 	entry_val = 0;
169f4616fabSMasami Hiramatsu 	exit_val = 0;
170f4616fabSMasami Hiramatsu 	target2(rand1);
171f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_NE(test, 0, entry_val);
172f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
17334cabf8fSMasami Hiramatsu (Google) 
17434cabf8fSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
17534cabf8fSMasami Hiramatsu (Google) }
17634cabf8fSMasami Hiramatsu (Google) 
17734cabf8fSMasami Hiramatsu (Google) /* Test private entry_data */
test_fprobe_data(struct kunit * test)17834cabf8fSMasami Hiramatsu (Google) static void test_fprobe_data(struct kunit *test)
17934cabf8fSMasami Hiramatsu (Google) {
18034cabf8fSMasami Hiramatsu (Google) 	struct fprobe fp = {
18134cabf8fSMasami Hiramatsu (Google) 		.entry_handler = fp_entry_handler,
18234cabf8fSMasami Hiramatsu (Google) 		.exit_handler = fp_exit_handler,
18334cabf8fSMasami Hiramatsu (Google) 		.entry_data_size = sizeof(u32),
18434cabf8fSMasami Hiramatsu (Google) 	};
18534cabf8fSMasami Hiramatsu (Google) 
18634cabf8fSMasami Hiramatsu (Google) 	current_test = test;
18734cabf8fSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
18834cabf8fSMasami Hiramatsu (Google) 
18934cabf8fSMasami Hiramatsu (Google) 	target(rand1);
1907e7ef1bfSMasami Hiramatsu (Google) 
1917e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
1927e7ef1bfSMasami Hiramatsu (Google) }
1937e7ef1bfSMasami Hiramatsu (Google) 
1947e7ef1bfSMasami Hiramatsu (Google) /* Test nr_maxactive */
test_fprobe_nest(struct kunit * test)1957e7ef1bfSMasami Hiramatsu (Google) static void test_fprobe_nest(struct kunit *test)
1967e7ef1bfSMasami Hiramatsu (Google) {
1977e7ef1bfSMasami Hiramatsu (Google) 	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"};
1987e7ef1bfSMasami Hiramatsu (Google) 	struct fprobe fp = {
1997e7ef1bfSMasami Hiramatsu (Google) 		.entry_handler = nest_entry_handler,
2007e7ef1bfSMasami Hiramatsu (Google) 		.exit_handler = nest_exit_handler,
2017e7ef1bfSMasami Hiramatsu (Google) 		.nr_maxactive = 1,
2027e7ef1bfSMasami Hiramatsu (Google) 	};
2037e7ef1bfSMasami Hiramatsu (Google) 
2047e7ef1bfSMasami Hiramatsu (Google) 	current_test = test;
2057e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
2067e7ef1bfSMasami Hiramatsu (Google) 
2077e7ef1bfSMasami Hiramatsu (Google) 	target_nest(rand1, target);
2087e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 1, fp.nmissed);
209*87de2163SMasami Hiramatsu (Google) 
210*87de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
211*87de2163SMasami Hiramatsu (Google) }
212*87de2163SMasami Hiramatsu (Google) 
test_fprobe_skip(struct kunit * test)213*87de2163SMasami Hiramatsu (Google) static void test_fprobe_skip(struct kunit *test)
214*87de2163SMasami Hiramatsu (Google) {
215*87de2163SMasami Hiramatsu (Google) 	struct fprobe fp = {
216*87de2163SMasami Hiramatsu (Google) 		.entry_handler = fp_entry_handler,
217*87de2163SMasami Hiramatsu (Google) 		.exit_handler = fp_exit_handler,
218*87de2163SMasami Hiramatsu (Google) 	};
219*87de2163SMasami Hiramatsu (Google) 
220*87de2163SMasami Hiramatsu (Google) 	current_test = test;
221*87de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
222*87de2163SMasami Hiramatsu (Google) 
223*87de2163SMasami Hiramatsu (Google) 	entry_return_value = 1;
224*87de2163SMasami Hiramatsu (Google) 	entry_val = 0;
225*87de2163SMasami Hiramatsu (Google) 	exit_val = 0;
226*87de2163SMasami Hiramatsu (Google) 	target(rand1);
227*87de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_NE(test, 0, entry_val);
228*87de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, exit_val);
229*87de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, fp.nmissed);
230*87de2163SMasami Hiramatsu (Google) 	entry_return_value = 0;
231f4616fabSMasami Hiramatsu 
232f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
233f4616fabSMasami Hiramatsu }
234f4616fabSMasami Hiramatsu 
get_ftrace_location(void * func)235f4616fabSMasami Hiramatsu static unsigned long get_ftrace_location(void *func)
236f4616fabSMasami Hiramatsu {
237f4616fabSMasami Hiramatsu 	unsigned long size, addr = (unsigned long)func;
238f4616fabSMasami Hiramatsu 
239f4616fabSMasami Hiramatsu 	if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
240f4616fabSMasami Hiramatsu 		return 0;
241f4616fabSMasami Hiramatsu 
242f4616fabSMasami Hiramatsu 	return ftrace_location_range(addr, addr + size - 1);
243d247aabdSJason A. Donenfeld }
244f4616fabSMasami Hiramatsu 
fprobe_test_init(struct kunit * test)245f4616fabSMasami Hiramatsu static int fprobe_test_init(struct kunit *test)
2467e7ef1bfSMasami Hiramatsu (Google) {
247f4616fabSMasami Hiramatsu 	rand1 = get_random_u32_above(div_factor);
248f4616fabSMasami Hiramatsu 	target = fprobe_selftest_target;
2497e7ef1bfSMasami Hiramatsu (Google) 	target2 = fprobe_selftest_target2;
250f4616fabSMasami Hiramatsu 	target_nest = fprobe_selftest_nest_target;
251f4616fabSMasami Hiramatsu 	target_ip = get_ftrace_location(target);
252f4616fabSMasami Hiramatsu 	target2_ip = get_ftrace_location(target2);
253f4616fabSMasami Hiramatsu 	target_nest_ip = get_ftrace_location(target_nest);
254f4616fabSMasami Hiramatsu 
255f4616fabSMasami Hiramatsu 	return 0;
256f4616fabSMasami Hiramatsu }
257f4616fabSMasami Hiramatsu 
25834cabf8fSMasami Hiramatsu (Google) static struct kunit_case fprobe_testcases[] = {
2597e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_CASE(test_fprobe_entry),
260*87de2163SMasami Hiramatsu (Google) 	KUNIT_CASE(test_fprobe),
261f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe_syms),
262f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe_data),
263f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe_nest),
264f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe_skip),
265f4616fabSMasami Hiramatsu 	{}
266f4616fabSMasami Hiramatsu };
267f4616fabSMasami Hiramatsu 
268f4616fabSMasami Hiramatsu static struct kunit_suite fprobe_test_suite = {
269f4616fabSMasami Hiramatsu 	.name = "fprobe_test",
270f4616fabSMasami Hiramatsu 	.init = fprobe_test_init,
271f4616fabSMasami Hiramatsu 	.test_cases = fprobe_testcases,
272 };
273 
274 kunit_test_suites(&fprobe_test_suite);
275 
276