diff options
| author | Jakub Kicinski <jakub.kicinski@netronome.com> | 2016-09-21 06:43:56 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2016-09-21 19:50:02 -0400 |
| commit | 3df126f35f88dc76eea33769f85a3c3bb8ce6c6b (patch) | |
| tree | 72d11939e2d7219edb506fb783014f02f4130740 /kernel/bpf | |
| parent | eadb41489fd2249e71fd14b36fb488ed7217ca4b (diff) | |
bpf: don't (ab)use instructions to store state
Storing state in reserved fields of instructions makes
it impossible to run verifier on programs already
marked as read-only. Allocate and use an array of
per-instruction state instead.
While touching the error path rename and move existing
jump target.
Suggested-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/bpf')
| -rw-r--r-- | kernel/bpf/verifier.c | 70 |
1 files changed, 40 insertions, 30 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3a75ee3bdcd1..a9542d89f293 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
| @@ -181,6 +181,10 @@ struct verifier_stack_elem { | |||
| 181 | struct verifier_stack_elem *next; | 181 | struct verifier_stack_elem *next; |
| 182 | }; | 182 | }; |
| 183 | 183 | ||
| 184 | struct bpf_insn_aux_data { | ||
| 185 | enum bpf_reg_type ptr_type; /* pointer type for load/store insns */ | ||
| 186 | }; | ||
| 187 | |||
| 184 | #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ | 188 | #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ |
| 185 | 189 | ||
| 186 | /* single container for all structs | 190 | /* single container for all structs |
| @@ -197,6 +201,7 @@ struct verifier_env { | |||
| 197 | u32 id_gen; /* used to generate unique reg IDs */ | 201 | u32 id_gen; /* used to generate unique reg IDs */ |
| 198 | bool allow_ptr_leaks; | 202 | bool allow_ptr_leaks; |
| 199 | bool seen_direct_write; | 203 | bool seen_direct_write; |
| 204 | struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */ | ||
| 200 | }; | 205 | }; |
| 201 | 206 | ||
| 202 | #define BPF_COMPLEXITY_LIMIT_INSNS 65536 | 207 | #define BPF_COMPLEXITY_LIMIT_INSNS 65536 |
| @@ -2344,7 +2349,7 @@ static int do_check(struct verifier_env *env) | |||
| 2344 | return err; | 2349 | return err; |
| 2345 | 2350 | ||
| 2346 | } else if (class == BPF_LDX) { | 2351 | } else if (class == BPF_LDX) { |
| 2347 | enum bpf_reg_type src_reg_type; | 2352 | enum bpf_reg_type *prev_src_type, src_reg_type; |
| 2348 | 2353 | ||
| 2349 | /* check for reserved fields is already done */ | 2354 | /* check for reserved fields is already done */ |
| 2350 | 2355 | ||
| @@ -2374,16 +2379,18 @@ static int do_check(struct verifier_env *env) | |||
| 2374 | continue; | 2379 | continue; |
| 2375 | } | 2380 | } |
| 2376 | 2381 | ||
| 2377 | if (insn->imm == 0) { | 2382 | prev_src_type = &env->insn_aux_data[insn_idx].ptr_type; |
| 2383 | |||
| 2384 | if (*prev_src_type == NOT_INIT) { | ||
| 2378 | /* saw a valid insn | 2385 | /* saw a valid insn |
| 2379 | * dst_reg = *(u32 *)(src_reg + off) | 2386 | * dst_reg = *(u32 *)(src_reg + off) |
| 2380 | * use reserved 'imm' field to mark this insn | 2387 | * save type to validate intersecting paths |
| 2381 | */ | 2388 | */ |
| 2382 | insn->imm = src_reg_type; | 2389 | *prev_src_type = src_reg_type; |
| 2383 | 2390 | ||
| 2384 | } else if (src_reg_type != insn->imm && | 2391 | } else if (src_reg_type != *prev_src_type && |
| 2385 | (src_reg_type == PTR_TO_CTX || | 2392 | (src_reg_type == PTR_TO_CTX || |
| 2386 | insn->imm == PTR_TO_CTX)) { | 2393 | *prev_src_type == PTR_TO_CTX)) { |
| 2387 | /* ABuser program is trying to use the same insn | 2394 | /* ABuser program is trying to use the same insn |
| 2388 | * dst_reg = *(u32*) (src_reg + off) | 2395 | * dst_reg = *(u32*) (src_reg + off) |
| 2389 | * with different pointer types: | 2396 | * with different pointer types: |
| @@ -2396,7 +2403,7 @@ static int do_check(struct verifier_env *env) | |||
| 2396 | } | 2403 | } |
| 2397 | 2404 | ||
| 2398 | } else if (class == BPF_STX) { | 2405 | } else if (class == BPF_STX) { |
| 2399 | enum bpf_reg_type dst_reg_type; | 2406 | enum bpf_reg_type *prev_dst_type, dst_reg_type; |
| 2400 | 2407 | ||
| 2401 | if (BPF_MODE(insn->code) == BPF_XADD) { | 2408 | if (BPF_MODE(insn->code) == BPF_XADD) { |
| 2402 | err = check_xadd(env, insn); | 2409 | err = check_xadd(env, insn); |
| @@ -2424,11 +2431,13 @@ static int do_check(struct verifier_env *env) | |||
| 2424 | if (err) | 2431 | if (err) |
| 2425 | return err; | 2432 | return err; |
| 2426 | 2433 | ||
| 2427 | if (insn->imm == 0) { | 2434 | prev_dst_type = &env->insn_aux_data[insn_idx].ptr_type; |
| 2428 | insn->imm = dst_reg_type; | 2435 | |
| 2429 | } else if (dst_reg_type != insn->imm && | 2436 | if (*prev_dst_type == NOT_INIT) { |
| 2437 | *prev_dst_type = dst_reg_type; | ||
| 2438 | } else if (dst_reg_type != *prev_dst_type && | ||
| 2430 | (dst_reg_type == PTR_TO_CTX || | 2439 | (dst_reg_type == PTR_TO_CTX || |
| 2431 | insn->imm == PTR_TO_CTX)) { | 2440 | *prev_dst_type == PTR_TO_CTX)) { |
| 2432 | verbose("same insn cannot be used with different pointers\n"); | 2441 | verbose("same insn cannot be used with different pointers\n"); |
| 2433 | return -EINVAL; | 2442 | return -EINVAL; |
| 2434 | } | 2443 | } |
| @@ -2686,10 +2695,11 @@ static void convert_pseudo_ld_imm64(struct verifier_env *env) | |||
| 2686 | static int convert_ctx_accesses(struct verifier_env *env) | 2695 | static int convert_ctx_accesses(struct verifier_env *env) |
| 2687 | { | 2696 | { |
| 2688 | const struct bpf_verifier_ops *ops = env->prog->aux->ops; | 2697 | const struct bpf_verifier_ops *ops = env->prog->aux->ops; |
| 2698 | const int insn_cnt = env->prog->len; | ||
| 2689 | struct bpf_insn insn_buf[16], *insn; | 2699 | struct bpf_insn insn_buf[16], *insn; |
| 2690 | struct bpf_prog *new_prog; | 2700 | struct bpf_prog *new_prog; |
| 2691 | enum bpf_access_type type; | 2701 | enum bpf_access_type type; |
| 2692 | int i, insn_cnt, cnt; | 2702 | int i, cnt, delta = 0; |
| 2693 | 2703 | ||
| 2694 | if (ops->gen_prologue) { | 2704 | if (ops->gen_prologue) { |
| 2695 | cnt = ops->gen_prologue(insn_buf, env->seen_direct_write, | 2705 | cnt = ops->gen_prologue(insn_buf, env->seen_direct_write, |
| @@ -2703,18 +2713,16 @@ static int convert_ctx_accesses(struct verifier_env *env) | |||
| 2703 | if (!new_prog) | 2713 | if (!new_prog) |
| 2704 | return -ENOMEM; | 2714 | return -ENOMEM; |
| 2705 | env->prog = new_prog; | 2715 | env->prog = new_prog; |
| 2716 | delta += cnt - 1; | ||
| 2706 | } | 2717 | } |
| 2707 | } | 2718 | } |
| 2708 | 2719 | ||
| 2709 | if (!ops->convert_ctx_access) | 2720 | if (!ops->convert_ctx_access) |
| 2710 | return 0; | 2721 | return 0; |
| 2711 | 2722 | ||
| 2712 | insn_cnt = env->prog->len; | 2723 | insn = env->prog->insnsi + delta; |
| 2713 | insn = env->prog->insnsi; | ||
| 2714 | 2724 | ||
| 2715 | for (i = 0; i < insn_cnt; i++, insn++) { | 2725 | for (i = 0; i < insn_cnt; i++, insn++) { |
| 2716 | u32 insn_delta; | ||
| 2717 | |||
| 2718 | if (insn->code == (BPF_LDX | BPF_MEM | BPF_W) || | 2726 | if (insn->code == (BPF_LDX | BPF_MEM | BPF_W) || |
| 2719 | insn->code == (BPF_LDX | BPF_MEM | BPF_DW)) | 2727 | insn->code == (BPF_LDX | BPF_MEM | BPF_DW)) |
| 2720 | type = BPF_READ; | 2728 | type = BPF_READ; |
| @@ -2724,11 +2732,8 @@ static int convert_ctx_accesses(struct verifier_env *env) | |||
| 2724 | else | 2732 | else |
| 2725 | continue; | 2733 | continue; |
| 2726 | 2734 | ||
| 2727 | if (insn->imm != PTR_TO_CTX) { | 2735 | if (env->insn_aux_data[i].ptr_type != PTR_TO_CTX) |
| 2728 | /* clear internal mark */ | ||
| 2729 | insn->imm = 0; | ||
| 2730 | continue; | 2736 | continue; |
| 2731 | } | ||
| 2732 | 2737 | ||
| 2733 | cnt = ops->convert_ctx_access(type, insn->dst_reg, insn->src_reg, | 2738 | cnt = ops->convert_ctx_access(type, insn->dst_reg, insn->src_reg, |
| 2734 | insn->off, insn_buf, env->prog); | 2739 | insn->off, insn_buf, env->prog); |
| @@ -2737,18 +2742,16 @@ static int convert_ctx_accesses(struct verifier_env *env) | |||
| 2737 | return -EINVAL; | 2742 | return -EINVAL; |
| 2738 | } | 2743 | } |
| 2739 | 2744 | ||
| 2740 | new_prog = bpf_patch_insn_single(env->prog, i, insn_buf, cnt); | 2745 | new_prog = bpf_patch_insn_single(env->prog, i + delta, insn_buf, |
| 2746 | cnt); | ||
| 2741 | if (!new_prog) | 2747 | if (!new_prog) |
| 2742 | return -ENOMEM; | 2748 | return -ENOMEM; |
| 2743 | 2749 | ||
| 2744 | insn_delta = cnt - 1; | 2750 | delta += cnt - 1; |
| 2745 | 2751 | ||
| 2746 | /* keep walking new program and skip insns we just inserted */ | 2752 | /* keep walking new program and skip insns we just inserted */ |
| 2747 | env->prog = new_prog; | 2753 | env->prog = new_prog; |
| 2748 | insn = new_prog->insnsi + i + insn_delta; | 2754 | insn = new_prog->insnsi + i + delta; |
| 2749 | |||
| 2750 | insn_cnt += insn_delta; | ||
| 2751 | i += insn_delta; | ||
| 2752 | } | 2755 | } |
| 2753 | 2756 | ||
| 2754 | return 0; | 2757 | return 0; |
| @@ -2792,6 +2795,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) | |||
| 2792 | if (!env) | 2795 | if (!env) |
| 2793 | return -ENOMEM; | 2796 | return -ENOMEM; |
| 2794 | 2797 | ||
| 2798 | env->insn_aux_data = vzalloc(sizeof(struct bpf_insn_aux_data) * | ||
| 2799 | (*prog)->len); | ||
| 2800 | ret = -ENOMEM; | ||
| 2801 | if (!env->insn_aux_data) | ||
| 2802 | goto err_free_env; | ||
| 2795 | env->prog = *prog; | 2803 | env->prog = *prog; |
| 2796 | 2804 | ||
| 2797 | /* grab the mutex to protect few globals used by verifier */ | 2805 | /* grab the mutex to protect few globals used by verifier */ |
| @@ -2810,12 +2818,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) | |||
| 2810 | /* log_* values have to be sane */ | 2818 | /* log_* values have to be sane */ |
| 2811 | if (log_size < 128 || log_size > UINT_MAX >> 8 || | 2819 | if (log_size < 128 || log_size > UINT_MAX >> 8 || |
| 2812 | log_level == 0 || log_ubuf == NULL) | 2820 | log_level == 0 || log_ubuf == NULL) |
| 2813 | goto free_env; | 2821 | goto err_unlock; |
| 2814 | 2822 | ||
| 2815 | ret = -ENOMEM; | 2823 | ret = -ENOMEM; |
| 2816 | log_buf = vmalloc(log_size); | 2824 | log_buf = vmalloc(log_size); |
| 2817 | if (!log_buf) | 2825 | if (!log_buf) |
| 2818 | goto free_env; | 2826 | goto err_unlock; |
| 2819 | } else { | 2827 | } else { |
| 2820 | log_level = 0; | 2828 | log_level = 0; |
| 2821 | } | 2829 | } |
| @@ -2884,14 +2892,16 @@ skip_full_check: | |||
| 2884 | free_log_buf: | 2892 | free_log_buf: |
| 2885 | if (log_level) | 2893 | if (log_level) |
| 2886 | vfree(log_buf); | 2894 | vfree(log_buf); |
| 2887 | free_env: | ||
| 2888 | if (!env->prog->aux->used_maps) | 2895 | if (!env->prog->aux->used_maps) |
| 2889 | /* if we didn't copy map pointers into bpf_prog_info, release | 2896 | /* if we didn't copy map pointers into bpf_prog_info, release |
| 2890 | * them now. Otherwise free_bpf_prog_info() will release them. | 2897 | * them now. Otherwise free_bpf_prog_info() will release them. |
| 2891 | */ | 2898 | */ |
| 2892 | release_maps(env); | 2899 | release_maps(env); |
| 2893 | *prog = env->prog; | 2900 | *prog = env->prog; |
| 2894 | kfree(env); | 2901 | err_unlock: |
| 2895 | mutex_unlock(&bpf_verifier_lock); | 2902 | mutex_unlock(&bpf_verifier_lock); |
| 2903 | vfree(env->insn_aux_data); | ||
| 2904 | err_free_env: | ||
| 2905 | kfree(env); | ||
| 2896 | return ret; | 2906 | return ret; |
| 2897 | } | 2907 | } |
