bpf: adjust insn_aux_data when patching insns
authorAlexei Starovoitov <ast@fb.com>
Thu, 16 Mar 2017 01:26:41 +0000 (18:26 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 17 Mar 2017 03:44:11 +0000 (20:44 -0700)
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>
kernel/bpf/verifier.c

index 5dfa9b8111da9209b1e003a044428ad244e591b4..2990fda1c6a5d4aa8c8460fa628d89771e25a122 100644 (file)
@@ -3162,6 +3162,41 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env)
                        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'
  */
@@ -3181,10 +3216,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                        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;
                }
@@ -3209,7 +3244,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                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, insn_buf, env->prog);
@@ -3218,8 +3253,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
                        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;