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.w...@netronome.com>
---
 kernel/bpf/verifier.c | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6e62cc8..b75913c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1303,6 +1303,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)
 {
@@ -7306,14 +7324,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)
@@ -7321,8 +7348,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;
@@ -7355,7 +7384,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;
-- 
2.7.4

Reply via email to