On Thu, 20 Dec 2018 07:19:06 +0000, Yonghong Song wrote:
> > I think this will break func_info, since it's not adjusted here.
> > Also iirc line_info is relying on first insn to always have line_info.
> > If first insn is dead, second insn might not have a line_info generated
> > and things won't go well.  
> 
> Right, func_info needs to be adjusted as well. The first line_info for 
> each func must have insn_off = 0. In case of dead code elimination from
> the start, if the first remaining insn has a line_info, just use it.
> Otherwise, you can use the old first line_info.

I think I got it (see the incremental diff below).  I want to also
tackle the JIT linfo offsets for offloads while at it and post an RFC
(unless you're handling that already Martin?)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e2ba323e7a57..a75ba4beae2f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6254,13 +6254,26 @@ static int adjust_subprog_starts_after_remove(struct 
bpf_verifier_env *env,
                j--;
 
        if (j > i) {
+               struct bpf_prog_aux *aux = env->prog->aux;
+               int move;
+
                /* move fake 'exit' subprog as well */
-               int move = env->subprog_cnt + 1 - j;
+               move = env->subprog_cnt + 1 - j;
 
                memmove(env->subprog_info + i,
                        env->subprog_info + j,
                        sizeof(*env->subprog_info) * move);
                env->subprog_cnt -= j - i;
+
+               /* remove func_info */
+               if (aux->func_info) {
+                       move = aux->func_info_cnt - j;
+
+                       memmove(aux->func_info + i,
+                               aux->func_info + j,
+                               sizeof(*aux->func_info) * move);
+                       aux->func_info_cnt -= j - i;
+               }
        }
 
        /* convert i from "first prog to remove" to "first to adjust" */
@@ -6274,23 +6287,37 @@ static int adjust_subprog_starts_after_remove(struct 
bpf_verifier_env *env,
        return 0;
 }
 
-static void bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off,
-                                      u32 cnt)
+static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off,
+                                     u32 cnt)
 {
+       struct bpf_subprog_info *need_first_linfo;
        struct bpf_prog *prog = env->prog;
        u32 i, l_off, l_cnt, nr_linfo;
        struct bpf_line_info *linfo;
 
        nr_linfo = prog->aux->nr_linfo;
        if (!nr_linfo || !cnt)
-               return;
+               return 0;
 
        linfo = prog->aux->linfo;
 
+       /* progs are already adjusted, if any program starts on off, it may had
+        * its start cut off and its line info may need to be preserved
+        */
+       for (i = 0; i < env->subprog_cnt; i++)
+               if (env->subprog_info[i].start >= off)
+                       break;
+       if (i < env->subprog_cnt && env->subprog_info[i].start == off)
+               need_first_linfo = &env->subprog_info[i];
+       else
+               need_first_linfo = NULL;
+
+       /* find first line info to remove */
        for (i = 0; i < nr_linfo; i++)
                if (linfo[i].insn_off >= off)
                        break;
 
+       /* count lines to be removed */
        l_off = i;
        l_cnt = 0;
        for (; i < nr_linfo; i++)
@@ -6299,7 +6326,32 @@ static void bpf_adj_linfo_after_remove(struct 
bpf_verifier_env *env, u32 off,
                else
                        break;
 
-       /* Remove the line info which refers to the removed instructions */
+       /* either we didn't actually cut the start of we can just use line info
+        * of first instruction if it exists
+        */
+       if (i < nr_linfo && linfo[i].insn_off == off + cnt)
+               need_first_linfo = NULL;
+       if (need_first_linfo) {
+               if (WARN_ONCE(!l_cnt,
+                             "verifier bug - no linfo removed, yet its 
missing"))
+                       return -EINVAL;
+               if (WARN_ONCE(need_first_linfo->linfo_idx < l_off ||
+                             need_first_linfo->linfo_idx >= l_off + l_cnt,
+                             "verifier bug - removed prog linfo not in removed 
range"))
+                       return -EINVAL;
+               /* subprog linfo_idx is not adjusted yet, so just find out
+                * which line it used to be and swap it
+                */
+               memmove(linfo + l_off, linfo + need_first_linfo->linfo_idx,
+                       sizeof(*linfo));
+               need_first_linfo->linfo_idx = l_off;
+               linfo[l_off].insn_off = off;
+
+               l_off++;
+               l_cnt--;
+       }
+
+       /* remove the line info which refers to the removed instructions */
        if (l_cnt) {
                memmove(linfo + l_off, linfo + i,
                        sizeof(*linfo) * (nr_linfo - i));
@@ -6308,16 +6360,17 @@ static void bpf_adj_linfo_after_remove(struct 
bpf_verifier_env *env, u32 off,
                nr_linfo = prog->aux->nr_linfo;
        }
 
-       /* Pull all linfo[i].insn_off >= off + cnt in by cnt */
+       /* pull all linfo[i].insn_off >= off + cnt in by cnt */
        for (i = l_off; i < nr_linfo; i++)
                linfo[i].insn_off -= cnt;
 
-       /* Fix up all subprogs (incl. 'exit') which start >= off */
-       for (i = 0; i <= env->subprog_cnt; i++)
-               if (env->subprog_info[i].linfo_idx > l_off + l_cnt)
+       /* fix up all subprogs (incl. 'exit') which start >= off */
+       for (i = 0; i <= env->subprog_cnt; i++) {
+               if (env->subprog_info[i].linfo_idx >= l_off + l_cnt)
                        env->subprog_info[i].linfo_idx -= l_cnt;
-               else if (env->subprog_info[i].linfo_idx > l_off)
-                       env->subprog_info[i].linfo_idx = l_off;
+       }
+
+       return 0;
 }
 
 static int verifier_remove_insns(struct bpf_verifier_env *env, u32 off, u32 
cnt)
@@ -6333,12 +6386,18 @@ static int verifier_remove_insns(struct 
bpf_verifier_env *env, u32 off, u32 cnt)
        if (err)
                return err;
 
-       bpf_adj_linfo_after_remove(env, off, cnt);
+       err = adjust_subprog_starts_after_remove(env, off, cnt);
+       if (err)
+               return err;
+
+       err = bpf_adj_linfo_after_remove(env, off, cnt);
+       if (err)
+               return err;
 
        memmove(aux_data + off, aux_data + off + cnt,
                sizeof(*aux_data) * (orig_prog_len - off - cnt));
 
-       return adjust_subprog_starts_after_remove(env, off, cnt);
+       return 0;
 }
 
 /* The verifier does more data flow analysis than llvm and will not
-- 
2.19.2

Reply via email to