xref: /linux/tools/testing/selftests/bpf/progs/exceptions.c (revision 576482b55c19e7ec00e162a0fde4c4f1a95128c7)
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 __noinline int assert_nz_gfunc(u64 c)
216 {
217 	volatile u64 cookie = c;
218 
219 	bpf_assert(cookie != 0);
220 	return 0;
221 }
222 
223 __noinline int assert_zero_gfunc(u64 c)
224 {
225 	volatile u64 cookie = c;
226 
227 	bpf_assert(bpf_cmp_unlikely(cookie, ==, 0));
228 	return 0;
229 }
230 
231 __noinline int assert_neg_gfunc(s64 c)
232 {
233 	volatile s64 cookie = c;
234 
235 	bpf_assert(bpf_cmp_unlikely(cookie, <, 0));
236 	return 0;
237 }
238 
239 __noinline int assert_pos_gfunc(s64 c)
240 {
241 	volatile s64 cookie = c;
242 
243 	bpf_assert(bpf_cmp_unlikely(cookie, >, 0));
244 	return 0;
245 }
246 
247 __noinline int assert_negeq_gfunc(s64 c)
248 {
249 	volatile s64 cookie = c;
250 
251 	bpf_assert(bpf_cmp_unlikely(cookie, <=, -1));
252 	return 0;
253 }
254 
255 __noinline int assert_poseq_gfunc(s64 c)
256 {
257 	volatile s64 cookie = c;
258 
259 	bpf_assert(bpf_cmp_unlikely(cookie, >=, 1));
260 	return 0;
261 }
262 
263 __noinline int assert_nz_gfunc_with(u64 c)
264 {
265 	volatile u64 cookie = c;
266 
267 	bpf_assert_with(cookie != 0, cookie + 100);
268 	return 0;
269 }
270 
271 __noinline int assert_zero_gfunc_with(u64 c)
272 {
273 	volatile u64 cookie = c;
274 
275 	bpf_assert_with(bpf_cmp_unlikely(cookie, ==, 0), cookie + 100);
276 	return 0;
277 }
278 
279 __noinline int assert_neg_gfunc_with(s64 c)
280 {
281 	volatile s64 cookie = c;
282 
283 	bpf_assert_with(bpf_cmp_unlikely(cookie, <, 0), cookie + 100);
284 	return 0;
285 }
286 
287 __noinline int assert_pos_gfunc_with(s64 c)
288 {
289 	volatile s64 cookie = c;
290 
291 	bpf_assert_with(bpf_cmp_unlikely(cookie, >, 0), cookie + 100);
292 	return 0;
293 }
294 
295 __noinline int assert_negeq_gfunc_with(s64 c)
296 {
297 	volatile s64 cookie = c;
298 
299 	bpf_assert_with(bpf_cmp_unlikely(cookie, <=, -1), cookie + 100);
300 	return 0;
301 }
302 
303 __noinline int assert_poseq_gfunc_with(s64 c)
304 {
305 	volatile s64 cookie = c;
306 
307 	bpf_assert_with(bpf_cmp_unlikely(cookie, >=, 1), cookie + 100);
308 	return 0;
309 }
310 
311 #define check_assert(name, cookie, tag)				\
312 SEC("tc")							\
313 int exception##tag##name(struct __sk_buff *ctx)			\
314 {								\
315 	return name(cookie) + 1;				\
316 }
317 
318 check_assert(assert_nz_gfunc, 5, _);
319 check_assert(assert_zero_gfunc, 0, _);
320 check_assert(assert_neg_gfunc, -100, _);
321 check_assert(assert_pos_gfunc, 100, _);
322 check_assert(assert_negeq_gfunc, -1, _);
323 check_assert(assert_poseq_gfunc, 1, _);
324 
325 check_assert(assert_nz_gfunc_with, 5, _);
326 check_assert(assert_zero_gfunc_with, 0, _);
327 check_assert(assert_neg_gfunc_with, -100, _);
328 check_assert(assert_pos_gfunc_with, 100, _);
329 check_assert(assert_negeq_gfunc_with, -1, _);
330 check_assert(assert_poseq_gfunc_with, 1, _);
331 
332 check_assert(assert_nz_gfunc, 0, _bad_);
333 check_assert(assert_zero_gfunc, 5, _bad_);
334 check_assert(assert_neg_gfunc, 100, _bad_);
335 check_assert(assert_pos_gfunc, -100, _bad_);
336 check_assert(assert_negeq_gfunc, 1, _bad_);
337 check_assert(assert_poseq_gfunc, -1, _bad_);
338 
339 check_assert(assert_nz_gfunc_with, 0, _bad_);
340 check_assert(assert_zero_gfunc_with, 5, _bad_);
341 check_assert(assert_neg_gfunc_with, 100, _bad_);
342 check_assert(assert_pos_gfunc_with, -100, _bad_);
343 check_assert(assert_negeq_gfunc_with, 1, _bad_);
344 check_assert(assert_poseq_gfunc_with, -1, _bad_);
345 
346 SEC("tc")
347 int exception_assert_range(struct __sk_buff *ctx)
348 {
349 	u64 time = bpf_ktime_get_ns();
350 
351 	bpf_assert_range(time, 0, ~0ULL);
352 	return 1;
353 }
354 
355 SEC("tc")
356 int exception_assert_range_with(struct __sk_buff *ctx)
357 {
358 	u64 time = bpf_ktime_get_ns();
359 
360 	bpf_assert_range_with(time, 0, ~0ULL, 10);
361 	return 1;
362 }
363 
364 SEC("tc")
365 int exception_bad_assert_range(struct __sk_buff *ctx)
366 {
367 	u64 time = bpf_ktime_get_ns();
368 
369 	bpf_assert_range(time, -100, 100);
370 	return 1;
371 }
372 
373 SEC("tc")
374 int exception_bad_assert_range_with(struct __sk_buff *ctx)
375 {
376 	u64 time = bpf_ktime_get_ns();
377 
378 	bpf_assert_range_with(time, -1000, 1000, 10);
379 	return 1;
380 }
381 
382 #if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) \
383 	&& defined(__BPF_FEATURE_STACK_ARGUMENT)
384 
385 const volatile bool has_stack_arg = true;
386 
387 long arg1 = 1, arg2 = 2, arg3 = 3, arg4 = 4, arg5 = 5;
388 long arg6 = 6, arg7 = 7, arg8 = 8, arg9 = 9, arg10 = 10;
389 
390 __noinline static long throwing_many_args(long a, long b, long c, long d,
391 					  long e, long f, long g, long h,
392 					  long i, long j)
393 {
394 	bpf_throw(a + b + c + d + e + f + g + h + i + j);
395 	return 0;
396 }
397 
398 __noinline int exception_cb_sa(u64 cookie)
399 {
400 	return cookie + 1;
401 }
402 
403 SEC("tc")
404 __exception_cb(exception_cb_sa)
405 int exception_throw_stack_arg(struct __sk_buff *ctx)
406 {
407 	throwing_many_args(arg1, arg2, arg3, arg4, arg5,
408 			   arg6, arg7, arg8, arg9, arg10);
409 	return 0;
410 }
411 
412 __noinline static long no_throw_many_args(long a, long b, long c, long d,
413 					  long e, long f, long g, long h,
414 					  long i, long j)
415 {
416 	return a + b + c + d + e + f + g + h + i + j;
417 }
418 
419 SEC("tc")
420 __exception_cb(exception_cb_sa)
421 int exception_throw_after_stack_arg(struct __sk_buff *ctx)
422 {
423 	long ret;
424 
425 	ret = no_throw_many_args(arg1, arg2, arg3, arg4, arg5,
426 				 arg6, arg7, arg8, arg9, arg10);
427 	if (ret > 0)
428 		bpf_throw(ret);
429 	return 0;
430 }
431 
432 __noinline static long subprog_throw_sa(long val)
433 {
434 	throwing_many_args(val, val + 1, val + 2, val + 3, val + 4,
435 			   val + 5, val + 6, val + 7, val + 8, val + 9);
436 	return 0;
437 }
438 
439 SEC("tc")
440 __exception_cb(exception_cb_sa)
441 int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
442 {
443 	subprog_throw_sa(arg1);
444 	return 0;
445 }
446 
447 __noinline static long subprog_throw_after_sa(long val)
448 {
449 	long ret;
450 
451 	ret = no_throw_many_args(val, val + 1, val + 2, val + 3, val + 4,
452 				 val + 5, val + 6, val + 7, val + 8, val + 9);
453 	if (ret > 0)
454 		bpf_throw(ret);
455 	return 0;
456 }
457 
458 SEC("tc")
459 __exception_cb(exception_cb_sa)
460 int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
461 {
462 	subprog_throw_after_sa(arg1);
463 	return 0;
464 }
465 
466 #else
467 
468 const volatile bool has_stack_arg = false;
469 
470 SEC("tc")
471 int exception_throw_stack_arg(struct __sk_buff *ctx)
472 {
473 	return 0;
474 }
475 
476 SEC("tc")
477 int exception_throw_after_stack_arg(struct __sk_buff *ctx)
478 {
479 	return 0;
480 }
481 
482 SEC("tc")
483 int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
484 {
485 	return 0;
486 }
487 
488 SEC("tc")
489 int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
490 {
491 	return 0;
492 }
493 
494 #endif
495 
496 char _license[] SEC("license") = "GPL";
497