xref: /openbmc/linux/lib/test_fprobe.c (revision 474be445555ba8f2e776b4b6458c310bc215f76b)
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 unsigned long target_ip;
21  static unsigned long target2_ip;
22  
23  static noinline u32 fprobe_selftest_target(u32 value)
24  {
25  	return (value / div_factor);
26  }
27  
28  static noinline u32 fprobe_selftest_target2(u32 value)
29  {
30  	return (value / div_factor) + 1;
31  }
32  
33  static notrace void fp_entry_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
34  {
35  	KUNIT_EXPECT_FALSE(current_test, preemptible());
36  	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */
37  	if (ip != target_ip)
38  		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
39  	entry_val = (rand1 / div_factor);
40  }
41  
42  static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip, struct pt_regs *regs)
43  {
44  	unsigned long ret = regs_return_value(regs);
45  
46  	KUNIT_EXPECT_FALSE(current_test, preemptible());
47  	if (ip != target_ip) {
48  		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);
49  		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1);
50  	} else
51  		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor));
52  	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));
53  	exit_val = entry_val + div_factor;
54  }
55  
56  /* Test entry only (no rethook) */
57  static void test_fprobe_entry(struct kunit *test)
58  {
59  	struct fprobe fp_entry = {
60  		.entry_handler = fp_entry_handler,
61  	};
62  
63  	current_test = test;
64  
65  	/* Before register, unregister should be failed. */
66  	KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry));
67  	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL));
68  
69  	entry_val = 0;
70  	exit_val = 0;
71  	target(rand1);
72  	KUNIT_EXPECT_NE(test, 0, entry_val);
73  	KUNIT_EXPECT_EQ(test, 0, exit_val);
74  
75  	entry_val = 0;
76  	exit_val = 0;
77  	target2(rand1);
78  	KUNIT_EXPECT_NE(test, 0, entry_val);
79  	KUNIT_EXPECT_EQ(test, 0, exit_val);
80  
81  	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry));
82  }
83  
84  static void test_fprobe(struct kunit *test)
85  {
86  	struct fprobe fp = {
87  		.entry_handler = fp_entry_handler,
88  		.exit_handler = fp_exit_handler,
89  	};
90  
91  	current_test = test;
92  	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL));
93  
94  	entry_val = 0;
95  	exit_val = 0;
96  	target(rand1);
97  	KUNIT_EXPECT_NE(test, 0, entry_val);
98  	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
99  
100  	entry_val = 0;
101  	exit_val = 0;
102  	target2(rand1);
103  	KUNIT_EXPECT_NE(test, 0, entry_val);
104  	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
105  
106  	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
107  }
108  
109  static void test_fprobe_syms(struct kunit *test)
110  {
111  	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"};
112  	struct fprobe fp = {
113  		.entry_handler = fp_entry_handler,
114  		.exit_handler = fp_exit_handler,
115  	};
116  
117  	current_test = test;
118  	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));
119  
120  	entry_val = 0;
121  	exit_val = 0;
122  	target(rand1);
123  	KUNIT_EXPECT_NE(test, 0, entry_val);
124  	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
125  
126  	entry_val = 0;
127  	exit_val = 0;
128  	target2(rand1);
129  	KUNIT_EXPECT_NE(test, 0, entry_val);
130  	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);
131  
132  	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));
133  }
134  
135  static unsigned long get_ftrace_location(void *func)
136  {
137  	unsigned long size, addr = (unsigned long)func;
138  
139  	if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)
140  		return 0;
141  
142  	return ftrace_location_range(addr, addr + size - 1);
143  }
144  
145  static int fprobe_test_init(struct kunit *test)
146  {
147  	do {
148  		rand1 = get_random_u32();
149  	} while (rand1 <= div_factor);
150  
151  	target = fprobe_selftest_target;
152  	target2 = fprobe_selftest_target2;
153  	target_ip = get_ftrace_location(target);
154  	target2_ip = get_ftrace_location(target2);
155  
156  	return 0;
157  }
158  
159  static struct kunit_case fprobe_testcases[] = {
160  	KUNIT_CASE(test_fprobe_entry),
161  	KUNIT_CASE(test_fprobe),
162  	KUNIT_CASE(test_fprobe_syms),
163  	{}
164  };
165  
166  static struct kunit_suite fprobe_test_suite = {
167  	.name = "fprobe_test",
168  	.init = fprobe_test_init,
169  	.test_cases = fprobe_testcases,
170  };
171  
172  kunit_test_suites(&fprobe_test_suite);
173  
174  MODULE_LICENSE("GPL");
175