]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
bpf: adjust insn_aux_data when patching insns
authorAlexei Starovoitov <ast@fb.com>
Thu, 16 Mar 2017 01:26:41 +0000 (18:26 -0700)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Tue, 13 Mar 2018 10:47:42 +0000 (11:47 +0100)
BugLink: http://bugs.launchpad.net/bugs/1745266
commit 8041902dae5299c1f194ba42d14383f734631009 upstream.

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: Jiri Slaby <jslaby@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
kernel/bpf/verifier.c

index 89fa58656069ec95b53e39a204b021f633441b24..ed438c7dbb86df7d8d8fffe65c3c5df0c5fba2ba 100644 (file)
@@ -2143,6 +2143,41 @@ static void sanitize_dead_code(struct verifier_env *env)
        }
 }
 
+/* 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 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 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'
  */
@@ -2168,7 +2203,7 @@ static int convert_ctx_accesses(struct 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 = env->prog->aux->ops->
@@ -2179,8 +2214,7 @@ static int convert_ctx_accesses(struct 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;