xref: /linux/tools/testing/selftests/bpf/progs/bpf_gotox.c (revision 2cddfc2e8fc78c13b0f5286ea5dd48cdf527ad41)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "vmlinux.h"
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_tracing.h>
6 #include <bpf/bpf_core_read.h>
7 #include "bpf_misc.h"
8 
9 __u64 in_user;
10 __u64 ret_user;
11 
12 int pid;
13 
14 /*
15  * Skip all the tests if compiler doesn't support indirect jumps.
16  *
17  * If tests are skipped, then all functions below are compiled as
18  * dummy, such that the skeleton looks the same, and the userspace
19  * program can avoid any checks rather than if data->skip is set.
20  */
21 #ifdef __BPF_FEATURE_GOTOX
22 __u64 skip SEC(".data") = 0;
23 #else
24 __u64 skip = 1;
25 #endif
26 
27 struct simple_ctx {
28 	__u64 x;
29 };
30 
31 #ifdef __BPF_FEATURE_GOTOX
32 __u64 some_var;
33 
34 /*
35  * This function adds code which will be replaced by a different
36  * number of instructions by the verifier. This adds additional
37  * stress on testing the insn_array maps corresponding to indirect jumps.
38  */
39 static __always_inline void adjust_insns(__u64 x)
40 {
41 	some_var ^= x + bpf_jiffies64();
42 }
43 
44 SEC("syscall")
45 int one_switch(struct simple_ctx *ctx)
46 {
47 	switch (ctx->x) {
48 	case 0:
49 		adjust_insns(ctx->x + 1);
50 		ret_user = 2;
51 		break;
52 	case 1:
53 		adjust_insns(ctx->x + 7);
54 		ret_user = 3;
55 		break;
56 	case 2:
57 		adjust_insns(ctx->x + 9);
58 		ret_user = 4;
59 		break;
60 	case 3:
61 		adjust_insns(ctx->x + 11);
62 		ret_user = 5;
63 		break;
64 	case 4:
65 		adjust_insns(ctx->x + 17);
66 		ret_user = 7;
67 		break;
68 	default:
69 		adjust_insns(ctx->x + 177);
70 		ret_user = 19;
71 		break;
72 	}
73 
74 	return 0;
75 }
76 
77 SEC("syscall")
78 int one_switch_non_zero_sec_off(struct simple_ctx *ctx)
79 {
80 	switch (ctx->x) {
81 	case 0:
82 		adjust_insns(ctx->x + 1);
83 		ret_user = 2;
84 		break;
85 	case 1:
86 		adjust_insns(ctx->x + 7);
87 		ret_user = 3;
88 		break;
89 	case 2:
90 		adjust_insns(ctx->x + 9);
91 		ret_user = 4;
92 		break;
93 	case 3:
94 		adjust_insns(ctx->x + 11);
95 		ret_user = 5;
96 		break;
97 	case 4:
98 		adjust_insns(ctx->x + 17);
99 		ret_user = 7;
100 		break;
101 	default:
102 		adjust_insns(ctx->x + 177);
103 		ret_user = 19;
104 		break;
105 	}
106 
107 	return 0;
108 }
109 
110 SEC("fentry/" SYS_PREFIX "sys_nanosleep")
111 int simple_test_other_sec(struct pt_regs *ctx)
112 {
113 	__u64 x = in_user;
114 
115 	if (bpf_get_current_pid_tgid() >> 32 != pid)
116 		return 0;
117 
118 	switch (x) {
119 	case 0:
120 		adjust_insns(x + 1);
121 		ret_user = 2;
122 		break;
123 	case 1:
124 		adjust_insns(x + 7);
125 		ret_user = 3;
126 		break;
127 	case 2:
128 		adjust_insns(x + 9);
129 		ret_user = 4;
130 		break;
131 	case 3:
132 		adjust_insns(x + 11);
133 		ret_user = 5;
134 		break;
135 	case 4:
136 		adjust_insns(x + 17);
137 		ret_user = 7;
138 		break;
139 	default:
140 		adjust_insns(x + 177);
141 		ret_user = 19;
142 		break;
143 	}
144 
145 	return 0;
146 }
147 
148 SEC("syscall")
149 int two_switches(struct simple_ctx *ctx)
150 {
151 	switch (ctx->x) {
152 	case 0:
153 		adjust_insns(ctx->x + 1);
154 		ret_user = 2;
155 		break;
156 	case 1:
157 		adjust_insns(ctx->x + 7);
158 		ret_user = 3;
159 		break;
160 	case 2:
161 		adjust_insns(ctx->x + 9);
162 		ret_user = 4;
163 		break;
164 	case 3:
165 		adjust_insns(ctx->x + 11);
166 		ret_user = 5;
167 		break;
168 	case 4:
169 		adjust_insns(ctx->x + 17);
170 		ret_user = 7;
171 		break;
172 	default:
173 		adjust_insns(ctx->x + 177);
174 		ret_user = 19;
175 		break;
176 	}
177 
178 	switch (ctx->x + !!ret_user) {
179 	case 1:
180 		adjust_insns(ctx->x + 7);
181 		ret_user = 103;
182 		break;
183 	case 2:
184 		adjust_insns(ctx->x + 9);
185 		ret_user = 104;
186 		break;
187 	case 3:
188 		adjust_insns(ctx->x + 11);
189 		ret_user = 107;
190 		break;
191 	case 4:
192 		adjust_insns(ctx->x + 11);
193 		ret_user = 205;
194 		break;
195 	case 5:
196 		adjust_insns(ctx->x + 11);
197 		ret_user = 115;
198 		break;
199 	default:
200 		adjust_insns(ctx->x + 177);
201 		ret_user = 1019;
202 		break;
203 	}
204 
205 	return 0;
206 }
207 
208 SEC("syscall")
209 int big_jump_table(struct simple_ctx *ctx __attribute__((unused)))
210 {
211 	const void *const jt[256] = {
212 		[0 ... 255] = &&default_label,
213 		[0] = &&l0,
214 		[11] = &&l11,
215 		[27] = &&l27,
216 		[31] = &&l31,
217 	};
218 
219 	goto *jt[ctx->x & 0xff];
220 
221 l0:
222 	adjust_insns(ctx->x + 1);
223 	ret_user = 2;
224 	return 0;
225 
226 l11:
227 	adjust_insns(ctx->x + 7);
228 	ret_user = 3;
229 	return 0;
230 
231 l27:
232 	adjust_insns(ctx->x + 9);
233 	ret_user = 4;
234 	return 0;
235 
236 l31:
237 	adjust_insns(ctx->x + 11);
238 	ret_user = 5;
239 	return 0;
240 
241 default_label:
242 	adjust_insns(ctx->x + 177);
243 	ret_user = 19;
244 	return 0;
245 }
246 
247 SEC("syscall")
248 int one_jump_two_maps(struct simple_ctx *ctx __attribute__((unused)))
249 {
250 	__label__ l1, l2, l3, l4;
251 	void *jt1[2] = { &&l1, &&l2 };
252 	void *jt2[2] = { &&l3, &&l4 };
253 	unsigned int a = ctx->x % 2;
254 	unsigned int b = (ctx->x / 2) % 2;
255 	volatile int ret = 0;
256 
257 	if (!(a < 2 && b < 2))
258 		return 19;
259 
260 	if (ctx->x % 2)
261 		goto *jt1[a];
262 	else
263 		goto *jt2[b];
264 
265 	l1: ret += 1;
266 	l2: ret += 3;
267 	l3: ret += 5;
268 	l4: ret += 7;
269 
270 	ret_user = ret;
271 	return ret;
272 }
273 
274 SEC("syscall")
275 int one_map_two_jumps(struct simple_ctx *ctx __attribute__((unused)))
276 {
277 	__label__ l1, l2, l3;
278 	void *jt[3] = { &&l1, &&l2, &&l3 };
279 	unsigned int a = (ctx->x >> 2) & 1;
280 	unsigned int b = (ctx->x >> 3) & 1;
281 	volatile int ret = 0;
282 
283 	if (ctx->x % 2)
284 		goto *jt[a];
285 
286 	if (ctx->x % 3)
287 		goto *jt[a + b];
288 
289 	l1: ret += 3;
290 	l2: ret += 5;
291 	l3: ret += 7;
292 
293 	ret_user = ret;
294 	return ret;
295 }
296 
297 /* Just to introduce some non-zero offsets in .text */
298 static __noinline int f0(volatile struct simple_ctx *ctx __arg_ctx)
299 {
300 	if (ctx)
301 		return 1;
302 	else
303 		return 13;
304 }
305 
306 SEC("syscall") int f1(struct simple_ctx *ctx)
307 {
308 	ret_user = 0;
309 	return f0(ctx);
310 }
311 
312 static __noinline int __static_global(__u64 x)
313 {
314 	switch (x) {
315 	case 0:
316 		adjust_insns(x + 1);
317 		ret_user = 2;
318 		break;
319 	case 1:
320 		adjust_insns(x + 7);
321 		ret_user = 3;
322 		break;
323 	case 2:
324 		adjust_insns(x + 9);
325 		ret_user = 4;
326 		break;
327 	case 3:
328 		adjust_insns(x + 11);
329 		ret_user = 5;
330 		break;
331 	case 4:
332 		adjust_insns(x + 17);
333 		ret_user = 7;
334 		break;
335 	default:
336 		adjust_insns(x + 177);
337 		ret_user = 19;
338 		break;
339 	}
340 
341 	return 0;
342 }
343 
344 SEC("syscall")
345 int use_static_global1(struct simple_ctx *ctx)
346 {
347 	ret_user = 0;
348 	return __static_global(ctx->x);
349 }
350 
351 SEC("syscall")
352 int use_static_global2(struct simple_ctx *ctx)
353 {
354 	ret_user = 0;
355 	adjust_insns(ctx->x + 1);
356 	return __static_global(ctx->x);
357 }
358 
359 SEC("fentry/" SYS_PREFIX "sys_nanosleep")
360 int use_static_global_other_sec(void *ctx)
361 {
362 	if (bpf_get_current_pid_tgid() >> 32 != pid)
363 		return 0;
364 
365 	return __static_global(in_user);
366 }
367 
368 __noinline int __nonstatic_global(__u64 x)
369 {
370 	switch (x) {
371 	case 0:
372 		adjust_insns(x + 1);
373 		ret_user = 2;
374 		break;
375 	case 1:
376 		adjust_insns(x + 7);
377 		ret_user = 3;
378 		break;
379 	case 2:
380 		adjust_insns(x + 9);
381 		ret_user = 4;
382 		break;
383 	case 3:
384 		adjust_insns(x + 11);
385 		ret_user = 5;
386 		break;
387 	case 4:
388 		adjust_insns(x + 17);
389 		ret_user = 7;
390 		break;
391 	default:
392 		adjust_insns(x + 177);
393 		ret_user = 19;
394 		break;
395 	}
396 
397 	return 0;
398 }
399 
400 SEC("syscall")
401 int use_nonstatic_global1(struct simple_ctx *ctx)
402 {
403 	ret_user = 0;
404 	return __nonstatic_global(ctx->x);
405 }
406 
407 SEC("syscall")
408 int use_nonstatic_global2(struct simple_ctx *ctx)
409 {
410 	ret_user = 0;
411 	adjust_insns(ctx->x + 1);
412 	return __nonstatic_global(ctx->x);
413 }
414 
415 SEC("fentry/" SYS_PREFIX "sys_nanosleep")
416 int use_nonstatic_global_other_sec(void *ctx)
417 {
418 	if (bpf_get_current_pid_tgid() >> 32 != pid)
419 		return 0;
420 
421 	return __nonstatic_global(in_user);
422 }
423 
424 #else /* __BPF_FEATURE_GOTOX */
425 
426 #define SKIP_TEST(TEST_NAME)				\
427 	SEC("syscall") int TEST_NAME(void *ctx)		\
428 	{						\
429 		return 0;				\
430 	}
431 
432 SKIP_TEST(one_switch);
433 SKIP_TEST(one_switch_non_zero_sec_off);
434 SKIP_TEST(simple_test_other_sec);
435 SKIP_TEST(two_switches);
436 SKIP_TEST(big_jump_table);
437 SKIP_TEST(one_jump_two_maps);
438 SKIP_TEST(one_map_two_jumps);
439 SKIP_TEST(use_static_global1);
440 SKIP_TEST(use_static_global2);
441 SKIP_TEST(use_static_global_other_sec);
442 SKIP_TEST(use_nonstatic_global1);
443 SKIP_TEST(use_nonstatic_global2);
444 SKIP_TEST(use_nonstatic_global_other_sec);
445 
446 #endif /* __BPF_FEATURE_GOTOX */
447 
448 char _license[] SEC("license") = "GPL";
449