All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexei Starovoitov <alexei.starovoitov@gmail.com>
To: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	kernel-team@fb.com, John Fastabend <john.fastabend@gmail.com>,
	ecree@solarflare.com
Subject: Re: [PATCH bpf-next 1/2] bpf: fix a verifier failure with xor
Date: Tue, 25 Aug 2020 18:58:36 -0700	[thread overview]
Message-ID: <20200826015836.2rlfvhoznylkabp6@ast-mbp.dhcp.thefacebook.com> (raw)
In-Reply-To: <20200825064608.2017937-1-yhs@fb.com>

On Mon, Aug 24, 2020 at 11:46:08PM -0700, Yonghong Song wrote:
> bpf selftest test_progs/test_sk_assign failed with llvm 11 and llvm 12.
> Compared to llvm 10, llvm 11 and 12 generates xor instruction which
> is not handled properly in verifier. The following illustrates the
> problem:
> 
>   16: (b4) w5 = 0
>   17: ... R5_w=inv0 ...
>   ...
>   132: (a4) w5 ^= 1
>   133: ... R5_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) ...
>   ...
>   37: (bc) w8 = w5
>   38: ... R5=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff))
>           R8_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) ...
>   ...
>   41: (bc) w3 = w8
>   42: ... R3_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) ...
>   45: (56) if w3 != 0x0 goto pc+1
>    ... R3_w=inv0 ...
>   46: (b7) r1 = 34
>   47: R1_w=inv34 R7=pkt(id=0,off=26,r=38,imm=0)
>   47: (0f) r7 += r1
>   48: R1_w=invP34 R3_w=inv0 R7_w=pkt(id=0,off=60,r=38,imm=0)
>   48: (b4) w9 = 0
>   49: R1_w=invP34 R3_w=inv0 R7_w=pkt(id=0,off=60,r=38,imm=0)
>   49: (69) r1 = *(u16 *)(r7 +0)
>   invalid access to packet, off=60 size=2, R7(id=0,off=60,r=38)
>   R7 offset is outside of the packet
> 
> At above insn 132, w5 = 0, but after w5 ^= 1, we give a really conservative
> value of w5. At insn 45, in reality the condition should be always false.
> But due to conservative value for w3, the verifier evaluates it could be
> true and this later leads to verifier failure complaining potential
> packet out-of-bound access.
> 
> This patch implemented proper XOR support in verifier.
> In the above example, we have:
>   132: R5=invP0
>   132: (a4) w5 ^= 1
>   133: R5_w=invP1
>   ...
>   37: (bc) w8 = w5
>   ...
>   41: (bc) w3 = w8
>   42: R3_w=invP1
>   ...
>   45: (56) if w3 != 0x0 goto pc+1
>   47: R3_w=invP1
>   ...
>   processed 353 insns ...
> and the verifier can verify the program successfully.
> 
> Cc: John Fastabend <john.fastabend@gmail.com>
> Signed-off-by: Yonghong Song <yhs@fb.com>
> ---
>  kernel/bpf/verifier.c | 66 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 66 insertions(+)
> 
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index dd24503ab3d3..a08cabc0f683 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -5801,6 +5801,67 @@ static void scalar_min_max_or(struct bpf_reg_state *dst_reg,
>  	__update_reg_bounds(dst_reg);
>  }
>  
> +static void scalar32_min_max_xor(struct bpf_reg_state *dst_reg,
> +				 struct bpf_reg_state *src_reg)
> +{
> +	bool src_known = tnum_subreg_is_const(src_reg->var_off);
> +	bool dst_known = tnum_subreg_is_const(dst_reg->var_off);
> +	struct tnum var32_off = tnum_subreg(dst_reg->var_off);
> +	s32 smin_val = src_reg->s32_min_value;
> +
> +	/* Assuming scalar64_min_max_xor will be called so it is safe
> +	 * to skip updating register for known case.
> +	 */
> +	if (src_known && dst_known)
> +		return;

why?
I've looked at _and() and _or() variants that do the same and
couldn't quite remember why it's ok to do so.

> +
> +	/* We get both minimum and maximum from the var32_off. */
> +	dst_reg->u32_min_value = var32_off.value;
> +	dst_reg->u32_max_value = var32_off.value | var32_off.mask;
> +
> +	if (dst_reg->s32_min_value >= 0 && smin_val >= 0) {
> +		/* XORing two positive sign numbers gives a positive,
> +		 * so safe to cast u32 result into s32.
> +		 */
> +		dst_reg->s32_min_value = dst_reg->u32_min_value;
> +		dst_reg->s32_max_value = dst_reg->u32_max_value;
> +	} else {
> +		dst_reg->s32_min_value = S32_MIN;
> +		dst_reg->s32_max_value = S32_MAX;
> +	}
> +}
> +
> +static void scalar_min_max_xor(struct bpf_reg_state *dst_reg,
> +			       struct bpf_reg_state *src_reg)
> +{
> +	bool src_known = tnum_is_const(src_reg->var_off);
> +	bool dst_known = tnum_is_const(dst_reg->var_off);
> +	s64 smin_val = src_reg->smin_value;
> +
> +	if (src_known && dst_known) {
> +		/* dst_reg->var_off.value has been updated earlier */

right, but that means that there is sort-of 'bug' (unnecessary operation)
that caused me a lot of head scratching.
scalar_min_max_and() and scalar_min_max_or() do the alu in similar situation:
        if (src_known && dst_known) {
                __mark_reg_known(dst_reg, dst_reg->var_off.value |
                                          src_reg->var_off.value);
I guess it's still technically correct to repeat alu operation.
second & and second | won't change the value of dst_reg,
but it feels that it's correct by accident?
John ?

> +		__mark_reg_known(dst_reg, dst_reg->var_off.value);
> +		return;
> +	}
> +
> +	/* We get both minimum and maximum from the var_off. */
> +	dst_reg->umin_value = dst_reg->var_off.value;
> +	dst_reg->umax_value = dst_reg->var_off.value | dst_reg->var_off.mask;

I think this is correct, but I hope somebody else can analyze this as well.
John, Ed ?

> +
> +	if (dst_reg->smin_value >= 0 && smin_val >= 0) {
> +		/* XORing two positive sign numbers gives a positive,
> +		 * so safe to cast u64 result into s64.
> +		 */
> +		dst_reg->smin_value = dst_reg->umin_value;
> +		dst_reg->smax_value = dst_reg->umax_value;
> +	} else {
> +		dst_reg->smin_value = S64_MIN;
> +		dst_reg->smax_value = S64_MAX;
> +	}
> +
> +	__update_reg_bounds(dst_reg);
> +}

  reply	other threads:[~2020-08-26  1:58 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-25  6:46 [PATCH bpf-next 0/2] fix a verifier failure with xor Yonghong Song
2020-08-25  6:46 ` [PATCH bpf-next 1/2] bpf: " Yonghong Song
2020-08-26  1:58   ` Alexei Starovoitov [this message]
2020-08-26  3:36     ` Yonghong Song
2020-08-26 22:06       ` John Fastabend
2020-08-27  5:12         ` Alexei Starovoitov
2020-08-27 18:43           ` John Fastabend
2020-09-01 20:07   ` Andrii Nakryiko
2020-09-02  2:17     ` Yonghong Song
2020-09-02  5:27       ` John Fastabend
2020-09-02  5:43         ` Yonghong Song
2020-09-04  5:29           ` John Fastabend
2020-09-02  9:33       ` Toke Høiland-Jørgensen
2020-09-02 14:21         ` Alexei Starovoitov
2020-09-02 15:01           ` Toke Høiland-Jørgensen
2020-09-02 21:40             ` Alexei Starovoitov
2020-08-25  6:46 ` [PATCH bpf-next 2/2] selftests/bpf: add verifier tests for xor operation Yonghong Song

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=20200826015836.2rlfvhoznylkabp6@ast-mbp.dhcp.thefacebook.com \
    --to=alexei.starovoitov@gmail.com \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=ecree@solarflare.com \
    --cc=john.fastabend@gmail.com \
    --cc=kernel-team@fb.com \
    --cc=yhs@fb.com \
    /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 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.