| @@ -20,6 +20,8 @@ · 19 unmodified lines |
| 20 | 20 | #include <linux/file.h> |
| 21 | 21 | #include <linux/vmalloc.h> |
| 22 | 22 | #include <linux/stringify.h> |
| 23 | +#include <linux/bsearch.h> |
| 24 | +#include <linux/sort.h> |
| 23 | 25 | |
| 24 | 26 | #include "disasm.h" |
| 25 | 27 | |
| @@ -636,6 +638,113 @@ enum reg_arg_type { · 610 unmodified lines |
| 636 | 638 | DST_OP_NO_MARK /* same as above, check only, don't mark */ |
| 637 | 639 | }; |
| 638 | 640 | |
| 641 | +static int cmp_subprogs(const void *a, const void *b) |
| 642 | +{ |
| 643 | + return *(int *)a - *(int *)b; |
| 644 | +} |
| 645 | + |
| 646 | +static int find_subprog(struct bpf_verifier_env *env, int off) |
| 647 | +{ |
| 648 | + u32 *p; |
| 649 | + |
| 650 | + p = bsearch(&off, env->subprog_starts, env->subprog_cnt, |
| 651 | + sizeof(env->subprog_starts[0]), cmp_subprogs); |
| 652 | + if (!p) |
| 653 | + return -ENOENT; |
| 654 | + return p - env->subprog_starts; |
| 655 | + |
| 656 | +} |
| 657 | + |
| 658 | +static int add_subprog(struct bpf_verifier_env *env, int off) |
| 659 | +{ |
| 660 | + int insn_cnt = env->prog->len; |
| 661 | + int ret; |
| 662 | + |
| 663 | + if (off >= insn_cnt || off < 0) { |
| 664 | + verbose(env, "call to invalid destination\n"); |
| 665 | + return -EINVAL; |
| 666 | + } |
| 667 | + ret = find_subprog(env, off); |
| 668 | + if (ret >= 0) |
| 669 | + return 0; |
| 670 | + if (env->subprog_cnt >= BPF_MAX_SUBPROGS) { |
| 671 | + verbose(env, "too many subprograms\n"); |
| 672 | + return -E2BIG; |
| 673 | + } |
| 674 | + env->subprog_starts[env->subprog_cnt++] = off; |
| 675 | + sort(env->subprog_starts, env->subprog_cnt, |
| 676 | + sizeof(env->subprog_starts[0]), cmp_subprogs, NULL); |
| 677 | + return 0; |
| 678 | +} |
| 679 | + |
| 680 | +static int check_subprogs(struct bpf_verifier_env *env) |
| 681 | +{ |
| 682 | + int i, ret, subprog_start, subprog_end, off, cur_subprog = 0; |
| 683 | + struct bpf_insn *insn = env->prog->insnsi; |
| 684 | + int insn_cnt = env->prog->len; |
| 685 | + |
| 686 | + /* determine subprog starts. The end is one before the next starts */ |
| 687 | + for (i = 0; i < insn_cnt; i++) { |
| 688 | + if (insn[i].code != (BPF_JMP | BPF_CALL)) |
| 689 | + continue; |
| 690 | + if (insn[i].src_reg != BPF_PSEUDO_CALL) |
| 691 | + continue; |
| 692 | + if (!env->allow_ptr_leaks) { |
| 693 | + verbose(env, "function calls to other bpf functions are allowed for root only\n"); |
| 694 | + return -EPERM; |
| 695 | + } |
| 696 | + if (bpf_prog_is_dev_bound(env->prog->aux)) { |
| 697 | + verbose(env, "funcation calls in offloaded programs are not supported yet\n"); |
| 698 | + return -EINVAL; |
| 699 | + } |
| 700 | + ret = add_subprog(env, i + insn[i].imm + 1); |
| 701 | + if (ret < 0) |
| 702 | + return ret; |
| 703 | + } |
| 704 | + |
| 705 | + if (env->log.level > 1) |
| 706 | + for (i = 0; i < env->subprog_cnt; i++) |
| 707 | + verbose(env, "func#%d @%d\n", i, env->subprog_starts[i]); |
| 708 | + |
| 709 | + /* now check that all jumps are within the same subprog */ |
| 710 | + subprog_start = 0; |
| 711 | + if (env->subprog_cnt == cur_subprog) |
| 712 | + subprog_end = insn_cnt; |
| 713 | + else |
| 714 | + subprog_end = env->subprog_starts[cur_subprog++]; |
| 715 | + for (i = 0; i < insn_cnt; i++) { |
| 716 | + u8 code = insn[i].code; |
| 717 | + |
| 718 | + if (BPF_CLASS(code) != BPF_JMP) |
| 719 | + goto next; |
| 720 | + if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL) |
| 721 | + goto next; |
| 722 | + off = i + insn[i].off + 1; |
| 723 | + if (off < subprog_start || off >= subprog_end) { |
| 724 | + verbose(env, "jump out of range from insn %d to %d\n", i, off); |
| 725 | + return -EINVAL; |
| 726 | + } |
| 727 | +next: |
| 728 | + if (i == subprog_end - 1) { |
| 729 | + /* to avoid fall-through from one subprog into another |
| 730 | + * the last insn of the subprog should be either exit |
| 731 | + * or unconditional jump back |
| 732 | + */ |
| 733 | + if (code != (BPF_JMP | BPF_EXIT) && |
| 734 | + code != (BPF_JMP | BPF_JA)) { |
| 735 | + verbose(env, "last insn is not an exit or jmp\n"); |
| 736 | + return -EINVAL; |
| 737 | + } |
| 738 | + subprog_start = subprog_end; |
| 739 | + if (env->subprog_cnt == cur_subprog) |
| 740 | + subprog_end = insn_cnt; |
| 741 | + else |
| 742 | + subprog_end = env->subprog_starts[cur_subprog++]; |
| 743 | + } |
| 744 | + } |
| 745 | + return 0; |
| 746 | +} |
| 747 | + |
| 639 | 748 | static void mark_reg_read(const struct bpf_verifier_state *state, u32 regno) |
| 640 | 749 | { |
| 641 | 750 | struct bpf_verifier_state *parent = state->parent; |
| @@ -3284,6 +3393,10 @@ static int check_cfg(struct bpf_verifier_env *env) · 2642 unmodified lines |
| 3284 | 3393 | int ret = 0; |
| 3285 | 3394 | int i, t; |
| 3286 | 3395 | |
| 3396 | + ret = check_subprogs(env); |
| 3397 | + if (ret < 0) |
| 3398 | + return ret; |
| 3399 | + |
| 3287 | 3400 | insn_state = kcalloc(insn_cnt, sizeof(int), GFP_KERNEL); |
| 3288 | 3401 | if (!insn_state) |
| 3289 | 3402 | return -ENOMEM; |
| @@ -3316,6 +3429,14 @@ peek_stack: · 26 unmodified lines |
| 3316 | 3429 | goto err_free; |
| 3317 | 3430 | if (t + 1 < insn_cnt) |
| 3318 | 3431 | env->explored_states[t + 1] = STATE_LIST_MARK; |
| 3432 | + if (insns[t].src_reg == BPF_PSEUDO_CALL) { |
| 3433 | + env->explored_states[t] = STATE_LIST_MARK; |
| 3434 | + ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env); |
| 3435 | + if (ret == 1) |
| 3436 | + goto peek_stack; |
| 3437 | + else if (ret < 0) |
| 3438 | + goto err_free; |
| 3439 | + } |
| 3319 | 3440 | } else if (opcode == BPF_JA) { |
| 3320 | 3441 | if (BPF_SRC(insns[t].code) != BPF_K) { |
| 3321 | 3442 | ret = -EINVAL; |
| @@ -4245,6 +4366,19 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, · 923 unmodified lines |
| 4245 | 4366 | return 0; |
| 4246 | 4367 | } |
| 4247 | 4368 | |
| 4369 | +static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len) |
| 4370 | +{ |
| 4371 | + int i; |
| 4372 | + |
| 4373 | + if (len == 1) |
| 4374 | + return; |
| 4375 | + for (i = 0; i < env->subprog_cnt; i++) { |
| 4376 | + if (env->subprog_starts[i] < off) |
| 4377 | + continue; |
| 4378 | + env->subprog_starts[i] += len - 1; |
| 4379 | + } |
| 4380 | +} |
| 4381 | + |
| 4248 | 4382 | static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, |
| 4249 | 4383 | const struct bpf_insn *patch, u32 len) |
| 4250 | 4384 | { |
| @@ -4255,6 +4389,7 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of · 4 unmodified lines |
| 4255 | 4389 | return NULL; |
| 4256 | 4390 | if (adjust_insn_aux_data(env, new_prog->len, off, len)) |
| 4257 | 4391 | return NULL; |
| 4392 | + adjust_subprog_starts(env, off, len); |
| 4258 | 4393 | return new_prog; |
| 4259 | 4394 | } |
| 4260 | 4395 | |
| @@ -4408,6 +4543,8 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) · 147 unmodified lines |
| 4408 | 4543 | for (i = 0; i < insn_cnt; i++, insn++) { |
| 4409 | 4544 | if (insn->code != (BPF_JMP | BPF_CALL)) |
| 4410 | 4545 | continue; |
| 4546 | + if (insn->src_reg == BPF_PSEUDO_CALL) |
| 4547 | + continue; |
| 4411 | 4548 | |
| 4412 | 4549 | if (insn->imm == BPF_FUNC_get_route_realm) |
| 4413 | 4550 | prog->dst_needed = 1; |
| @@ -4589,12 +4726,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) · 175 unmodified lines |
| 4589 | 4726 | if (!env->explored_states) |
| 4590 | 4727 | goto skip_full_check; |
| 4591 | 4728 | |
| 4729 | + env->allow_ptr_leaks = capable(CAP_SYS_ADMIN); |
| 4730 | + |
| 4592 | 4731 | ret = check_cfg(env); |
| 4593 | 4732 | if (ret < 0) |
| 4594 | 4733 | goto skip_full_check; |
| 4595 | 4734 | |
| 4596 | | − env->allow_ptr_leaks = capable(CAP_SYS_ADMIN); |
| 4597 | | − |
| 4598 | 4735 | ret = do_check(env); |
| 4599 | 4736 | if (env->cur_state) { |
| 4600 | 4737 | free_verifier_state(env->cur_state, true); |