File Index
Files and directories
Sign in
FILE

test_fprobe.c

File preview with syntax highlighting.

linux-6.19.14/lib/tests/test_fprobe.c C
  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
~/ Read-only file preview.