xref: /linux/tools/testing/selftests/bpf/progs/exceptions.c (revision 422f1efabd1389020be1c768c66aa6df40f528e7)
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 char _license[] SEC("license") = "GPL";
383