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