All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis
@ 2018-12-13 19:42 Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 1/4] bpf: speed up stacksafe check Alexei Starovoitov
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Alexei Starovoitov @ 2018-12-13 19:42 UTC (permalink / raw)
  To: David S . Miller
  Cc: daniel, ecree, jakub.kicinski, jiong.wang, netdev, kernel-team

v1->v2:
with optimization suggested by Jakub patch 4 safety check became cheap enough.

Several improvements to verifier state logic.
Patch 1 - trivial optimization
Patch 3 - significant optimization for stack state equivalence
Patch 4 - safety check for liveness and prep for future state merging

Alexei Starovoitov (4):
  bpf: speed up stacksafe check
  selftests/bpf: check insn processed in test_verifier
  bpf: improve stacksafe state comparison
  bpf: add self-check logic to liveness analysis

 include/linux/bpf_verifier.h                |   1 +
 kernel/bpf/verifier.c                       | 125 ++++++++++++++++++--
 tools/testing/selftests/bpf/test_verifier.c |  37 +++++-
 3 files changed, 154 insertions(+), 9 deletions(-)

-- 
2.17.1

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 bpf-next 1/4] bpf: speed up stacksafe check
  2018-12-13 19:42 [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
@ 2018-12-13 19:42 ` Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 2/4] selftests/bpf: check insn processed in test_verifier Alexei Starovoitov
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Alexei Starovoitov @ 2018-12-13 19:42 UTC (permalink / raw)
  To: David S . Miller
  Cc: daniel, ecree, jakub.kicinski, jiong.wang, netdev, kernel-team

Don't check the same stack liveness condition 8 times.
once is enough.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Edward Cree <ecree@solarflare.com>
Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 kernel/bpf/verifier.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8b511a4fe84a..d4db4727a50e 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5147,9 +5147,11 @@ static bool stacksafe(struct bpf_func_state *old,
 	for (i = 0; i < old->allocated_stack; i++) {
 		spi = i / BPF_REG_SIZE;
 
-		if (!(old->stack[spi].spilled_ptr.live & REG_LIVE_READ))
+		if (!(old->stack[spi].spilled_ptr.live & REG_LIVE_READ)) {
+			i += BPF_REG_SIZE - 1;
 			/* explored state didn't use this */
 			continue;
+		}
 
 		if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID)
 			continue;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 bpf-next 2/4] selftests/bpf: check insn processed in test_verifier
  2018-12-13 19:42 [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 1/4] bpf: speed up stacksafe check Alexei Starovoitov
@ 2018-12-13 19:42 ` Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 3/4] bpf: improve stacksafe state comparison Alexei Starovoitov
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Alexei Starovoitov @ 2018-12-13 19:42 UTC (permalink / raw)
  To: David S . Miller
  Cc: daniel, ecree, jakub.kicinski, jiong.wang, netdev, kernel-team

Teach test_verifier to parse verifier output for insn processed
and compare with expected number.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Edward Cree <ecree@solarflare.com>
Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 tools/testing/selftests/bpf/test_verifier.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index a08c67c8767e..82359cdbc805 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -76,7 +76,7 @@ struct bpf_test {
 	int fixup_percpu_cgroup_storage[MAX_FIXUPS];
 	const char *errstr;
 	const char *errstr_unpriv;
-	uint32_t retval, retval_unpriv;
+	uint32_t retval, retval_unpriv, insn_processed;
 	enum {
 		UNDEF,
 		ACCEPT,
@@ -14444,6 +14444,19 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 		}
 	}
 
+	if (test->insn_processed) {
+		uint32_t insn_processed;
+		char *proc;
+
+		proc = strstr(bpf_vlog, "processed ");
+		insn_processed = atoi(proc + 10);
+		if (test->insn_processed != insn_processed) {
+			printf("FAIL\nUnexpected insn_processed %u vs %u\n",
+			       insn_processed, test->insn_processed);
+			goto fail_log;
+		}
+	}
+
 	if (fd_prog >= 0) {
 		__u8 tmp[TEST_DATA_LEN << 2];
 		__u32 size_tmp = sizeof(tmp);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 bpf-next 3/4] bpf: improve stacksafe state comparison
  2018-12-13 19:42 [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 1/4] bpf: speed up stacksafe check Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 2/4] selftests/bpf: check insn processed in test_verifier Alexei Starovoitov
@ 2018-12-13 19:42 ` Alexei Starovoitov
  2018-12-13 19:42 ` [PATCH v2 bpf-next 4/4] bpf: add self-check logic to liveness analysis Alexei Starovoitov
  2018-12-15  0:37 ` [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Daniel Borkmann
  4 siblings, 0 replies; 7+ messages in thread
From: Alexei Starovoitov @ 2018-12-13 19:42 UTC (permalink / raw)
  To: David S . Miller
  Cc: daniel, ecree, jakub.kicinski, jiong.wang, netdev, kernel-team

"if (old->allocated_stack > cur->allocated_stack)" check is too conservative.
In some cases explored stack could have allocated more space,
but that stack space was not live.
The test case improves from 19 to 15 processed insns
and improvement on real programs is significant as well:

                       before    after
bpf_lb-DLB_L3.o        1940      1831
bpf_lb-DLB_L4.o        3089      3029
bpf_lb-DUNKNOWN.o      1065      1064
bpf_lxc-DDROP_ALL.o    28052     26309
bpf_lxc-DUNKNOWN.o     35487     33517
bpf_netdev.o           10864     9713
bpf_overlay.o          6643      6184
bpf_lcx_jit.o          38437     37335

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Edward Cree <ecree@solarflare.com>
Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 kernel/bpf/verifier.c                       | 13 ++++++------
 tools/testing/selftests/bpf/test_verifier.c | 22 +++++++++++++++++++++
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d4db4727a50e..6e5ea4c4d8e7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5134,12 +5134,6 @@ static bool stacksafe(struct bpf_func_state *old,
 {
 	int i, spi;
 
-	/* if explored stack has more populated slots than current stack
-	 * such stacks are not equivalent
-	 */
-	if (old->allocated_stack > cur->allocated_stack)
-		return false;
-
 	/* walk slots of the explored stack and ignore any additional
 	 * slots in the current stack, since explored(safe) state
 	 * didn't use them
@@ -5155,6 +5149,13 @@ static bool stacksafe(struct bpf_func_state *old,
 
 		if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID)
 			continue;
+
+		/* explored stack has more populated slots than current stack
+		 * and these slots were used
+		 */
+		if (i >= cur->allocated_stack)
+			return false;
+
 		/* if old state was safe with misc data in the stack
 		 * it will be safe with zero-initialized stack.
 		 * The opposite is not true
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index 82359cdbc805..f9de7fe0c26d 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -13647,6 +13647,28 @@ static struct bpf_test tests[] = {
 		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
 		.result = ACCEPT,
 	},
+	{
+		"allocated_stack",
+		.insns = {
+			BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1),
+			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+			BPF_ALU64_REG(BPF_MOV, BPF_REG_7, BPF_REG_0),
+			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
+			BPF_MOV64_IMM(BPF_REG_0, 0),
+			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
+			BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, -8),
+			BPF_STX_MEM(BPF_B, BPF_REG_10, BPF_REG_7, -9),
+			BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_10, -9),
+			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+			BPF_EXIT_INSN(),
+		},
+		.result = ACCEPT,
+		.result_unpriv = ACCEPT,
+		.insn_processed = 15,
+	},
 	{
 		"reference tracking in call: free reference in subprog and outside",
 		.insns = {
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 bpf-next 4/4] bpf: add self-check logic to liveness analysis
  2018-12-13 19:42 [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
                   ` (2 preceding siblings ...)
  2018-12-13 19:42 ` [PATCH v2 bpf-next 3/4] bpf: improve stacksafe state comparison Alexei Starovoitov
@ 2018-12-13 19:42 ` Alexei Starovoitov
  2018-12-13 22:26   ` Jakub Kicinski
  2018-12-15  0:37 ` [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Daniel Borkmann
  4 siblings, 1 reply; 7+ messages in thread
From: Alexei Starovoitov @ 2018-12-13 19:42 UTC (permalink / raw)
  To: David S . Miller
  Cc: daniel, ecree, jakub.kicinski, jiong.wang, netdev, kernel-team

Introduce REG_LIVE_DONE to check the liveness propagation
and prepare the states for merging.
See algorithm description in clean_live_states().

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
 include/linux/bpf_verifier.h |   1 +
 kernel/bpf/verifier.c        | 108 ++++++++++++++++++++++++++++++++++-
 2 files changed, 108 insertions(+), 1 deletion(-)

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index c736945be7c5..07a55073c809 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -38,6 +38,7 @@ enum bpf_reg_liveness {
 	REG_LIVE_NONE = 0, /* reg hasn't been read or written this branch */
 	REG_LIVE_READ, /* reg was read, so we're sensitive to initial value */
 	REG_LIVE_WRITTEN, /* reg was written first, screening off later reads */
+	REG_LIVE_DONE = 4, /* liveness won't be updating this register anymore */
 };
 
 struct bpf_reg_state {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6e5ea4c4d8e7..a4fe18c5a330 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -339,12 +339,14 @@ static char slot_type_char[] = {
 static void print_liveness(struct bpf_verifier_env *env,
 			   enum bpf_reg_liveness live)
 {
-	if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN))
+	if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN | REG_LIVE_DONE))
 	    verbose(env, "_");
 	if (live & REG_LIVE_READ)
 		verbose(env, "r");
 	if (live & REG_LIVE_WRITTEN)
 		verbose(env, "w");
+	if (live & REG_LIVE_DONE)
+		verbose(env, "D");
 }
 
 static struct bpf_func_state *func(struct bpf_verifier_env *env,
@@ -1074,6 +1076,12 @@ static int mark_reg_read(struct bpf_verifier_env *env,
 		/* if read wasn't screened by an earlier write ... */
 		if (writes && state->live & REG_LIVE_WRITTEN)
 			break;
+		if (parent->live & REG_LIVE_DONE) {
+			verbose(env, "verifier BUG type %s var_off %lld off %d\n",
+				reg_type_str[parent->type],
+				parent->var_off.value, parent->off);
+			return -EFAULT;
+		}
 		/* ... then we depend on parent's value */
 		parent->live |= REG_LIVE_READ;
 		state = parent;
@@ -5021,6 +5029,102 @@ static bool check_ids(u32 old_id, u32 cur_id, struct idpair *idmap)
 	return false;
 }
 
+static void clean_func_state(struct bpf_verifier_env *env,
+			     struct bpf_func_state *st)
+{
+	enum bpf_reg_liveness live;
+	int i, j;
+
+	for (i = 0; i < BPF_REG_FP; i++) {
+		live = st->regs[i].live;
+		/* liveness must not touch this register anymore */
+		st->regs[i].live |= REG_LIVE_DONE;
+		if (!(live & REG_LIVE_READ))
+			/* since the register is unused, clear its state
+			 * to make further comparison simpler
+			 */
+			__mark_reg_not_init(&st->regs[i]);
+	}
+
+	for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
+		live = st->stack[i].spilled_ptr.live;
+		/* liveness must not touch this stack slot anymore */
+		st->stack[i].spilled_ptr.live |= REG_LIVE_DONE;
+		if (!(live & REG_LIVE_READ)) {
+			__mark_reg_not_init(&st->stack[i].spilled_ptr);
+			for (j = 0; j < BPF_REG_SIZE; j++)
+				st->stack[i].slot_type[j] = STACK_INVALID;
+		}
+	}
+}
+
+static void clean_verifier_state(struct bpf_verifier_env *env,
+				 struct bpf_verifier_state *st)
+{
+	int i;
+
+	if (st->frame[0]->regs[0].live & REG_LIVE_DONE)
+		/* all regs in this state in all frames were already marked */
+		return;
+
+	for (i = 0; i <= st->curframe; i++)
+		clean_func_state(env, st->frame[i]);
+}
+
+/* the parentage chains form a tree.
+ * the verifier states are added to state lists at given insn and
+ * pushed into state stack for future exploration.
+ * when the verifier reaches bpf_exit insn some of the verifer states
+ * stored in the state lists have their final liveness state already,
+ * but a lot of states will get revised from liveness point of view when
+ * the verifier explores other branches.
+ * Example:
+ * 1: r0 = 1
+ * 2: if r1 == 100 goto pc+1
+ * 3: r0 = 2
+ * 4: exit
+ * when the verifier reaches exit insn the register r0 in the state list of
+ * insn 2 will be seen as !REG_LIVE_READ. Then the verifier pops the other_branch
+ * of insn 2 and goes exploring further. At the insn 4 it will walk the
+ * parentage chain from insn 4 into insn 2 and will mark r0 as REG_LIVE_READ.
+ *
+ * Since the verifier pushes the branch states as it sees them while exploring
+ * the program the condition of walking the branch instruction for the second
+ * time means that all states below this branch were already explored and
+ * their final liveness markes are already propagated.
+ * Hence when the verifier completes the search of state list in is_state_visited()
+ * we can call this clean_live_states() function to mark all liveness states
+ * as REG_LIVE_DONE to indicate that 'parent' pointers of 'struct bpf_reg_state'
+ * will not be used.
+ * This function also clears the registers and stack for states that !READ
+ * to simplify state merging.
+ *
+ * Important note here that walking the same branch instruction in the callee
+ * doesn't meant that the states are DONE. The verifier has to compare
+ * the callsites
+ */
+static void clean_live_states(struct bpf_verifier_env *env, int insn,
+			      struct bpf_verifier_state *cur)
+{
+	struct bpf_verifier_state_list *sl;
+	int i;
+
+	sl = env->explored_states[insn];
+	if (!sl)
+		return;
+
+	while (sl != STATE_LIST_MARK) {
+		if (sl->state.curframe != cur->curframe)
+			goto next;
+		for (i = 0; i <= cur->curframe; i++)
+			if (sl->state.frame[i]->callsite != cur->frame[i]->callsite)
+				goto next;
+		clean_verifier_state(env, &sl->state);
+next:
+		sl = sl->next;
+	}
+}
+
 /* Returns true if (rold safe implies rcur safe) */
 static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
 		    struct idpair *idmap)
@@ -5339,6 +5443,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
 		 */
 		return 0;
 
+	clean_live_states(env, insn_idx, cur);
+
 	while (sl != STATE_LIST_MARK) {
 		if (states_equal(env, &sl->state, cur)) {
 			/* reached equivalent register/stack state,
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 bpf-next 4/4] bpf: add self-check logic to liveness analysis
  2018-12-13 19:42 ` [PATCH v2 bpf-next 4/4] bpf: add self-check logic to liveness analysis Alexei Starovoitov
@ 2018-12-13 22:26   ` Jakub Kicinski
  0 siblings, 0 replies; 7+ messages in thread
From: Jakub Kicinski @ 2018-12-13 22:26 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David S . Miller, daniel, ecree, jiong.wang, netdev, kernel-team

On Thu, 13 Dec 2018 11:42:34 -0800, Alexei Starovoitov wrote:
> Introduce REG_LIVE_DONE to check the liveness propagation
> and prepare the states for merging.
> See algorithm description in clean_live_states().
> 
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>

Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis
  2018-12-13 19:42 [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
                   ` (3 preceding siblings ...)
  2018-12-13 19:42 ` [PATCH v2 bpf-next 4/4] bpf: add self-check logic to liveness analysis Alexei Starovoitov
@ 2018-12-15  0:37 ` Daniel Borkmann
  4 siblings, 0 replies; 7+ messages in thread
From: Daniel Borkmann @ 2018-12-15  0:37 UTC (permalink / raw)
  To: Alexei Starovoitov, David S . Miller
  Cc: ecree, jakub.kicinski, jiong.wang, netdev, kernel-team

On 12/13/2018 08:42 PM, Alexei Starovoitov wrote:
> v1->v2:
> with optimization suggested by Jakub patch 4 safety check became cheap enough.
> 
> Several improvements to verifier state logic.
> Patch 1 - trivial optimization
> Patch 3 - significant optimization for stack state equivalence
> Patch 4 - safety check for liveness and prep for future state merging
> 
> Alexei Starovoitov (4):
>   bpf: speed up stacksafe check
>   selftests/bpf: check insn processed in test_verifier
>   bpf: improve stacksafe state comparison
>   bpf: add self-check logic to liveness analysis
> 
>  include/linux/bpf_verifier.h                |   1 +
>  kernel/bpf/verifier.c                       | 125 ++++++++++++++++++--
>  tools/testing/selftests/bpf/test_verifier.c |  37 +++++-
>  3 files changed, 154 insertions(+), 9 deletions(-)
> 

Applied, thanks!

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-12-15  0:37 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-13 19:42 [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
2018-12-13 19:42 ` [PATCH v2 bpf-next 1/4] bpf: speed up stacksafe check Alexei Starovoitov
2018-12-13 19:42 ` [PATCH v2 bpf-next 2/4] selftests/bpf: check insn processed in test_verifier Alexei Starovoitov
2018-12-13 19:42 ` [PATCH v2 bpf-next 3/4] bpf: improve stacksafe state comparison Alexei Starovoitov
2018-12-13 19:42 ` [PATCH v2 bpf-next 4/4] bpf: add self-check logic to liveness analysis Alexei Starovoitov
2018-12-13 22:26   ` Jakub Kicinski
2018-12-15  0:37 ` [PATCH v2 bpf-next 0/4] bpf: improve verifier state analysis Daniel Borkmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.