xref: /linux/tools/testing/selftests/bpf/progs/exceptions.c (revision ff9b465ddb95d7842282998348df88ff55f62c4a)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <vmlinux.h>
3 #include <bpf/bpf_tracing.h>
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_core_read.h>
6 #include <bpf/bpf_endian.h>
7 #include "bpf_misc.h"
8 #include "bpf_experimental.h"
9 
10 #ifndef ETH_P_IP
11 #define ETH_P_IP 0x0800
12 #endif
13 
14 struct {
15 	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
16 	__uint(max_entries, 4);
17 	__uint(key_size, sizeof(__u32));
18 	__uint(value_size, sizeof(__u32));
19 } jmp_table SEC(".maps");
20 
21 static __noinline int static_func(u64 i)
22 {
23 	bpf_throw(32);
24 	return i;
25 }
26 
27 __noinline int global2static_simple(u64 i)
28 {
29 	static_func(i + 2);
30 	return i - 1;
31 }
32 
33 __noinline int global2static(u64 i)
34 {
35 	if (i == ETH_P_IP)
36 		bpf_throw(16);
37 	return static_func(i);
38 }
39 
40 static __noinline int static2global(u64 i)
41 {
42 	return global2static(i) + i;
43 }
44 
45 SEC("tc")
46 int exception_throw_always_1(struct __sk_buff *ctx)
47 {
48 	bpf_throw(64);
49 	return 0;
50 }
51 
52 /* In this case, the global func will never be seen executing after call to
53  * static subprog, hence verifier will DCE the remaining instructions. Ensure we
54  * are resilient to that.
55  */
56 SEC("tc")
57 int exception_throw_always_2(struct __sk_buff *ctx)
58 {
59 	return global2static_simple(ctx->protocol);
60 }
61 
62 SEC("tc")
63 int exception_throw_unwind_1(struct __sk_buff *ctx)
64 {
65 	return static2global(bpf_ntohs(ctx->protocol));
66 }
67 
68 SEC("tc")
69 int exception_throw_unwind_2(struct __sk_buff *ctx)
70 {
71 	return static2global(bpf_ntohs(ctx->protocol) - 1);
72 }
73 
74 SEC("tc")
75 int exception_throw_default(struct __sk_buff *ctx)
76 {
77 	bpf_throw(0);
78 	return 1;
79 }
80 
81 SEC("tc")
82 int exception_throw_default_value(struct __sk_buff *ctx)
83 {
84 	bpf_throw(5);
85 	return 1;
86 }
87 
88 SEC("tc")
89 int exception_tail_call_target(struct __sk_buff *ctx)
90 {
91 	bpf_throw(16);
92 	return 0;
93 }
94 
95 static __noinline
96 int exception_tail_call_subprog(struct __sk_buff *ctx)
97 {
98 	volatile int ret = 10;
99 
100 	bpf_tail_call_static(ctx, &jmp_table, 0);
101 	return ret;
102 }
103 
104 SEC("tc")
105 int exception_tail_call(struct __sk_buff *ctx) {
106 	volatile int ret = 0;
107 
108 	ret = exception_tail_call_subprog(ctx);
109 	return ret + 8;
110 }
111 
112 __weak
113 void throw_11(void)
114 {
115 	bpf_throw(11);
116 }
117 
118 SEC("tc")
119 int exception_throw_from_void_global(struct __sk_buff *ctx)
120 {
121 	throw_11();
122 
123 	return 0;
124 }
125 
126 __noinline int exception_ext_global(struct __sk_buff *ctx)
127 {
128 	volatile int ret = 0;
129 
130 	return ret;
131 }
132 
133 static __noinline int exception_ext_static(struct __sk_buff *ctx)
134 {
135 	return exception_ext_global(ctx);
136 }
137 
138 SEC("tc")
139 int exception_ext(struct __sk_buff *ctx)
140 {
141 	return exception_ext_static(ctx);
142 }
143 
144 __noinline int exception_cb_mod_global(u64 cookie)
145 {
146 	volatile int ret = 0;
147 
148 	return ret;
149 }
150 
151 /* Example of how the exception callback supplied during verification can still
152  * introduce extensions by calling to dummy global functions, and alter runtime
153  * behavior.
154  *
155  * Right now we don't allow freplace attachment to exception callback itself,
156  * but if the need arises this restriction is technically feasible to relax in
157  * the future.
158  */
159 __noinline int exception_cb_mod(u64 cookie)
160 {
161 	return exception_cb_mod_global(cookie) + cookie + 10;
162 }
163 
164 SEC("tc")
165 __exception_cb(exception_cb_mod)
166 int exception_ext_mod_cb_runtime(struct __sk_buff *ctx)
167 {
168 	bpf_throw(25);
169 	return 0;
170 }
171 
172 __noinline static int subprog(struct __sk_buff *ctx)
173 {
174 	return bpf_ktime_get_ns();
175 }
176 
177 __noinline static int throwing_subprog(struct __sk_buff *ctx)
178 {
179 	if (ctx->tstamp)
180 		bpf_throw(0);
181 	return bpf_ktime_get_ns();
182 }
183 
184 __noinline int global_subprog(struct __sk_buff *ctx)
185 {
186 	return bpf_ktime_get_ns();
187 }
188 
189 __noinline int throwing_global_subprog(struct __sk_buff *ctx)
190 {
191 	if (ctx->tstamp)
192 		bpf_throw(0);
193 	return bpf_ktime_get_ns();
194 }
195 
196 SEC("tc")
197 int exception_throw_subprog(struct __sk_buff *ctx)
198 {
199 	switch (ctx->protocol) {
200 	case 1:
201 		return subprog(ctx);
202 	case 2:
203 		return global_subprog(ctx);
204 	case 3:
205 		return throwing_subprog(ctx);
206 	case 4:
207 		return throwing_global_subprog(ctx);
208 	default:
209 		break;
210 	}
211 	bpf_throw(1);
212 	return 0;
213 }
214 
215 u64 exception_cb_stack_src = 0x1234;
216 
217 /*
218  * The address handed to the helper has to be this callback's own stack
219  * slot, not one from a frame that is already gone.
220  */
221 __noinline int exception_cb_stack(u64 cookie)
222 {
223 	volatile u64 val = 0xdead;
224 
225 	bpf_probe_read_kernel((void *)&val, sizeof(val), &exception_cb_stack_src);
226 	return val;
227 }
228 
229 /* Throws from a subprogram that has a stack of its own. */
230 __noinline static int throwing_subprog_stack(struct __sk_buff *ctx)
231 {
232 	volatile u64 pad[4] = {};
233 
234 	bpf_throw(pad[0]);
235 	return 0;
236 }
237 
238 SEC("tc")
239 __exception_cb(exception_cb_stack)
240 int exception_throw_subprog_stack_cb(struct __sk_buff *ctx)
241 {
242 	return throwing_subprog_stack(ctx);
243 }
244 
245 __noinline int assert_nz_gfunc(u64 c)
246 {
247 	volatile u64 cookie = c;
248 
249 	bpf_assert(cookie != 0);
250 	return 0;
251 }
252 
253 __noinline int assert_zero_gfunc(u64 c)
254 {
255 	volatile u64 cookie = c;
256 
257 	bpf_assert(bpf_cmp_unlikely(cookie, ==, 0));
258 	return 0;
259 }
260 
261 __noinline int assert_neg_gfunc(s64 c)
262 {
263 	volatile s64 cookie = c;
264 
265 	bpf_assert(bpf_cmp_unlikely(cookie, <, 0));
266 	return 0;
267 }
268 
269 __noinline int assert_pos_gfunc(s64 c)
270 {
271 	volatile s64 cookie = c;
272 
273 	bpf_assert(bpf_cmp_unlikely(cookie, >, 0));
274 	return 0;
275 }
276 
277 __noinline int assert_negeq_gfunc(s64 c)
278 {
279 	volatile s64 cookie = c;
280 
281 	bpf_assert(bpf_cmp_unlikely(cookie, <=, -1));
282 	return 0;
283 }
284 
285 __noinline int assert_poseq_gfunc(s64 c)
286 {
287 	volatile s64 cookie = c;
288 
289 	bpf_assert(bpf_cmp_unlikely(cookie, >=, 1));
290 	return 0;
291 }
292 
293 __noinline int assert_nz_gfunc_with(u64 c)
294 {
295 	volatile u64 cookie = c;
296 
297 	bpf_assert_with(cookie != 0, cookie + 100);
298 	return 0;
299 }
300 
301 __noinline int assert_zero_gfunc_with(u64 c)
302 {
303 	volatile u64 cookie = c;
304 
305 	bpf_assert_with(bpf_cmp_unlikely(cookie, ==, 0), cookie + 100);
306 	return 0;
307 }
308 
309 __noinline int assert_neg_gfunc_with(s64 c)
310 {
311 	volatile s64 cookie = c;
312 
313 	bpf_assert_with(bpf_cmp_unlikely(cookie, <, 0), cookie + 100);
314 	return 0;
315 }
316 
317 __noinline int assert_pos_gfunc_with(s64 c)
318 {
319 	volatile s64 cookie = c;
320 
321 	bpf_assert_with(bpf_cmp_unlikely(cookie, >, 0), cookie + 100);
322 	return 0;
323 }
324 
325 __noinline int assert_negeq_gfunc_with(s64 c)
326 {
327 	volatile s64 cookie = c;
328 
329 	bpf_assert_with(bpf_cmp_unlikely(cookie, <=, -1), cookie + 100);
330 	return 0;
331 }
332 
333 __noinline int assert_poseq_gfunc_with(s64 c)
334 {
335 	volatile s64 cookie = c;
336 
337 	bpf_assert_with(bpf_cmp_unlikely(cookie, >=, 1), cookie + 100);
338 	return 0;
339 }
340 
341 #define check_assert(name, cookie, tag)				\
342 SEC("tc")							\
343 int exception##tag##name(struct __sk_buff *ctx)			\
344 {								\
345 	return name(cookie) + 1;				\
346 }
347 
348 check_assert(assert_nz_gfunc, 5, _);
349 check_assert(assert_zero_gfunc, 0, _);
350 check_assert(assert_neg_gfunc, -100, _);
351 check_assert(assert_pos_gfunc, 100, _);
352 check_assert(assert_negeq_gfunc, -1, _);
353 check_assert(assert_poseq_gfunc, 1, _);
354 
355 check_assert(assert_nz_gfunc_with, 5, _);
356 check_assert(assert_zero_gfunc_with, 0, _);
357 check_assert(assert_neg_gfunc_with, -100, _);
358 check_assert(assert_pos_gfunc_with, 100, _);
359 check_assert(assert_negeq_gfunc_with, -1, _);
360 check_assert(assert_poseq_gfunc_with, 1, _);
361 
362 check_assert(assert_nz_gfunc, 0, _bad_);
363 check_assert(assert_zero_gfunc, 5, _bad_);
364 check_assert(assert_neg_gfunc, 100, _bad_);
365 check_assert(assert_pos_gfunc, -100, _bad_);
366 check_assert(assert_negeq_gfunc, 1, _bad_);
367 check_assert(assert_poseq_gfunc, -1, _bad_);
368 
369 check_assert(assert_nz_gfunc_with, 0, _bad_);
370 check_assert(assert_zero_gfunc_with, 5, _bad_);
371 check_assert(assert_neg_gfunc_with, 100, _bad_);
372 check_assert(assert_pos_gfunc_with, -100, _bad_);
373 check_assert(assert_negeq_gfunc_with, 1, _bad_);
374 check_assert(assert_poseq_gfunc_with, -1, _bad_);
375 
376 SEC("tc")
377 int exception_assert_range(struct __sk_buff *ctx)
378 {
379 	u64 time = bpf_ktime_get_ns();
380 
381 	bpf_assert_range(time, 0, ~0ULL);
382 	return 1;
383 }
384 
385 SEC("tc")
386 int exception_assert_range_with(struct __sk_buff *ctx)
387 {
388 	u64 time = bpf_ktime_get_ns();
389 
390 	bpf_assert_range_with(time, 0, ~0ULL, 10);
391 	return 1;
392 }
393 
394 SEC("tc")
395 int exception_bad_assert_range(struct __sk_buff *ctx)
396 {
397 	u64 time = bpf_ktime_get_ns();
398 
399 	bpf_assert_range(time, -100, 100);
400 	return 1;
401 }
402 
403 SEC("tc")
404 int exception_bad_assert_range_with(struct __sk_buff *ctx)
405 {
406 	u64 time = bpf_ktime_get_ns();
407 
408 	bpf_assert_range_with(time, -1000, 1000, 10);
409 	return 1;
410 }
411 
412 #if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) \
413 	&& defined(__BPF_FEATURE_STACK_ARGUMENT)
414 
415 const volatile bool has_stack_arg = true;
416 
417 long arg1 = 1, arg2 = 2, arg3 = 3, arg4 = 4, arg5 = 5;
418 long arg6 = 6, arg7 = 7, arg8 = 8, arg9 = 9, arg10 = 10;
419 
420 __noinline static long throwing_many_args(long a, long b, long c, long d,
421 					  long e, long f, long g, long h,
422 					  long i, long j)
423 {
424 	bpf_throw(a + b + c + d + e + f + g + h + i + j);
425 	return 0;
426 }
427 
428 __noinline int exception_cb_sa(u64 cookie)
429 {
430 	return cookie + 1;
431 }
432 
433 SEC("tc")
434 __exception_cb(exception_cb_sa)
435 int exception_throw_stack_arg(struct __sk_buff *ctx)
436 {
437 	throwing_many_args(arg1, arg2, arg3, arg4, arg5,
438 			   arg6, arg7, arg8, arg9, arg10);
439 	return 0;
440 }
441 
442 __noinline static long no_throw_many_args(long a, long b, long c, long d,
443 					  long e, long f, long g, long h,
444 					  long i, long j)
445 {
446 	return a + b + c + d + e + f + g + h + i + j;
447 }
448 
449 SEC("tc")
450 __exception_cb(exception_cb_sa)
451 int exception_throw_after_stack_arg(struct __sk_buff *ctx)
452 {
453 	long ret;
454 
455 	ret = no_throw_many_args(arg1, arg2, arg3, arg4, arg5,
456 				 arg6, arg7, arg8, arg9, arg10);
457 	if (ret > 0)
458 		bpf_throw(ret);
459 	return 0;
460 }
461 
462 __noinline static long subprog_throw_sa(long val)
463 {
464 	throwing_many_args(val, val + 1, val + 2, val + 3, val + 4,
465 			   val + 5, val + 6, val + 7, val + 8, val + 9);
466 	return 0;
467 }
468 
469 SEC("tc")
470 __exception_cb(exception_cb_sa)
471 int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
472 {
473 	subprog_throw_sa(arg1);
474 	return 0;
475 }
476 
477 __noinline static long subprog_throw_after_sa(long val)
478 {
479 	long ret;
480 
481 	ret = no_throw_many_args(val, val + 1, val + 2, val + 3, val + 4,
482 				 val + 5, val + 6, val + 7, val + 8, val + 9);
483 	if (ret > 0)
484 		bpf_throw(ret);
485 	return 0;
486 }
487 
488 SEC("tc")
489 __exception_cb(exception_cb_sa)
490 int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
491 {
492 	subprog_throw_after_sa(arg1);
493 	return 0;
494 }
495 
496 #else
497 
498 const volatile bool has_stack_arg = false;
499 
500 SEC("tc")
501 int exception_throw_stack_arg(struct __sk_buff *ctx)
502 {
503 	return 0;
504 }
505 
506 SEC("tc")
507 int exception_throw_after_stack_arg(struct __sk_buff *ctx)
508 {
509 	return 0;
510 }
511 
512 SEC("tc")
513 int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
514 {
515 	return 0;
516 }
517 
518 SEC("tc")
519 int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
520 {
521 	return 0;
522 }
523 
524 #endif
525 
526 char _license[] SEC("license") = "GPL";
527