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