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