lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20181221154632.230ed923@cakuba.netronome.com>
Date:   Fri, 21 Dec 2018 15:46:32 -0800
From:   Jakub Kicinski <jakub.kicinski@...ronome.com>
To:     Yonghong Song <yhs@...com>
Cc:     Alexei Starovoitov <alexei.starovoitov@...il.com>,
        "daniel@...earbox.net" <daniel@...earbox.net>,
        "oss-drivers@...ronome.com" <oss-drivers@...ronome.com>,
        "netdev@...r.kernel.org" <netdev@...r.kernel.org>,
        Martin Lau <kafai@...com>
Subject: Re: [PATCH bpf-next 07/19] bpf: verifier: remove dead code

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ