xref: /linux/lib/test_fprobe.c (revision cb16330d)
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;
2487de2163SMasami 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,
42*cb16330dSMasami Hiramatsu (Google) 				    unsigned long ret_ip,
4376d0de57SMasami Hiramatsu (Google) 				    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);
49f4616fabSMasami Hiramatsu 	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
5534cabf8fSMasami Hiramatsu (Google) 		KUNIT_EXPECT_NULL(current_test, data);
5639d95420SMasami Hiramatsu (Google) 
5787de2163SMasami Hiramatsu (Google) 	return entry_return_value;
58f4616fabSMasami Hiramatsu }
59f4616fabSMasami Hiramatsu 
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,
61*cb16330dSMasami Hiramatsu (Google) 				    unsigned long ret_ip,
6276d0de57SMasami Hiramatsu (Google) 				    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));
72f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));
73f4616fabSMasami Hiramatsu 	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);
7834cabf8fSMasami Hiramatsu (Google) 	} else
7934cabf8fSMasami Hiramatsu (Google) 		KUNIT_EXPECT_NULL(current_test, data);
80f4616fabSMasami Hiramatsu }
81f4616fabSMasami Hiramatsu 
nest_entry_handler(struct fprobe * fp,unsigned long ip,unsigned long ret_ip,struct pt_regs * regs,void * data)8239d95420SMasami Hiramatsu (Google) static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,
83*cb16330dSMasami Hiramatsu (Google) 				      unsigned long ret_ip,
847e7ef1bfSMasami Hiramatsu (Google) 				      struct pt_regs *regs, void *data)
857e7ef1bfSMasami Hiramatsu (Google) {
867e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_FALSE(current_test, preemptible());
8739d95420SMasami 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,
91*cb16330dSMasami Hiramatsu (Google) 				      unsigned long ret_ip,
927e7ef1bfSMasami Hiramatsu (Google) 				      struct pt_regs *regs, void *data)
937e7ef1bfSMasami Hiramatsu (Google) {
947e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_FALSE(current_test, preemptible());
957e7ef1bfSMasami Hiramatsu (Google) 	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);
173f4616fabSMasami Hiramatsu 
174f4616fabSMasami Hiramatsu 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
175f4616fabSMasami Hiramatsu }
176f4616fabSMasami Hiramatsu 
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);
19034cabf8fSMasami Hiramatsu (Google) 
19134cabf8fSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
19234cabf8fSMasami Hiramatsu (Google) }
19334cabf8fSMasami 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);
2097e7ef1bfSMasami Hiramatsu (Google) 
2107e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
2117e7ef1bfSMasami Hiramatsu (Google) }
2127e7ef1bfSMasami Hiramatsu (Google) 
test_fprobe_skip(struct kunit * test)21387de2163SMasami Hiramatsu (Google) static void test_fprobe_skip(struct kunit *test)
21487de2163SMasami Hiramatsu (Google) {
21587de2163SMasami Hiramatsu (Google) 	struct fprobe fp = {
21687de2163SMasami Hiramatsu (Google) 		.entry_handler = fp_entry_handler,
21787de2163SMasami Hiramatsu (Google) 		.exit_handler = fp_exit_handler,
21887de2163SMasami Hiramatsu (Google) 	};
21987de2163SMasami Hiramatsu (Google) 
22087de2163SMasami Hiramatsu (Google) 	current_test = test;
22187de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));
22287de2163SMasami Hiramatsu (Google) 
22387de2163SMasami Hiramatsu (Google) 	entry_return_value = 1;
22487de2163SMasami Hiramatsu (Google) 	entry_val = 0;
22587de2163SMasami Hiramatsu (Google) 	exit_val = 0;
22687de2163SMasami Hiramatsu (Google) 	target(rand1);
22787de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_NE(test, 0, entry_val);
22887de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, exit_val);
22987de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, fp.nmissed);
23087de2163SMasami Hiramatsu (Google) 	entry_return_value = 0;
23187de2163SMasami Hiramatsu (Google) 
23287de2163SMasami Hiramatsu (Google) 	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
23387de2163SMasami Hiramatsu (Google) }
23487de2163SMasami Hiramatsu (Google) 
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);
243f4616fabSMasami Hiramatsu }
244f4616fabSMasami Hiramatsu 
fprobe_test_init(struct kunit * test)245f4616fabSMasami Hiramatsu static int fprobe_test_init(struct kunit *test)
246f4616fabSMasami Hiramatsu {
247d247aabdSJason A. Donenfeld 	rand1 = get_random_u32_above(div_factor);
248f4616fabSMasami Hiramatsu 	target = fprobe_selftest_target;
249f4616fabSMasami Hiramatsu 	target2 = fprobe_selftest_target2;
2507e7ef1bfSMasami Hiramatsu (Google) 	target_nest = fprobe_selftest_nest_target;
251f4616fabSMasami Hiramatsu 	target_ip = get_ftrace_location(target);
252f4616fabSMasami Hiramatsu 	target2_ip = get_ftrace_location(target2);
2537e7ef1bfSMasami Hiramatsu (Google) 	target_nest_ip = get_ftrace_location(target_nest);
254f4616fabSMasami Hiramatsu 
255f4616fabSMasami Hiramatsu 	return 0;
256f4616fabSMasami Hiramatsu }
257f4616fabSMasami Hiramatsu 
258f4616fabSMasami Hiramatsu static struct kunit_case fprobe_testcases[] = {
259f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe_entry),
260f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe),
261f4616fabSMasami Hiramatsu 	KUNIT_CASE(test_fprobe_syms),
26234cabf8fSMasami Hiramatsu (Google) 	KUNIT_CASE(test_fprobe_data),
2637e7ef1bfSMasami Hiramatsu (Google) 	KUNIT_CASE(test_fprobe_nest),
26487de2163SMasami Hiramatsu (Google) 	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,
272f4616fabSMasami Hiramatsu };
273f4616fabSMasami Hiramatsu 
274f4616fabSMasami Hiramatsu kunit_test_suites(&fprobe_test_suite);
275f4616fabSMasami Hiramatsu 
276