]>
Commit | Line | Data |
---|---|---|
f4616fab MH |
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 | ||
13 | static struct kunit *current_test; | |
14 | ||
15 | static u32 rand1, entry_val, exit_val; | |
16 | ||
17 | /* Use indirect calls to avoid inlining the target functions */ | |
18 | static u32 (*target)(u32 value); | |
19 | static u32 (*target2)(u32 value); | |
7e7ef1bf | 20 | static u32 (*target_nest)(u32 value, u32 (*nest)(u32)); |
f4616fab MH |
21 | static unsigned long target_ip; |
22 | static unsigned long target2_ip; | |
7e7ef1bf | 23 | static unsigned long target_nest_ip; |
87de2163 | 24 | static int entry_return_value; |
f4616fab MH |
25 | |
26 | static noinline u32 fprobe_selftest_target(u32 value) | |
27 | { | |
28 | return (value / div_factor); | |
29 | } | |
30 | ||
31 | static noinline u32 fprobe_selftest_target2(u32 value) | |
32 | { | |
33 | return (value / div_factor) + 1; | |
34 | } | |
35 | ||
7e7ef1bf MHG |
36 | static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32)) |
37 | { | |
38 | return nest(value + 2); | |
39 | } | |
40 | ||
39d95420 | 41 | static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip, |
cb16330d MHG |
42 | unsigned long ret_ip, |
43 | struct pt_regs *regs, void *data) | |
f4616fab MH |
44 | { |
45 | KUNIT_EXPECT_FALSE(current_test, preemptible()); | |
46 | /* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */ | |
47 | if (ip != target_ip) | |
48 | KUNIT_EXPECT_EQ(current_test, ip, target2_ip); | |
49 | entry_val = (rand1 / div_factor); | |
34cabf8f MHG |
50 | if (fp->entry_data_size) { |
51 | KUNIT_EXPECT_NOT_NULL(current_test, data); | |
52 | if (data) | |
53 | *(u32 *)data = entry_val; | |
54 | } else | |
55 | KUNIT_EXPECT_NULL(current_test, data); | |
39d95420 | 56 | |
87de2163 | 57 | return entry_return_value; |
f4616fab MH |
58 | } |
59 | ||
76d0de57 | 60 | static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, |
cb16330d | 61 | unsigned long ret_ip, |
76d0de57 | 62 | struct pt_regs *regs, void *data) |
f4616fab MH |
63 | { |
64 | unsigned long ret = regs_return_value(regs); | |
65 | ||
66 | KUNIT_EXPECT_FALSE(current_test, preemptible()); | |
67 | if (ip != target_ip) { | |
68 | KUNIT_EXPECT_EQ(current_test, ip, target2_ip); | |
69 | KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1); | |
70 | } else | |
71 | KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor)); | |
72 | KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor)); | |
73 | exit_val = entry_val + div_factor; | |
34cabf8f MHG |
74 | if (fp->entry_data_size) { |
75 | KUNIT_EXPECT_NOT_NULL(current_test, data); | |
76 | if (data) | |
77 | KUNIT_EXPECT_EQ(current_test, *(u32 *)data, entry_val); | |
78 | } else | |
79 | KUNIT_EXPECT_NULL(current_test, data); | |
f4616fab MH |
80 | } |
81 | ||
39d95420 | 82 | static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip, |
cb16330d MHG |
83 | unsigned long ret_ip, |
84 | struct pt_regs *regs, void *data) | |
7e7ef1bf MHG |
85 | { |
86 | KUNIT_EXPECT_FALSE(current_test, preemptible()); | |
39d95420 | 87 | return 0; |
7e7ef1bf MHG |
88 | } |
89 | ||
90 | static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip, | |
cb16330d MHG |
91 | unsigned long ret_ip, |
92 | struct pt_regs *regs, void *data) | |
7e7ef1bf MHG |
93 | { |
94 | KUNIT_EXPECT_FALSE(current_test, preemptible()); | |
95 | KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip); | |
f4616fab MH |
96 | } |
97 | ||
98 | /* Test entry only (no rethook) */ | |
99 | static void test_fprobe_entry(struct kunit *test) | |
100 | { | |
101 | struct fprobe fp_entry = { | |
102 | .entry_handler = fp_entry_handler, | |
103 | }; | |
104 | ||
105 | current_test = test; | |
106 | ||
107 | /* Before register, unregister should be failed. */ | |
108 | KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry)); | |
109 | KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL)); | |
110 | ||
111 | entry_val = 0; | |
112 | exit_val = 0; | |
113 | target(rand1); | |
114 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
115 | KUNIT_EXPECT_EQ(test, 0, exit_val); | |
116 | ||
117 | entry_val = 0; | |
118 | exit_val = 0; | |
119 | target2(rand1); | |
120 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
121 | KUNIT_EXPECT_EQ(test, 0, exit_val); | |
122 | ||
123 | KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry)); | |
124 | } | |
125 | ||
126 | static void test_fprobe(struct kunit *test) | |
127 | { | |
128 | struct fprobe fp = { | |
129 | .entry_handler = fp_entry_handler, | |
130 | .exit_handler = fp_exit_handler, | |
131 | }; | |
132 | ||
133 | current_test = test; | |
134 | KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL)); | |
135 | ||
136 | entry_val = 0; | |
137 | exit_val = 0; | |
138 | target(rand1); | |
139 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
140 | KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); | |
141 | ||
142 | entry_val = 0; | |
143 | exit_val = 0; | |
144 | target2(rand1); | |
145 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
146 | KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); | |
147 | ||
148 | KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); | |
149 | } | |
150 | ||
151 | static void test_fprobe_syms(struct kunit *test) | |
152 | { | |
153 | static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"}; | |
154 | struct fprobe fp = { | |
155 | .entry_handler = fp_entry_handler, | |
156 | .exit_handler = fp_exit_handler, | |
157 | }; | |
158 | ||
159 | current_test = test; | |
160 | KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2)); | |
161 | ||
162 | entry_val = 0; | |
163 | exit_val = 0; | |
164 | target(rand1); | |
165 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
166 | KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); | |
167 | ||
168 | entry_val = 0; | |
169 | exit_val = 0; | |
170 | target2(rand1); | |
171 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
172 | KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val); | |
173 | ||
174 | KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); | |
175 | } | |
176 | ||
34cabf8f MHG |
177 | /* Test private entry_data */ |
178 | static void test_fprobe_data(struct kunit *test) | |
179 | { | |
180 | struct fprobe fp = { | |
181 | .entry_handler = fp_entry_handler, | |
182 | .exit_handler = fp_exit_handler, | |
183 | .entry_data_size = sizeof(u32), | |
184 | }; | |
185 | ||
186 | current_test = test; | |
187 | KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL)); | |
188 | ||
189 | target(rand1); | |
190 | ||
191 | KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); | |
192 | } | |
193 | ||
7e7ef1bf MHG |
194 | /* Test nr_maxactive */ |
195 | static void test_fprobe_nest(struct kunit *test) | |
196 | { | |
197 | static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"}; | |
198 | struct fprobe fp = { | |
199 | .entry_handler = nest_entry_handler, | |
200 | .exit_handler = nest_exit_handler, | |
201 | .nr_maxactive = 1, | |
202 | }; | |
203 | ||
204 | current_test = test; | |
205 | KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2)); | |
206 | ||
207 | target_nest(rand1, target); | |
208 | KUNIT_EXPECT_EQ(test, 1, fp.nmissed); | |
209 | ||
210 | KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); | |
211 | } | |
212 | ||
87de2163 MHG |
213 | static void test_fprobe_skip(struct kunit *test) |
214 | { | |
215 | struct fprobe fp = { | |
216 | .entry_handler = fp_entry_handler, | |
217 | .exit_handler = fp_exit_handler, | |
218 | }; | |
219 | ||
220 | current_test = test; | |
221 | KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL)); | |
222 | ||
223 | entry_return_value = 1; | |
224 | entry_val = 0; | |
225 | exit_val = 0; | |
226 | target(rand1); | |
227 | KUNIT_EXPECT_NE(test, 0, entry_val); | |
228 | KUNIT_EXPECT_EQ(test, 0, exit_val); | |
229 | KUNIT_EXPECT_EQ(test, 0, fp.nmissed); | |
230 | entry_return_value = 0; | |
231 | ||
232 | KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp)); | |
233 | } | |
234 | ||
f4616fab MH |
235 | static unsigned long get_ftrace_location(void *func) |
236 | { | |
237 | unsigned long size, addr = (unsigned long)func; | |
238 | ||
239 | if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size) | |
240 | return 0; | |
241 | ||
242 | return ftrace_location_range(addr, addr + size - 1); | |
243 | } | |
244 | ||
245 | static int fprobe_test_init(struct kunit *test) | |
246 | { | |
d247aabd | 247 | rand1 = get_random_u32_above(div_factor); |
f4616fab MH |
248 | target = fprobe_selftest_target; |
249 | target2 = fprobe_selftest_target2; | |
7e7ef1bf | 250 | target_nest = fprobe_selftest_nest_target; |
f4616fab MH |
251 | target_ip = get_ftrace_location(target); |
252 | target2_ip = get_ftrace_location(target2); | |
7e7ef1bf | 253 | target_nest_ip = get_ftrace_location(target_nest); |
f4616fab MH |
254 | |
255 | return 0; | |
256 | } | |
257 | ||
258 | static struct kunit_case fprobe_testcases[] = { | |
259 | KUNIT_CASE(test_fprobe_entry), | |
260 | KUNIT_CASE(test_fprobe), | |
261 | KUNIT_CASE(test_fprobe_syms), | |
34cabf8f | 262 | KUNIT_CASE(test_fprobe_data), |
7e7ef1bf | 263 | KUNIT_CASE(test_fprobe_nest), |
87de2163 | 264 | KUNIT_CASE(test_fprobe_skip), |
f4616fab MH |
265 | {} |
266 | }; | |
267 | ||
268 | static struct kunit_suite fprobe_test_suite = { | |
269 | .name = "fprobe_test", | |
270 | .init = fprobe_test_init, | |
271 | .test_cases = fprobe_testcases, | |
272 | }; | |
273 | ||
274 | kunit_test_suites(&fprobe_test_suite); | |
275 |