| From foo@baz Fri Dec 22 16:57:35 CET 2017 |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| Date: Fri, 22 Dec 2017 16:29:02 +0100 |
| Subject: bpf: adjust insn_aux_data when patching insns |
| To: gregkh@linuxfoundation.org |
| Cc: ast@kernel.org, daniel@iogearbox.net, jannh@google.com, stable@vger.kernel.org, Alexei Starovoitov <ast@fb.com>, "David S . Miller" <davem@davemloft.net> |
| Message-ID: <20171222152905.3455-2-daniel@iogearbox.net> |
| |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| |
| |
| From: Alexei Starovoitov <ast@fb.com> |
| |
| [ Upstream commit 8041902dae5299c1f194ba42d14383f734631009 ] |
| |
| convert_ctx_accesses() replaces single bpf instruction with a set of |
| instructions. Adjust corresponding insn_aux_data while patching. |
| It's needed to make sure subsequent 'for(all insn)' loops |
| have matching insn and insn_aux_data. |
| |
| Signed-off-by: Alexei Starovoitov <ast@kernel.org> |
| Acked-by: Daniel Borkmann <daniel@iogearbox.net> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| kernel/bpf/verifier.c | 44 +++++++++++++++++++++++++++++++++++++++----- |
| 1 file changed, 39 insertions(+), 5 deletions(-) |
| |
| --- a/kernel/bpf/verifier.c |
| +++ b/kernel/bpf/verifier.c |
| @@ -3210,6 +3210,41 @@ static void convert_pseudo_ld_imm64(stru |
| insn->src_reg = 0; |
| } |
| |
| +/* single env->prog->insni[off] instruction was replaced with the range |
| + * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying |
| + * [0, off) and [off, end) to new locations, so the patched range stays zero |
| + */ |
| +static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, |
| + u32 off, u32 cnt) |
| +{ |
| + struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; |
| + |
| + if (cnt == 1) |
| + return 0; |
| + new_data = vzalloc(sizeof(struct bpf_insn_aux_data) * prog_len); |
| + if (!new_data) |
| + return -ENOMEM; |
| + memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off); |
| + memcpy(new_data + off + cnt - 1, old_data + off, |
| + sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); |
| + env->insn_aux_data = new_data; |
| + vfree(old_data); |
| + return 0; |
| +} |
| + |
| +static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, |
| + const struct bpf_insn *patch, u32 len) |
| +{ |
| + struct bpf_prog *new_prog; |
| + |
| + new_prog = bpf_patch_insn_single(env->prog, off, patch, len); |
| + if (!new_prog) |
| + return NULL; |
| + if (adjust_insn_aux_data(env, new_prog->len, off, len)) |
| + return NULL; |
| + return new_prog; |
| +} |
| + |
| /* convert load instructions that access fields of 'struct __sk_buff' |
| * into sequence of instructions that access fields of 'struct sk_buff' |
| */ |
| @@ -3229,10 +3264,10 @@ static int convert_ctx_accesses(struct b |
| verbose("bpf verifier is misconfigured\n"); |
| return -EINVAL; |
| } else if (cnt) { |
| - new_prog = bpf_patch_insn_single(env->prog, 0, |
| - insn_buf, cnt); |
| + new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt); |
| if (!new_prog) |
| return -ENOMEM; |
| + |
| env->prog = new_prog; |
| delta += cnt - 1; |
| } |
| @@ -3253,7 +3288,7 @@ static int convert_ctx_accesses(struct b |
| else |
| continue; |
| |
| - if (env->insn_aux_data[i].ptr_type != PTR_TO_CTX) |
| + if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) |
| continue; |
| |
| cnt = ops->convert_ctx_access(type, insn->dst_reg, insn->src_reg, |
| @@ -3263,8 +3298,7 @@ static int convert_ctx_accesses(struct b |
| return -EINVAL; |
| } |
| |
| - new_prog = bpf_patch_insn_single(env->prog, i + delta, insn_buf, |
| - cnt); |
| + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); |
| if (!new_prog) |
| return -ENOMEM; |
| |