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