netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jakub Kicinski <jakub.kicinski@netronome.com>
To: Alexei Starovoitov <ast@kernel.org>
Cc: "David S . Miller" <davem@davemloft.net>, <daniel@iogearbox.net>,
	<ecree@solarflare.com>, <jiong.wang@netronome.com>,
	<netdev@vger.kernel.org>, <kernel-team@fb.com>
Subject: Re: [PATCH bpf-next 4/4] bpf: add self-check logic to liveness analysis
Date: Wed, 12 Dec 2018 17:21:35 -0800	[thread overview]
Message-ID: <20181212172135.05d5be65@cakuba.netronome.com> (raw)
In-Reply-To: <20181212052854.4105971-5-ast@kernel.org>

On Tue, 11 Dec 2018 21:28:54 -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().
> No difference in tests.
> 
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>

> @@ -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;
> +		if (live & REG_LIVE_DONE)
> +			continue;

Perhaps return; here?  Seeing any "done" flag in the state entry
implies all regs and stack slots are "done", no?  Or even check if
there are any DONE flags in clean_verifier_state() before the loop?

> +		/* 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;
> +		if (live & REG_LIVE_DONE)
> +			continue;
> +		/* 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;
> +
> +	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
> + */

Little bike shedding - if I understand Ed's comment I feel the same way
about "any state we see must be fully done".  Different call sites are
logically different state lists in my mind, we effectively "inline" the
functions in the verifier walk... 

> +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)
> @@ -5354,11 +5458,13 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
>  			err = propagate_liveness(env, &sl->state, cur);
>  			if (err)
>  				return err;
> +			clean_live_states(env, insn_idx, cur);
>  			return 1;
>  		}
>  		sl = sl->next;
>  		states_cnt++;
>  	}
> +	clean_live_states(env, insn_idx, cur);
>  
>  	if (!env->allow_ptr_leaks && states_cnt > BPF_COMPLEXITY_LIMIT_STATES)
>  		return 0;

  parent reply	other threads:[~2018-12-13  1:21 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-12  5:28 [PATCH bpf-next 0/4] bpf: improve verifier state analysis Alexei Starovoitov
2018-12-12  5:28 ` [PATCH bpf-next 1/4] bpf: speed up stacksafe check Alexei Starovoitov
2018-12-12  5:28 ` [PATCH bpf-next 2/4] selftests/bpf: check insn processed in test_verifier Alexei Starovoitov
2018-12-12  5:28 ` [PATCH bpf-next 3/4] bpf: improve stacksafe state comparison Alexei Starovoitov
2018-12-12  5:28 ` [PATCH bpf-next 4/4] bpf: add self-check logic to liveness analysis Alexei Starovoitov
2018-12-12 20:58   ` Edward Cree
2018-12-12 22:00     ` Alexei Starovoitov
2018-12-12 22:26       ` Edward Cree
2018-12-13  0:00         ` Alexei Starovoitov
2018-12-13 20:02           ` Edward Cree
2018-12-13  1:21   ` Jakub Kicinski [this message]
2018-12-13  4:37     ` Alexei Starovoitov
2018-12-13  1:22 ` [PATCH bpf-next 0/4] bpf: improve verifier state analysis Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181212172135.05d5be65@cakuba.netronome.com \
    --to=jakub.kicinski@netronome.com \
    --cc=ast@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=ecree@solarflare.com \
    --cc=jiong.wang@netronome.com \
    --cc=kernel-team@fb.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).