bpf: verifier: mark patched-insn with sub-register zext flag
authorJiong Wang <jiong.wang@netronome.com>
Fri, 24 May 2019 22:25:13 +0000 (23:25 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 25 May 2019 01:58:37 +0000 (18:58 -0700)
Patched insns do not go through generic verification, therefore doesn't has
zero extension information collected during insn walking.

We don't bother analyze them at the moment, for any sub-register def comes
from them, just conservatively mark it as needing zero extension.

Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index f6b4c7148c3e8c2a18a3913bd4ba722ddc3a244f..a6af3166acae713dcd3ffaa5665c3a2d5769c10f 100644 (file)
@@ -1269,6 +1269,24 @@ static bool is_reg64(struct bpf_verifier_env *env, struct bpf_insn *insn,
        return true;
 }
 
+/* Return TRUE if INSN doesn't have explicit value define. */
+static bool insn_no_def(struct bpf_insn *insn)
+{
+       u8 class = BPF_CLASS(insn->code);
+
+       return (class == BPF_JMP || class == BPF_JMP32 ||
+               class == BPF_STX || class == BPF_ST);
+}
+
+/* Return TRUE if INSN has defined any 32-bit value explicitly. */
+static bool insn_has_def32(struct bpf_verifier_env *env, struct bpf_insn *insn)
+{
+       if (insn_no_def(insn))
+               return false;
+
+       return !is_reg64(env, insn, insn->dst_reg, NULL, DST_OP);
+}
+
 static void mark_insn_zext(struct bpf_verifier_env *env,
                           struct bpf_reg_state *reg)
 {
@@ -7298,14 +7316,23 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env)
  * 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)
+static int adjust_insn_aux_data(struct bpf_verifier_env *env,
+                               struct bpf_prog *new_prog, u32 off, u32 cnt)
 {
        struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data;
+       struct bpf_insn *insn = new_prog->insnsi;
+       u32 prog_len;
        int i;
 
+       /* aux info at OFF always needs adjustment, no matter fast path
+        * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the
+        * original insn at old prog.
+        */
+       old_data[off].zext_dst = insn_has_def32(env, insn + off + cnt - 1);
+
        if (cnt == 1)
                return 0;
+       prog_len = new_prog->len;
        new_data = vzalloc(array_size(prog_len,
                                      sizeof(struct bpf_insn_aux_data)));
        if (!new_data)
@@ -7313,8 +7340,10 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,
        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));
-       for (i = off; i < off + cnt - 1; i++)
+       for (i = off; i < off + cnt - 1; i++) {
                new_data[i].seen = true;
+               new_data[i].zext_dst = insn_has_def32(env, insn + i);
+       }
        env->insn_aux_data = new_data;
        vfree(old_data);
        return 0;
@@ -7347,7 +7376,7 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of
                                env->insn_aux_data[off].orig_idx);
                return NULL;
        }
-       if (adjust_insn_aux_data(env, new_prog->len, off, len))
+       if (adjust_insn_aux_data(env, new_prog, off, len))
                return NULL;
        adjust_subprog_starts(env, off, len);
        return new_prog;