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 | __noinline int exception_ext_global(struct __sk_buff *ctx) |
113 | { |
114 | volatile int ret = 0; |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | static __noinline int exception_ext_static(struct __sk_buff *ctx) |
120 | { |
121 | return exception_ext_global(ctx); |
122 | } |
123 | |
124 | SEC("tc" ) |
125 | int exception_ext(struct __sk_buff *ctx) |
126 | { |
127 | return exception_ext_static(ctx); |
128 | } |
129 | |
130 | __noinline int exception_cb_mod_global(u64 cookie) |
131 | { |
132 | volatile int ret = 0; |
133 | |
134 | return ret; |
135 | } |
136 | |
137 | /* Example of how the exception callback supplied during verification can still |
138 | * introduce extensions by calling to dummy global functions, and alter runtime |
139 | * behavior. |
140 | * |
141 | * Right now we don't allow freplace attachment to exception callback itself, |
142 | * but if the need arises this restriction is technically feasible to relax in |
143 | * the future. |
144 | */ |
145 | __noinline int exception_cb_mod(u64 cookie) |
146 | { |
147 | return exception_cb_mod_global(cookie) + cookie + 10; |
148 | } |
149 | |
150 | SEC("tc" ) |
151 | __exception_cb(exception_cb_mod) |
152 | int exception_ext_mod_cb_runtime(struct __sk_buff *ctx) |
153 | { |
154 | bpf_throw(25); |
155 | return 0; |
156 | } |
157 | |
158 | __noinline static int subprog(struct __sk_buff *ctx) |
159 | { |
160 | return bpf_ktime_get_ns(); |
161 | } |
162 | |
163 | __noinline static int throwing_subprog(struct __sk_buff *ctx) |
164 | { |
165 | if (ctx->tstamp) |
166 | bpf_throw(0); |
167 | return bpf_ktime_get_ns(); |
168 | } |
169 | |
170 | __noinline int global_subprog(struct __sk_buff *ctx) |
171 | { |
172 | return bpf_ktime_get_ns(); |
173 | } |
174 | |
175 | __noinline int throwing_global_subprog(struct __sk_buff *ctx) |
176 | { |
177 | if (ctx->tstamp) |
178 | bpf_throw(0); |
179 | return bpf_ktime_get_ns(); |
180 | } |
181 | |
182 | SEC("tc" ) |
183 | int exception_throw_subprog(struct __sk_buff *ctx) |
184 | { |
185 | switch (ctx->protocol) { |
186 | case 1: |
187 | return subprog(ctx); |
188 | case 2: |
189 | return global_subprog(ctx); |
190 | case 3: |
191 | return throwing_subprog(ctx); |
192 | case 4: |
193 | return throwing_global_subprog(ctx); |
194 | default: |
195 | break; |
196 | } |
197 | bpf_throw(1); |
198 | return 0; |
199 | } |
200 | |
201 | __noinline int assert_nz_gfunc(u64 c) |
202 | { |
203 | volatile u64 cookie = c; |
204 | |
205 | bpf_assert(cookie != 0); |
206 | return 0; |
207 | } |
208 | |
209 | __noinline int assert_zero_gfunc(u64 c) |
210 | { |
211 | volatile u64 cookie = c; |
212 | |
213 | bpf_assert(bpf_cmp_unlikely(cookie, ==, 0)); |
214 | return 0; |
215 | } |
216 | |
217 | __noinline int assert_neg_gfunc(s64 c) |
218 | { |
219 | volatile s64 cookie = c; |
220 | |
221 | bpf_assert(bpf_cmp_unlikely(cookie, <, 0)); |
222 | return 0; |
223 | } |
224 | |
225 | __noinline int assert_pos_gfunc(s64 c) |
226 | { |
227 | volatile s64 cookie = c; |
228 | |
229 | bpf_assert(bpf_cmp_unlikely(cookie, >, 0)); |
230 | return 0; |
231 | } |
232 | |
233 | __noinline int assert_negeq_gfunc(s64 c) |
234 | { |
235 | volatile s64 cookie = c; |
236 | |
237 | bpf_assert(bpf_cmp_unlikely(cookie, <=, -1)); |
238 | return 0; |
239 | } |
240 | |
241 | __noinline int assert_poseq_gfunc(s64 c) |
242 | { |
243 | volatile s64 cookie = c; |
244 | |
245 | bpf_assert(bpf_cmp_unlikely(cookie, >=, 1)); |
246 | return 0; |
247 | } |
248 | |
249 | __noinline int assert_nz_gfunc_with(u64 c) |
250 | { |
251 | volatile u64 cookie = c; |
252 | |
253 | bpf_assert_with(cookie != 0, cookie + 100); |
254 | return 0; |
255 | } |
256 | |
257 | __noinline int assert_zero_gfunc_with(u64 c) |
258 | { |
259 | volatile u64 cookie = c; |
260 | |
261 | bpf_assert_with(bpf_cmp_unlikely(cookie, ==, 0), cookie + 100); |
262 | return 0; |
263 | } |
264 | |
265 | __noinline int assert_neg_gfunc_with(s64 c) |
266 | { |
267 | volatile s64 cookie = c; |
268 | |
269 | bpf_assert_with(bpf_cmp_unlikely(cookie, <, 0), cookie + 100); |
270 | return 0; |
271 | } |
272 | |
273 | __noinline int assert_pos_gfunc_with(s64 c) |
274 | { |
275 | volatile s64 cookie = c; |
276 | |
277 | bpf_assert_with(bpf_cmp_unlikely(cookie, >, 0), cookie + 100); |
278 | return 0; |
279 | } |
280 | |
281 | __noinline int assert_negeq_gfunc_with(s64 c) |
282 | { |
283 | volatile s64 cookie = c; |
284 | |
285 | bpf_assert_with(bpf_cmp_unlikely(cookie, <=, -1), cookie + 100); |
286 | return 0; |
287 | } |
288 | |
289 | __noinline int assert_poseq_gfunc_with(s64 c) |
290 | { |
291 | volatile s64 cookie = c; |
292 | |
293 | bpf_assert_with(bpf_cmp_unlikely(cookie, >=, 1), cookie + 100); |
294 | return 0; |
295 | } |
296 | |
297 | #define check_assert(name, cookie, tag) \ |
298 | SEC("tc") \ |
299 | int exception##tag##name(struct __sk_buff *ctx) \ |
300 | { \ |
301 | return name(cookie) + 1; \ |
302 | } |
303 | |
304 | check_assert(assert_nz_gfunc, 5, _); |
305 | check_assert(assert_zero_gfunc, 0, _); |
306 | check_assert(assert_neg_gfunc, -100, _); |
307 | check_assert(assert_pos_gfunc, 100, _); |
308 | check_assert(assert_negeq_gfunc, -1, _); |
309 | check_assert(assert_poseq_gfunc, 1, _); |
310 | |
311 | check_assert(assert_nz_gfunc_with, 5, _); |
312 | check_assert(assert_zero_gfunc_with, 0, _); |
313 | check_assert(assert_neg_gfunc_with, -100, _); |
314 | check_assert(assert_pos_gfunc_with, 100, _); |
315 | check_assert(assert_negeq_gfunc_with, -1, _); |
316 | check_assert(assert_poseq_gfunc_with, 1, _); |
317 | |
318 | check_assert(assert_nz_gfunc, 0, _bad_); |
319 | check_assert(assert_zero_gfunc, 5, _bad_); |
320 | check_assert(assert_neg_gfunc, 100, _bad_); |
321 | check_assert(assert_pos_gfunc, -100, _bad_); |
322 | check_assert(assert_negeq_gfunc, 1, _bad_); |
323 | check_assert(assert_poseq_gfunc, -1, _bad_); |
324 | |
325 | check_assert(assert_nz_gfunc_with, 0, _bad_); |
326 | check_assert(assert_zero_gfunc_with, 5, _bad_); |
327 | check_assert(assert_neg_gfunc_with, 100, _bad_); |
328 | check_assert(assert_pos_gfunc_with, -100, _bad_); |
329 | check_assert(assert_negeq_gfunc_with, 1, _bad_); |
330 | check_assert(assert_poseq_gfunc_with, -1, _bad_); |
331 | |
332 | SEC("tc" ) |
333 | int exception_assert_range(struct __sk_buff *ctx) |
334 | { |
335 | u64 time = bpf_ktime_get_ns(); |
336 | |
337 | bpf_assert_range(time, 0, ~0ULL); |
338 | return 1; |
339 | } |
340 | |
341 | SEC("tc" ) |
342 | int exception_assert_range_with(struct __sk_buff *ctx) |
343 | { |
344 | u64 time = bpf_ktime_get_ns(); |
345 | |
346 | bpf_assert_range_with(time, 0, ~0ULL, 10); |
347 | return 1; |
348 | } |
349 | |
350 | SEC("tc" ) |
351 | int exception_bad_assert_range(struct __sk_buff *ctx) |
352 | { |
353 | u64 time = bpf_ktime_get_ns(); |
354 | |
355 | bpf_assert_range(time, -100, 100); |
356 | return 1; |
357 | } |
358 | |
359 | SEC("tc" ) |
360 | int exception_bad_assert_range_with(struct __sk_buff *ctx) |
361 | { |
362 | u64 time = bpf_ktime_get_ns(); |
363 | |
364 | bpf_assert_range_with(time, -1000, 1000, 10); |
365 | return 1; |
366 | } |
367 | |
368 | char _license[] SEC("license" ) = "GPL" ; |
369 | |