Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'allow-variable-offsets-for-syscall-ptr_to_ctx'

Kumar Kartikeya Dwivedi says:

====================
Allow variable offsets for syscall PTR_TO_CTX

Enable pointer modification with variable offsets accumulated in the
register for PTR_TO_CTX for syscall programs where it won't be
rewritten, and the context is user-supplied and checked against the max
offset. See patches for details. Fixed offset support landed in [0].

By combining this set with [0], examples like the one below should
succeed verification now.

SEC("syscall")
int prog(void *ctx) {
int *arr = ctx;
int i;

bpf_for(i, 0, 100)
arr[i] *= i;

return 0;
}

[0]: https://lore.kernel.org/bpf/20260227005725.1247305-1-memxor@gmail.com

Changelog:
----------
v4 -> v5
v4: https://lore.kernel.org/bpf/20260401122818.2240807-1-memxor@gmail.com

* Use is_var_ctx_off_allowed() consistently.
* Add acks. (Emil)

v3 -> v4
v3: https://lore.kernel.org/bpf/20260318103526.2590079-1-memxor@gmail.com

* Drop comment around describing choice of fixed or variable offsets. (Eduard)
* Simplify offset adjustment for different cases. (Eduard)
* Add PTR_TO_CTX case in __check_mem_access(). (Eduard)
* Drop aligned access constraint from syscall_prog_is_valid_access().
* Wrap naked checks for BPF_PROG_TYPE_SYSCALL in a utility function. (Eduard)
* Split tests into separate clean up and addition patches. (Eduard)
* Remove CAP_SYS_ADMIN changes. (Eduard)
* Enable unaligned access to syscall ctx, add tests.
* Add more tests for various corner cases.
* Add acks. (Puranjay, Mykyta)

v2 -> v3
v2: https://lore.kernel.org/bpf/20260318075133.1031781-1-memxor@gmail.com

* Prevent arg_type for KF_ARG_PTR_TO_CTX from applying to other cases
due to preceding fallthrough. (Gemini/Sashiko)

v1 -> v2
v1: https://lore.kernel.org/bpf/20260317111850.2107846-2-memxor@gmail.com

* Harden check_func_arg_reg_off check with ARG_PTR_TO_CTX.
* Add tests for unmodified ctx into tail calls.
* Squash unmodified ctx change into base commit.
* Add Reviewed-by's from Emil.
====================

Link: https://patch.msgid.link/20260406194403.1649608-1-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+703 -93
+1 -2
kernel/bpf/syscall.c
··· 6386 6386 { 6387 6387 if (off < 0 || off >= U16_MAX) 6388 6388 return false; 6389 - if (off % size != 0) 6390 - return false; 6389 + /* No alignment requirements for syscall ctx accesses. */ 6391 6390 return true; 6392 6391 } 6393 6392
+65 -38
kernel/bpf/verifier.c
··· 5983 5983 verbose(env, "invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n", 5984 5984 off, size, regno, reg->id, off, mem_size); 5985 5985 break; 5986 + case PTR_TO_CTX: 5987 + verbose(env, "invalid access to context, ctx_size=%d off=%d size=%d\n", 5988 + mem_size, off, size); 5989 + break; 5986 5990 case PTR_TO_MEM: 5987 5991 default: 5988 5992 verbose(env, "invalid access to memory, mem_size=%u off=%d size=%d\n", ··· 6480 6476 return 0; 6481 6477 } 6482 6478 6479 + static bool is_var_ctx_off_allowed(struct bpf_prog *prog) 6480 + { 6481 + return resolve_prog_type(prog) == BPF_PROG_TYPE_SYSCALL; 6482 + } 6483 + 6483 6484 /* check access to 'struct bpf_context' fields. Supports fixed offsets only */ 6484 - static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, 6485 - enum bpf_access_type t, struct bpf_insn_access_aux *info) 6485 + static int __check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, 6486 + enum bpf_access_type t, struct bpf_insn_access_aux *info) 6486 6487 { 6487 6488 if (env->ops->is_valid_access && 6488 6489 env->ops->is_valid_access(off, size, t, env->prog, info)) { ··· 6516 6507 6517 6508 verbose(env, "invalid bpf_context access off=%d size=%d\n", off, size); 6518 6509 return -EACCES; 6510 + } 6511 + 6512 + static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, u32 regno, 6513 + int off, int access_size, enum bpf_access_type t, 6514 + struct bpf_insn_access_aux *info) 6515 + { 6516 + /* 6517 + * Program types that don't rewrite ctx accesses can safely 6518 + * dereference ctx pointers with fixed offsets. 6519 + */ 6520 + bool var_off_ok = is_var_ctx_off_allowed(env->prog); 6521 + bool fixed_off_ok = !env->ops->convert_ctx_access; 6522 + struct bpf_reg_state *regs = cur_regs(env); 6523 + struct bpf_reg_state *reg = regs + regno; 6524 + int err; 6525 + 6526 + if (var_off_ok) 6527 + err = check_mem_region_access(env, regno, off, access_size, U16_MAX, false); 6528 + else 6529 + err = __check_ptr_off_reg(env, reg, regno, fixed_off_ok); 6530 + if (err) 6531 + return err; 6532 + off += reg->umax_value; 6533 + 6534 + err = __check_ctx_access(env, insn_idx, off, access_size, t, info); 6535 + if (err) 6536 + verbose_linfo(env, insn_idx, "; "); 6537 + return err; 6519 6538 } 6520 6539 6521 6540 static int check_flow_keys_access(struct bpf_verifier_env *env, int off, ··· 7976 7939 if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem)) 7977 7940 mark_reg_unknown(env, regs, value_regno); 7978 7941 } else if (reg->type == PTR_TO_CTX) { 7979 - /* 7980 - * Program types that don't rewrite ctx accesses can safely 7981 - * dereference ctx pointers with fixed offsets. 7982 - */ 7983 - bool fixed_off_ok = !env->ops->convert_ctx_access; 7984 - struct bpf_retval_range range; 7985 7942 struct bpf_insn_access_aux info = { 7986 7943 .reg_type = SCALAR_VALUE, 7987 7944 .is_ldsx = is_ldsx, 7988 7945 .log = &env->log, 7989 7946 }; 7947 + struct bpf_retval_range range; 7990 7948 7991 7949 if (t == BPF_WRITE && value_regno >= 0 && 7992 7950 is_pointer_value(env, value_regno)) { ··· 7989 7957 return -EACCES; 7990 7958 } 7991 7959 7992 - err = __check_ptr_off_reg(env, reg, regno, fixed_off_ok); 7993 - if (err < 0) 7994 - return err; 7995 - 7996 - /* 7997 - * Fold the register's constant offset into the insn offset so 7998 - * that is_valid_access() sees the true effective offset. 7999 - */ 8000 - if (fixed_off_ok) 8001 - off += reg->var_off.value; 8002 - err = check_ctx_access(env, insn_idx, off, size, t, &info); 8003 - if (err) 8004 - verbose_linfo(env, insn_idx, "; "); 7960 + err = check_ctx_access(env, insn_idx, regno, off, size, t, &info); 8005 7961 if (!err && t == BPF_READ && value_regno >= 0) { 8006 7962 /* ctx access returns either a scalar, or a 8007 7963 * PTR_TO_PACKET[_META,_END]. In the latter ··· 8563 8543 return check_ptr_to_btf_access(env, regs, regno, 0, 8564 8544 access_size, BPF_READ, -1); 8565 8545 case PTR_TO_CTX: 8566 - /* in case the function doesn't know how to access the context, 8567 - * (because we are in a program of type SYSCALL for example), we 8568 - * can not statically check its size. 8569 - * Dynamically check it now. 8570 - */ 8571 - if (!env->ops->convert_ctx_access) { 8572 - int offset = access_size - 1; 8573 - 8574 - /* Allow zero-byte read from PTR_TO_CTX */ 8575 - if (access_size == 0) 8576 - return zero_size_allowed ? 0 : -EACCES; 8577 - 8578 - return check_mem_access(env, env->insn_idx, regno, offset, BPF_B, 8579 - access_type, -1, false, false); 8546 + /* Only permit reading or writing syscall context using helper calls. */ 8547 + if (is_var_ctx_off_allowed(env->prog)) { 8548 + int err = check_mem_region_access(env, regno, 0, access_size, U16_MAX, 8549 + zero_size_allowed); 8550 + if (err) 8551 + return err; 8552 + if (env->prog->aux->max_ctx_offset < reg->umax_value + access_size) 8553 + env->prog->aux->max_ctx_offset = reg->umax_value + access_size; 8554 + return 0; 8580 8555 } 8581 - 8582 8556 fallthrough; 8583 8557 default: /* scalar_value or invalid ptr */ 8584 8558 /* Allow zero-byte read from NULL, regardless of pointer type */ ··· 9516 9502 PTR_TO_MEM | MEM_RINGBUF, 9517 9503 PTR_TO_BUF, 9518 9504 PTR_TO_BTF_ID | PTR_TRUSTED, 9505 + PTR_TO_CTX, 9519 9506 }, 9520 9507 }; 9521 9508 ··· 9826 9811 * still need to do checks instead of returning. 9827 9812 */ 9828 9813 return __check_ptr_off_reg(env, reg, regno, true); 9814 + case PTR_TO_CTX: 9815 + /* 9816 + * Allow fixed and variable offsets for syscall context, but 9817 + * only when the argument is passed as memory, not ctx, 9818 + * otherwise we may get modified ctx in tail called programs and 9819 + * global subprogs (that may act as extension prog hooks). 9820 + */ 9821 + if (arg_type != ARG_PTR_TO_CTX && is_var_ctx_off_allowed(env->prog)) 9822 + return 0; 9823 + fallthrough; 9829 9824 default: 9830 9825 return __check_ptr_off_reg(env, reg, regno, false); 9831 9826 } ··· 10883 10858 * invalid memory access. 10884 10859 */ 10885 10860 } else if (arg->arg_type == ARG_PTR_TO_CTX) { 10886 - ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE); 10861 + ret = check_func_arg_reg_off(env, reg, regno, ARG_PTR_TO_CTX); 10887 10862 if (ret < 0) 10888 10863 return ret; 10889 10864 /* If function expects ctx type in BTF check that caller ··· 13757 13732 } 13758 13733 } 13759 13734 fallthrough; 13760 - case KF_ARG_PTR_TO_CTX: 13761 13735 case KF_ARG_PTR_TO_DYNPTR: 13762 13736 case KF_ARG_PTR_TO_ITER: 13763 13737 case KF_ARG_PTR_TO_LIST_HEAD: ··· 13773 13749 case KF_ARG_PTR_TO_TASK_WORK: 13774 13750 case KF_ARG_PTR_TO_IRQ_FLAG: 13775 13751 case KF_ARG_PTR_TO_RES_SPIN_LOCK: 13752 + break; 13753 + case KF_ARG_PTR_TO_CTX: 13754 + arg_type = ARG_PTR_TO_CTX; 13776 13755 break; 13777 13756 default: 13778 13757 verifier_bug(env, "unknown kfunc arg type %d", kf_arg_type);
+1 -1
tools/testing/selftests/bpf/prog_tests/verifier.c
··· 175 175 void test_verifier_cgroup_storage(void) { RUN(verifier_cgroup_storage); } 176 176 void test_verifier_const(void) { RUN(verifier_const); } 177 177 void test_verifier_const_or(void) { RUN(verifier_const_or); } 178 - void test_verifier_ctx(void) { RUN(verifier_ctx); } 178 + void test_verifier_ctx(void) { RUN_TESTS(verifier_ctx); } 179 179 void test_verifier_ctx_sk_msg(void) { RUN(verifier_ctx_sk_msg); } 180 180 void test_verifier_d_path(void) { RUN(verifier_d_path); } 181 181 void test_verifier_default_trusted_ptr(void) { RUN_TESTS(verifier_default_trusted_ptr); }
+541 -50
tools/testing/selftests/bpf/progs/verifier_ctx.c
··· 4 4 #include "vmlinux.h" 5 5 #include <bpf/bpf_helpers.h> 6 6 #include "bpf_misc.h" 7 + #include "../test_kmods/bpf_testmod_kfunc.h" 8 + 9 + static const char ctx_strncmp_target[] = "ctx"; 10 + static const char ctx_snprintf_fmt[] = ""; 7 11 8 12 SEC("tc") 9 13 __description("context stores via BPF_ATOMIC") ··· 73 69 SEC("socket") 74 70 __description("pass modified ctx pointer to helper, 2") 75 71 __failure __msg("negative offset ctx ptr R1 off=-612 disallowed") 76 - __failure_unpriv __msg_unpriv("negative offset ctx ptr R1 off=-612 disallowed") 77 72 __naked void ctx_pointer_to_helper_2(void) 78 73 { 79 74 asm volatile (" \ ··· 295 292 __failure __msg("invalid bpf_context access") 296 293 padding_access("sk_reuseport", sk_reuseport_md, hash, 4); 297 294 298 - SEC("syscall") 295 + SEC("?syscall") 299 296 __description("syscall: write to ctx with fixed offset") 300 297 __success 301 - __naked void syscall_ctx_fixed_off_write(void) 298 + int syscall_ctx_fixed_off_write(void *ctx) 302 299 { 303 - asm volatile (" \ 304 - r0 = 0; \ 305 - *(u32*)(r1 + 0) = r0; \ 306 - r1 += 4; \ 307 - *(u32*)(r1 + 0) = r0; \ 308 - exit; \ 309 - " ::: __clobber_all); 300 + char *p = ctx; 301 + 302 + *(__u32 *)p = 0; 303 + *(__u32 *)(p + 4) = 0; 304 + return 0; 305 + } 306 + 307 + SEC("?syscall") 308 + __description("syscall: read ctx with fixed offset") 309 + __success 310 + int syscall_ctx_fixed_off_read(void *ctx) 311 + { 312 + char *p = ctx; 313 + volatile __u32 val; 314 + 315 + val = *(__u32 *)(p + 4); 316 + (void)val; 317 + return 0; 318 + } 319 + 320 + SEC("?syscall") 321 + __description("syscall: unaligned read ctx with fixed offset") 322 + __success 323 + int syscall_ctx_unaligned_fixed_off_read(void *ctx) 324 + { 325 + char *p = ctx; 326 + volatile __u32 val; 327 + 328 + val = *(__u32 *)(p + 2); 329 + (void)val; 330 + return 0; 331 + } 332 + 333 + SEC("?syscall") 334 + __description("syscall: unaligned write ctx with fixed offset") 335 + __success 336 + int syscall_ctx_unaligned_fixed_off_write(void *ctx) 337 + { 338 + char *p = ctx; 339 + 340 + *(__u32 *)(p + 2) = 0; 341 + return 0; 342 + } 343 + 344 + SEC("?syscall") 345 + __description("syscall: read ctx with variable offset") 346 + __success 347 + int syscall_ctx_var_off_read(void *ctx) 348 + { 349 + __u64 off = bpf_get_prandom_u32(); 350 + char *p = ctx; 351 + volatile __u32 val; 352 + 353 + off &= 0xfc; 354 + p += off; 355 + val = *(__u32 *)p; 356 + (void)val; 357 + return 0; 358 + } 359 + 360 + SEC("?syscall") 361 + __description("syscall: write ctx with variable offset") 362 + __success 363 + int syscall_ctx_var_off_write(void *ctx) 364 + { 365 + __u64 off = bpf_get_prandom_u32(); 366 + char *p = ctx; 367 + 368 + off &= 0xfc; 369 + p += off; 370 + *(__u32 *)p = 0; 371 + return 0; 372 + } 373 + 374 + SEC("?syscall") 375 + __description("syscall: unaligned read ctx with variable offset") 376 + __success 377 + int syscall_ctx_unaligned_var_off_read(void *ctx) 378 + { 379 + __u64 off = bpf_get_prandom_u32(); 380 + char *p = ctx; 381 + volatile __u32 val; 382 + 383 + off &= 0xfc; 384 + off += 2; 385 + p += off; 386 + val = *(__u32 *)p; 387 + (void)val; 388 + return 0; 389 + } 390 + 391 + SEC("?syscall") 392 + __description("syscall: unaligned write ctx with variable offset") 393 + __success 394 + int syscall_ctx_unaligned_var_off_write(void *ctx) 395 + { 396 + __u64 off = bpf_get_prandom_u32(); 397 + char *p = ctx; 398 + 399 + off &= 0xfc; 400 + off += 2; 401 + p += off; 402 + *(__u32 *)p = 0; 403 + return 0; 404 + } 405 + 406 + SEC("?syscall") 407 + __description("syscall: reject ctx access past U16_MAX with fixed offset") 408 + __failure __msg("outside of the allowed memory range") 409 + int syscall_ctx_u16_max_fixed_off(void *ctx) 410 + { 411 + char *p = ctx; 412 + volatile __u32 val; 413 + 414 + p += 65535; 415 + val = *(__u32 *)p; 416 + (void)val; 417 + return 0; 418 + } 419 + 420 + SEC("?syscall") 421 + __description("syscall: reject ctx access past U16_MAX with variable offset") 422 + __failure __msg("outside of the allowed memory range") 423 + int syscall_ctx_u16_max_var_off(void *ctx) 424 + { 425 + __u64 off = bpf_get_prandom_u32(); 426 + char *p = ctx; 427 + volatile __u32 val; 428 + 429 + off &= 0xffff; 430 + off += 1; 431 + p += off; 432 + val = *(__u32 *)p; 433 + (void)val; 434 + return 0; 435 + } 436 + 437 + SEC("?syscall") 438 + __description("syscall: reject negative variable offset ctx access") 439 + __failure __msg("min value is negative") 440 + int syscall_ctx_neg_var_off(void *ctx) 441 + { 442 + __u64 off = bpf_get_prandom_u32(); 443 + char *p = ctx; 444 + 445 + off &= 4; 446 + p -= off; 447 + return *(__u32 *)p; 448 + } 449 + 450 + SEC("?syscall") 451 + __description("syscall: reject unbounded variable offset ctx access") 452 + __failure __msg("unbounded memory access") 453 + int syscall_ctx_unbounded_var_off(void *ctx) 454 + { 455 + __u64 off = (__u32)bpf_get_prandom_u32(); 456 + char *p = ctx; 457 + 458 + off <<= 2; 459 + p += off; 460 + return *(__u32 *)p; 461 + } 462 + 463 + SEC("?syscall") 464 + __description("syscall: helper read ctx with fixed offset") 465 + __success 466 + int syscall_ctx_helper_fixed_off_read(void *ctx) 467 + { 468 + char *p = ctx; 469 + 470 + p += 4; 471 + return bpf_strncmp(p, 4, ctx_strncmp_target); 472 + } 473 + 474 + SEC("?syscall") 475 + __description("syscall: helper write ctx with fixed offset") 476 + __success 477 + int syscall_ctx_helper_fixed_off_write(void *ctx) 478 + { 479 + char *p = ctx; 480 + 481 + p += 4; 482 + return bpf_probe_read_kernel(p, 4, 0); 483 + } 484 + 485 + SEC("?syscall") 486 + __description("syscall: helper unaligned read ctx with fixed offset") 487 + __success 488 + int syscall_ctx_helper_unaligned_fixed_off_read(void *ctx) 489 + { 490 + char *p = ctx; 491 + 492 + p += 2; 493 + return bpf_strncmp(p, 4, ctx_strncmp_target); 494 + } 495 + 496 + SEC("?syscall") 497 + __description("syscall: helper unaligned write ctx with fixed offset") 498 + __success 499 + int syscall_ctx_helper_unaligned_fixed_off_write(void *ctx) 500 + { 501 + char *p = ctx; 502 + 503 + p += 2; 504 + return bpf_probe_read_kernel(p, 4, 0); 505 + } 506 + 507 + SEC("?syscall") 508 + __description("syscall: helper read ctx with variable offset") 509 + __success 510 + int syscall_ctx_helper_var_off_read(void *ctx) 511 + { 512 + __u64 off = bpf_get_prandom_u32(); 513 + char *p = ctx; 514 + 515 + off &= 0xfc; 516 + p += off; 517 + return bpf_strncmp(p, 4, ctx_strncmp_target); 518 + } 519 + 520 + SEC("?syscall") 521 + __description("syscall: helper write ctx with variable offset") 522 + __success 523 + int syscall_ctx_helper_var_off_write(void *ctx) 524 + { 525 + __u64 off = bpf_get_prandom_u32(); 526 + char *p = ctx; 527 + 528 + off &= 0xfc; 529 + p += off; 530 + return bpf_probe_read_kernel(p, 4, 0); 531 + } 532 + 533 + SEC("?syscall") 534 + __description("syscall: helper unaligned read ctx with variable offset") 535 + __success 536 + int syscall_ctx_helper_unaligned_var_off_read(void *ctx) 537 + { 538 + __u64 off = bpf_get_prandom_u32(); 539 + char *p = ctx; 540 + 541 + off &= 0xfc; 542 + off += 2; 543 + p += off; 544 + return bpf_strncmp(p, 4, ctx_strncmp_target); 545 + } 546 + 547 + SEC("?syscall") 548 + __description("syscall: helper unaligned write ctx with variable offset") 549 + __success 550 + int syscall_ctx_helper_unaligned_var_off_write(void *ctx) 551 + { 552 + __u64 off = bpf_get_prandom_u32(); 553 + char *p = ctx; 554 + 555 + off &= 0xfc; 556 + off += 2; 557 + p += off; 558 + return bpf_probe_read_kernel(p, 4, 0); 559 + } 560 + 561 + SEC("?syscall") 562 + __description("syscall: reject helper read ctx past U16_MAX with fixed offset") 563 + __failure __msg("outside of the allowed memory range") 564 + int syscall_ctx_helper_u16_max_fixed_off_read(void *ctx) 565 + { 566 + char *p = ctx; 567 + 568 + p += 65535; 569 + return bpf_strncmp(p, 4, ctx_strncmp_target); 570 + } 571 + 572 + SEC("?syscall") 573 + __description("syscall: reject helper write ctx past U16_MAX with fixed offset") 574 + __failure __msg("outside of the allowed memory range") 575 + int syscall_ctx_helper_u16_max_fixed_off_write(void *ctx) 576 + { 577 + char *p = ctx; 578 + 579 + p += 65535; 580 + return bpf_probe_read_kernel(p, 4, 0); 581 + } 582 + 583 + SEC("?syscall") 584 + __description("syscall: reject helper read ctx past U16_MAX with variable offset") 585 + __failure __msg("outside of the allowed memory range") 586 + int syscall_ctx_helper_u16_max_var_off_read(void *ctx) 587 + { 588 + __u64 off = bpf_get_prandom_u32(); 589 + char *p = ctx; 590 + 591 + off &= 0xffff; 592 + off += 1; 593 + p += off; 594 + return bpf_strncmp(p, 4, ctx_strncmp_target); 595 + } 596 + 597 + SEC("?syscall") 598 + __description("syscall: reject helper write ctx past U16_MAX with variable offset") 599 + __failure __msg("outside of the allowed memory range") 600 + int syscall_ctx_helper_u16_max_var_off_write(void *ctx) 601 + { 602 + __u64 off = bpf_get_prandom_u32(); 603 + char *p = ctx; 604 + 605 + off &= 0xffff; 606 + off += 1; 607 + p += off; 608 + return bpf_probe_read_kernel(p, 4, 0); 609 + } 610 + 611 + SEC("?syscall") 612 + __description("syscall: helper read zero-sized ctx access") 613 + __success 614 + int syscall_ctx_helper_zero_sized_read(void *ctx) 615 + { 616 + return bpf_snprintf(0, 0, ctx_snprintf_fmt, ctx, 0); 617 + } 618 + 619 + SEC("?syscall") 620 + __description("syscall: helper write zero-sized ctx access") 621 + __success 622 + int syscall_ctx_helper_zero_sized_write(void *ctx) 623 + { 624 + return bpf_probe_read_kernel(ctx, 0, 0); 625 + } 626 + 627 + SEC("?syscall") 628 + __description("syscall: kfunc access ctx with fixed offset") 629 + __success 630 + int syscall_ctx_kfunc_fixed_off(void *ctx) 631 + { 632 + char *p = ctx; 633 + 634 + p += 4; 635 + bpf_kfunc_call_test_mem_len_pass1(p, 4); 636 + return 0; 637 + } 638 + 639 + SEC("?syscall") 640 + __description("syscall: kfunc access ctx with variable offset") 641 + __success 642 + int syscall_ctx_kfunc_var_off(void *ctx) 643 + { 644 + __u64 off = bpf_get_prandom_u32(); 645 + char *p = ctx; 646 + 647 + off &= 0xfc; 648 + p += off; 649 + bpf_kfunc_call_test_mem_len_pass1(p, 4); 650 + return 0; 651 + } 652 + 653 + SEC("?syscall") 654 + __description("syscall: kfunc unaligned access ctx with fixed offset") 655 + __success 656 + int syscall_ctx_kfunc_unaligned_fixed_off(void *ctx) 657 + { 658 + char *p = ctx; 659 + 660 + p += 2; 661 + bpf_kfunc_call_test_mem_len_pass1(p, 4); 662 + return 0; 663 + } 664 + 665 + SEC("?syscall") 666 + __description("syscall: kfunc unaligned access ctx with variable offset") 667 + __success 668 + int syscall_ctx_kfunc_unaligned_var_off(void *ctx) 669 + { 670 + __u64 off = bpf_get_prandom_u32(); 671 + char *p = ctx; 672 + 673 + off &= 0xfc; 674 + off += 2; 675 + p += off; 676 + bpf_kfunc_call_test_mem_len_pass1(p, 4); 677 + return 0; 678 + } 679 + 680 + SEC("?syscall") 681 + __description("syscall: reject kfunc ctx access past U16_MAX with fixed offset") 682 + __failure __msg("outside of the allowed memory range") 683 + int syscall_ctx_kfunc_u16_max_fixed_off(void *ctx) 684 + { 685 + char *p = ctx; 686 + 687 + p += 65535; 688 + bpf_kfunc_call_test_mem_len_pass1(p, 4); 689 + return 0; 690 + } 691 + 692 + SEC("?syscall") 693 + __description("syscall: reject kfunc ctx access past U16_MAX with variable offset") 694 + __failure __msg("outside of the allowed memory range") 695 + int syscall_ctx_kfunc_u16_max_var_off(void *ctx) 696 + { 697 + __u64 off = bpf_get_prandom_u32(); 698 + char *p = ctx; 699 + 700 + off &= 0xffff; 701 + off += 1; 702 + p += off; 703 + bpf_kfunc_call_test_mem_len_pass1(p, 4); 704 + return 0; 705 + } 706 + 707 + SEC("?syscall") 708 + __description("syscall: kfunc access zero-sized ctx") 709 + __success 710 + int syscall_ctx_kfunc_zero_sized(void *ctx) 711 + { 712 + bpf_kfunc_call_test_mem_len_pass1(ctx, 0); 713 + return 0; 310 714 } 311 715 312 716 /* 313 - * Test that program types without convert_ctx_access can dereference 314 - * their ctx pointer after adding a fixed offset. Variable and negative 315 - * offsets should still be rejected. 717 + * For non-syscall program types without convert_ctx_access, direct ctx 718 + * dereference is still allowed after adding a fixed offset, while variable 719 + * and negative direct accesses reject. 720 + * 721 + * Passing ctx as a helper or kfunc memory argument is only permitted for 722 + * syscall programs, so the helper and kfunc cases below validate rejection 723 + * for non-syscall ctx pointers at fixed, variable, and zero-sized accesses. 316 724 */ 317 - #define no_rewrite_ctx_access(type, name, off, ld_op) \ 318 - SEC(type) \ 725 + #define no_rewrite_ctx_access(type, name, off, load_t) \ 726 + SEC("?" type) \ 319 727 __description(type ": read ctx at fixed offset") \ 320 728 __success \ 321 - __naked void no_rewrite_##name##_fixed(void) \ 729 + int no_rewrite_##name##_fixed(void *ctx) \ 322 730 { \ 323 - asm volatile (" \ 324 - r1 += %[__off]; \ 325 - r0 = *(" #ld_op " *)(r1 + 0); \ 326 - r0 = 0; \ 327 - exit;" \ 328 - : \ 329 - : __imm_const(__off, off) \ 330 - : __clobber_all); \ 731 + char *p = ctx; \ 732 + volatile load_t val; \ 733 + \ 734 + val = *(load_t *)(p + off); \ 735 + (void)val; \ 736 + return 0; \ 331 737 } \ 332 - SEC(type) \ 738 + SEC("?" type) \ 333 739 __description(type ": reject variable offset ctx access") \ 334 740 __failure __msg("variable ctx access var_off=") \ 335 - __naked void no_rewrite_##name##_var(void) \ 741 + int no_rewrite_##name##_var(void *ctx) \ 336 742 { \ 337 - asm volatile (" \ 338 - r6 = r1; \ 339 - call %[bpf_get_prandom_u32]; \ 340 - r1 = r6; \ 341 - r0 &= 4; \ 342 - r1 += r0; \ 343 - r0 = *(" #ld_op " *)(r1 + 0); \ 344 - r0 = 0; \ 345 - exit;" \ 346 - : \ 347 - : __imm(bpf_get_prandom_u32) \ 348 - : __clobber_all); \ 743 + __u64 off_var = bpf_get_prandom_u32(); \ 744 + char *p = ctx; \ 745 + \ 746 + off_var &= 4; \ 747 + p += off_var; \ 748 + return *(load_t *)p; \ 349 749 } \ 350 - SEC(type) \ 750 + SEC("?" type) \ 351 751 __description(type ": reject negative offset ctx access") \ 352 - __failure __msg("negative offset ctx ptr") \ 353 - __naked void no_rewrite_##name##_neg(void) \ 752 + __failure __msg("invalid bpf_context access") \ 753 + int no_rewrite_##name##_neg(void *ctx) \ 354 754 { \ 355 - asm volatile (" \ 356 - r1 += %[__neg_off]; \ 357 - r0 = *(" #ld_op " *)(r1 + 0); \ 358 - r0 = 0; \ 359 - exit;" \ 360 - : \ 361 - : __imm_const(__neg_off, -(off)) \ 362 - : __clobber_all); \ 755 + char *p = ctx; \ 756 + \ 757 + p -= 612; \ 758 + return *(load_t *)p; \ 759 + } \ 760 + SEC("?" type) \ 761 + __description(type ": reject helper read ctx at fixed offset") \ 762 + __failure __msg("dereference of modified ctx ptr") \ 763 + int no_rewrite_##name##_helper_read_fixed(void *ctx) \ 764 + { \ 765 + char *p = ctx; \ 766 + \ 767 + p += off; \ 768 + return bpf_strncmp(p, 4, ctx_strncmp_target); \ 769 + } \ 770 + SEC("?" type) \ 771 + __description(type ": reject helper write ctx at fixed offset") \ 772 + __failure __msg("dereference of modified ctx ptr") \ 773 + int no_rewrite_##name##_helper_write_fixed(void *ctx) \ 774 + { \ 775 + char *p = ctx; \ 776 + \ 777 + p += off; \ 778 + return bpf_probe_read_kernel(p, 4, 0); \ 779 + } \ 780 + SEC("?" type) \ 781 + __description(type ": reject helper read ctx with variable offset") \ 782 + __failure __msg("variable ctx access var_off=") \ 783 + int no_rewrite_##name##_helper_read_var(void *ctx) \ 784 + { \ 785 + __u64 off_var = bpf_get_prandom_u32(); \ 786 + char *p = ctx; \ 787 + \ 788 + off_var &= 4; \ 789 + p += off_var; \ 790 + return bpf_strncmp(p, 4, ctx_strncmp_target); \ 791 + } \ 792 + SEC("?" type) \ 793 + __description(type ": reject helper write ctx with variable offset") \ 794 + __failure __msg("variable ctx access var_off=") \ 795 + int no_rewrite_##name##_helper_write_var(void *ctx) \ 796 + { \ 797 + __u64 off_var = bpf_get_prandom_u32(); \ 798 + char *p = ctx; \ 799 + \ 800 + off_var &= 4; \ 801 + p += off_var; \ 802 + return bpf_probe_read_kernel(p, 4, 0); \ 803 + } \ 804 + SEC("?" type) \ 805 + __description(type ": reject helper read zero-sized ctx access") \ 806 + __failure __msg("R4 type=ctx expected=fp") \ 807 + int no_rewrite_##name##_helper_read_zero(void *ctx) \ 808 + { \ 809 + return bpf_snprintf(0, 0, ctx_snprintf_fmt, ctx, 0); \ 810 + } \ 811 + SEC("?" type) \ 812 + __description(type ": reject helper write zero-sized ctx access") \ 813 + __failure __msg("R1 type=ctx expected=fp") \ 814 + int no_rewrite_##name##_helper_write_zero(void *ctx) \ 815 + { \ 816 + return bpf_probe_read_kernel(ctx, 0, 0); \ 817 + } \ 818 + SEC("?" type) \ 819 + __description(type ": reject kfunc ctx at fixed offset") \ 820 + __failure __msg("dereference of modified ctx ptr") \ 821 + int no_rewrite_##name##_kfunc_fixed(void *ctx) \ 822 + { \ 823 + char *p = ctx; \ 824 + \ 825 + p += off; \ 826 + bpf_kfunc_call_test_mem_len_pass1(p, 4); \ 827 + return 0; \ 828 + } \ 829 + SEC("?" type) \ 830 + __description(type ": reject kfunc ctx with variable offset") \ 831 + __failure __msg("variable ctx access var_off=") \ 832 + int no_rewrite_##name##_kfunc_var(void *ctx) \ 833 + { \ 834 + __u64 off_var = bpf_get_prandom_u32(); \ 835 + char *p = ctx; \ 836 + \ 837 + off_var &= 4; \ 838 + p += off_var; \ 839 + bpf_kfunc_call_test_mem_len_pass1(p, 4); \ 840 + return 0; \ 841 + } \ 842 + SEC("?" type) \ 843 + __description(type ": reject kfunc zero-sized ctx access") \ 844 + __failure __msg("R1 type=ctx expected=fp") \ 845 + int no_rewrite_##name##_kfunc_zero(void *ctx) \ 846 + { \ 847 + bpf_kfunc_call_test_mem_len_pass1(ctx, 0); \ 848 + return 0; \ 363 849 } 364 850 365 - no_rewrite_ctx_access("syscall", syscall, 4, u32); 366 851 no_rewrite_ctx_access("kprobe", kprobe, 8, u64); 367 852 no_rewrite_ctx_access("tracepoint", tp, 8, u64); 368 853 no_rewrite_ctx_access("raw_tp", raw_tp, 8, u64);
+94 -1
tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
··· 134 134 135 135 SEC("?tracepoint") 136 136 __failure __log_level(2) 137 - __msg("invalid bpf_context access") 138 137 __msg("Caller passes invalid args into func#1 ('subprog_user_anon_mem')") 139 138 int anon_user_mem_invalid(void *ctx) 140 139 { ··· 355 356 int arg_tag_ctx_syscall(void *ctx) 356 357 { 357 358 return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx) + tp_whatever(ctx); 359 + } 360 + 361 + __weak int syscall_array_bpf_for(void *ctx __arg_ctx) 362 + { 363 + int *arr = ctx; 364 + int i; 365 + 366 + bpf_for(i, 0, 100) 367 + arr[i] *= i; 368 + 369 + return 0; 370 + } 371 + 372 + SEC("?syscall") 373 + __success __log_level(2) 374 + int arg_tag_ctx_syscall_bpf_for(void *ctx) 375 + { 376 + return syscall_array_bpf_for(ctx); 377 + } 378 + 379 + SEC("syscall") 380 + __auxiliary 381 + int syscall_tailcall_target(void *ctx) 382 + { 383 + return syscall_array_bpf_for(ctx); 384 + } 385 + 386 + struct { 387 + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 388 + __uint(max_entries, 1); 389 + __uint(key_size, sizeof(__u32)); 390 + __array(values, int (void *)); 391 + } syscall_prog_array SEC(".maps") = { 392 + .values = { 393 + [0] = (void *)&syscall_tailcall_target, 394 + }, 395 + }; 396 + 397 + SEC("?syscall") 398 + __success __log_level(2) 399 + int arg_tag_ctx_syscall_tailcall(void *ctx) 400 + { 401 + bpf_tail_call(ctx, &syscall_prog_array, 0); 402 + return 0; 403 + } 404 + 405 + SEC("?syscall") 406 + __failure __log_level(2) 407 + __msg("dereference of modified ctx ptr R1 off=8 disallowed") 408 + int arg_tag_ctx_syscall_tailcall_fixed_off_bad(void *ctx) 409 + { 410 + char *p = ctx; 411 + 412 + p += 8; 413 + bpf_tail_call(p, &syscall_prog_array, 0); 414 + return 0; 415 + } 416 + 417 + SEC("?syscall") 418 + __failure __log_level(2) 419 + __msg("variable ctx access var_off=(0x0; 0x4) disallowed") 420 + int arg_tag_ctx_syscall_tailcall_var_off_bad(void *ctx) 421 + { 422 + __u64 off = bpf_get_prandom_u32(); 423 + char *p = ctx; 424 + 425 + off &= 4; 426 + p += off; 427 + bpf_tail_call(p, &syscall_prog_array, 0); 428 + return 0; 429 + } 430 + 431 + SEC("?syscall") 432 + __failure __log_level(2) 433 + __msg("dereference of modified ctx ptr R1 off=8 disallowed") 434 + int arg_tag_ctx_syscall_fixed_off_bad(void *ctx) 435 + { 436 + char *p = ctx; 437 + 438 + p += 8; 439 + return subprog_ctx_tag(p); 440 + } 441 + 442 + SEC("?syscall") 443 + __failure __log_level(2) 444 + __msg("variable ctx access var_off=(0x0; 0x4) disallowed") 445 + int arg_tag_ctx_syscall_var_off_bad(void *ctx) 446 + { 447 + __u64 off = bpf_get_prandom_u32(); 448 + char *p = ctx; 449 + 450 + off &= 4; 451 + p += off; 452 + return subprog_ctx_tag(p); 358 453 } 359 454 360 455 __weak int subprog_dynptr(struct bpf_dynptr *dptr)
+1 -1
tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
··· 723 723 BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY) 724 724 BTF_ID_FLAGS(func, bpf_iter_testmod_seq_value) 725 725 BTF_ID_FLAGS(func, bpf_kfunc_common_test) 726 + BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_pass1) 726 727 BTF_ID_FLAGS(func, bpf_kfunc_dynptr_test) 727 728 BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_nonzero_offset_test, KF_ACQUIRE) 728 729 BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_zero_offset_test, KF_ACQUIRE) ··· 1288 1287 BTF_ID_FLAGS(func, bpf_kfunc_call_test3) 1289 1288 BTF_ID_FLAGS(func, bpf_kfunc_call_test4) 1290 1289 BTF_ID_FLAGS(func, bpf_kfunc_call_test5) 1291 - BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_pass1) 1292 1290 BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_fail1) 1293 1291 BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_fail2) 1294 1292 BTF_ID_FLAGS(func, bpf_kfunc_call_test_acquire, KF_ACQUIRE | KF_RET_NULL)