xref: /linux/tools/testing/selftests/bpf/progs/exceptions.c (revision 5fc5768c7ca92895ccd1de94dc521e5a55ae7896)
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 
static_func(u64 i)21 static __noinline int static_func(u64 i)
22 {
23 	bpf_throw(32);
24 	return i;
25 }
26 
global2static_simple(u64 i)27 __noinline int global2static_simple(u64 i)
28 {
29 	static_func(i + 2);
30 	return i - 1;
31 }
32 
global2static(u64 i)33 __noinline int global2static(u64 i)
34 {
35 	if (i == ETH_P_IP)
36 		bpf_throw(16);
37 	return static_func(i);
38 }
39 
static2global(u64 i)40 static __noinline int static2global(u64 i)
41 {
42 	return global2static(i) + i;
43 }
44 
45 SEC("tc")
exception_throw_always_1(struct __sk_buff * ctx)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")
exception_throw_always_2(struct __sk_buff * ctx)57 int exception_throw_always_2(struct __sk_buff *ctx)
58 {
59 	return global2static_simple(ctx->protocol);
60 }
61 
62 SEC("tc")
exception_throw_unwind_1(struct __sk_buff * ctx)63 int exception_throw_unwind_1(struct __sk_buff *ctx)
64 {
65 	return static2global(bpf_ntohs(ctx->protocol));
66 }
67 
68 SEC("tc")
exception_throw_unwind_2(struct __sk_buff * ctx)69 int exception_throw_unwind_2(struct __sk_buff *ctx)
70 {
71 	return static2global(bpf_ntohs(ctx->protocol) - 1);
72 }
73 
74 SEC("tc")
exception_throw_default(struct __sk_buff * ctx)75 int exception_throw_default(struct __sk_buff *ctx)
76 {
77 	bpf_throw(0);
78 	return 1;
79 }
80 
81 SEC("tc")
exception_throw_default_value(struct __sk_buff * ctx)82 int exception_throw_default_value(struct __sk_buff *ctx)
83 {
84 	bpf_throw(5);
85 	return 1;
86 }
87 
88 SEC("tc")
exception_tail_call_target(struct __sk_buff * ctx)89 int exception_tail_call_target(struct __sk_buff *ctx)
90 {
91 	bpf_throw(16);
92 	return 0;
93 }
94 
95 static __noinline
exception_tail_call_subprog(struct __sk_buff * ctx)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")
exception_tail_call(struct __sk_buff * ctx)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
throw_11(void)113 void throw_11(void)
114 {
115 	bpf_throw(11);
116 }
117 
118 SEC("tc")
exception_throw_from_void_global(struct __sk_buff * ctx)119 int exception_throw_from_void_global(struct __sk_buff *ctx)
120 {
121 	throw_11();
122 
123 	return 0;
124 }
125 
exception_ext_global(struct __sk_buff * ctx)126 __noinline int exception_ext_global(struct __sk_buff *ctx)
127 {
128 	volatile int ret = 0;
129 
130 	return ret;
131 }
132 
exception_ext_static(struct __sk_buff * ctx)133 static __noinline int exception_ext_static(struct __sk_buff *ctx)
134 {
135 	return exception_ext_global(ctx);
136 }
137 
138 SEC("tc")
exception_ext(struct __sk_buff * ctx)139 int exception_ext(struct __sk_buff *ctx)
140 {
141 	return exception_ext_static(ctx);
142 }
143 
exception_cb_mod_global(u64 cookie)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  */
exception_cb_mod(u64 cookie)159 __noinline int exception_cb_mod(u64 cookie)
160 {
161 	return exception_cb_mod_global(cookie) + cookie + 10;
162 }
163 
164 SEC("tc")
__exception_cb(exception_cb_mod)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 
subprog(struct __sk_buff * ctx)172 __noinline static int subprog(struct __sk_buff *ctx)
173 {
174 	return bpf_ktime_get_ns();
175 }
176 
throwing_subprog(struct __sk_buff * ctx)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 
global_subprog(struct __sk_buff * ctx)184 __noinline int global_subprog(struct __sk_buff *ctx)
185 {
186 	return bpf_ktime_get_ns();
187 }
188 
throwing_global_subprog(struct __sk_buff * ctx)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")
exception_throw_subprog(struct __sk_buff * ctx)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  */
exception_cb_stack(u64 cookie)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. */
throwing_subprog_stack(struct __sk_buff * ctx)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")
__exception_cb(exception_cb_stack)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 
assert_nz_gfunc(u64 c)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 
assert_zero_gfunc(u64 c)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 
assert_neg_gfunc(s64 c)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 
assert_pos_gfunc(s64 c)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 
assert_negeq_gfunc(s64 c)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 
assert_poseq_gfunc(s64 c)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 
assert_nz_gfunc_with(u64 c)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 
assert_zero_gfunc_with(u64 c)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 
assert_neg_gfunc_with(s64 c)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 
assert_pos_gfunc_with(s64 c)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 
assert_negeq_gfunc_with(s64 c)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 
assert_poseq_gfunc_with(s64 c)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")
exception_assert_range(struct __sk_buff * ctx)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")
exception_assert_range_with(struct __sk_buff * ctx)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")
exception_bad_assert_range(struct __sk_buff * ctx)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")
exception_bad_assert_range_with(struct __sk_buff * ctx)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 
throwing_many_args(long a,long b,long c,long d,long e,long f,long g,long h,long i,long j)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 
exception_cb_sa(u64 cookie)428 __noinline int exception_cb_sa(u64 cookie)
429 {
430 	return cookie + 1;
431 }
432 
433 SEC("tc")
__exception_cb(exception_cb_sa)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 
no_throw_many_args(long a,long b,long c,long d,long e,long f,long g,long h,long i,long j)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")
__exception_cb(exception_cb_sa)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 
subprog_throw_sa(long val)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")
__exception_cb(exception_cb_sa)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 
subprog_throw_after_sa(long val)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")
__exception_cb(exception_cb_sa)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")
exception_throw_stack_arg(struct __sk_buff * ctx)501 int exception_throw_stack_arg(struct __sk_buff *ctx)
502 {
503 	return 0;
504 }
505 
506 SEC("tc")
exception_throw_after_stack_arg(struct __sk_buff * ctx)507 int exception_throw_after_stack_arg(struct __sk_buff *ctx)
508 {
509 	return 0;
510 }
511 
512 SEC("tc")
exception_throw_subprog_stack_arg(struct __sk_buff * ctx)513 int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
514 {
515 	return 0;
516 }
517 
518 SEC("tc")
exception_throw_subprog_after_stack_arg(struct __sk_buff * ctx)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