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 | |
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>
-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 | } |