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