From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
To: Andrii Nakryiko <andrii@kernel.org>
Cc: bpf@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net,
martin.lau@kernel.org, kernel-team@meta.com
Subject: Re: [PATCH v4 bpf-next 3/7] bpf: enhance subregister bounds deduction logic
Date: Mon, 23 Oct 2023 11:20:46 +0800 [thread overview]
Message-ID: <ZTXmjp7AtrRpHZzR@u94a> (raw)
In-Reply-To: <20231022205743.72352-4-andrii@kernel.org>
On Sun, Oct 22, 2023 at 01:57:39PM -0700, Andrii Nakryiko wrote:
> Add handling of a bunch of possible cases which allows deducing extra
> information about subregister bounds, both u32 and s32, from full register
> u64/s64 bounds.
>
> Also add smin32/smax32 bounds derivation from corresponding umin32/umax32
> bounds, similar to what we did with smin/smax from umin/umax derivation in
> previous patch.
>
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
>
> ---
> kernel/bpf/verifier.c | 52 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 52 insertions(+)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 885dd4a2ff3a..3fc9bd5e72b8 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -2130,6 +2130,58 @@ static void __update_reg_bounds(struct bpf_reg_state *reg)
> /* Uses signed min/max values to inform unsigned, and vice-versa */
> static void __reg32_deduce_bounds(struct bpf_reg_state *reg)
> {
> + /* if upper 32 bits of u64/s64 range don't change,
> + * we can use lower 32 bits to improve our u32/s32 boundaries
> + */
> + if ((reg->umin_value >> 32) == (reg->umax_value >> 32)) {
> + /* u64 to u32 casting preserves validity of low 32 bits as
> + * a range, if upper 32 bits are the same
> + */
> + reg->u32_min_value = max_t(u32, reg->u32_min_value, (u32)reg->umin_value);
> + reg->u32_max_value = min_t(u32, reg->u32_max_value, (u32)reg->umax_value);
> +
> + if ((s32)reg->umin_value <= (s32)reg->umax_value) {
> + reg->s32_min_value = max_t(s32, reg->s32_min_value, (s32)reg->umin_value);
> + reg->s32_max_value = min_t(s32, reg->s32_max_value, (s32)reg->umax_value);
> + }
> + }
> + if ((reg->smin_value >> 32) == (reg->smax_value >> 32)) {
> + /* low 32 bits should form a proper u32 range */
> + if ((u32)reg->smin_value <= (u32)reg->smax_value) {
> + reg->u32_min_value = max_t(u32, reg->u32_min_value, (u32)reg->smin_value);
> + reg->u32_max_value = min_t(u32, reg->u32_max_value, (u32)reg->smax_value);
> + }
> + /* low 32 bits should form a proper s32 range */
> + if ((s32)reg->smin_value <= (s32)reg->smax_value) {
> + reg->s32_min_value = max_t(s32, reg->s32_min_value, (s32)reg->smin_value);
> + reg->s32_max_value = min_t(s32, reg->s32_max_value, (s32)reg->smax_value);
> + }
> + }
> + /* Special case where upper bits form a small sequence of two
> + * sequential numbers (in 32-bit unsigned space, so 0xffffffff to
> + * 0x00000000 is also valid), while lower bits form a proper s32 range
> + * going from negative numbers to positive numbers.
> + * E.g.: [0xfffffff0ffffff00; 0xfffffff100000010]. Iterating
> + * over full 64-bit numbers range will form a proper [-16, 16]
> + * ([0xffffff00; 0x00000010]) range in its lower 32 bits.
> + */
Not sure if we want ascii art here but though it'd be useful to share. It
took a while to wrap my head around this concept until I look at this as
number lines.
Say we've got umin, umax tracked like so (asterisk * marks the sequence of
numbers we believe is possible to occur).
u64
|--------***--------------|
{ 32-bits }{ 32-bits }
And s32_min, s32_max tracked liked so.
s32
|***---------|
The above u64 range can be mapped into two possible s32 range when we've
removed the upper 32-bits.
u64 same u64 wrapped
|--------***--------------|-----...
|||
|--***-------|------------|
s32 s32
Since both s32 range are possible, we take the union between then, and the
s32 range we're already tracking
|------------|
|--***-------|
|***---------|
And arrives at the final s32 range.
|*****-------|
Taking this (wrapped) number line view and operates them with set operations
(latter is similar to what tnum does) is quite useful and I think hints that
we may be able to unify signed and unsigned range tracking. I'll look into
this a bit more and send a follow up.
> + if ((u32)(reg->umin_value >> 32) + 1 == (u32)(reg->umax_value >> 32) &&
> + (s32)reg->umin_value < 0 && (s32)reg->umax_value >= 0) {
> + reg->s32_min_value = max_t(s32, reg->s32_min_value, (s32)reg->umin_value);
> + reg->s32_max_value = min_t(s32, reg->s32_max_value, (s32)reg->umax_value);
> + }
> + if ((u32)(reg->smin_value >> 32) + 1 == (u32)(reg->smax_value >> 32) &&
> + (s32)reg->smin_value < 0 && (s32)reg->smax_value >= 0) {
> + reg->s32_min_value = max_t(s32, reg->s32_min_value, (s32)reg->smin_value);
> + reg->s32_max_value = min_t(s32, reg->s32_max_value, (s32)reg->smax_value);
> + }
> + /* if u32 range forms a valid s32 range (due to matching sign bit),
> + * try to learn from that
> + */
> + if ((s32)reg->u32_min_value <= (s32)reg->u32_max_value) {
> + reg->s32_min_value = max_t(s32, reg->s32_min_value, reg->u32_min_value);
> + reg->s32_max_value = min_t(s32, reg->s32_max_value, reg->u32_max_value);
> + }
> /* Learn sign from signed bounds.
> * If we cannot cross the sign boundary, then signed and unsigned bounds
> * are the same, so combine. This works even in the negative case, e.g.
> --
> 2.34.1
>
next prev parent reply other threads:[~2023-10-23 3:20 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-22 20:57 [PATCH v4 bpf-next 0/7] BPF register bounds logic and testing improvements Andrii Nakryiko
2023-10-22 20:57 ` [PATCH v4 bpf-next 1/7] bpf: improve JEQ/JNE branch taken logic Andrii Nakryiko
2023-10-23 2:33 ` Shung-Hsi Yu
2023-10-22 20:57 ` [PATCH v4 bpf-next 2/7] bpf: derive smin/smax from umin/max bounds Andrii Nakryiko
2023-10-23 2:36 ` Shung-Hsi Yu
2023-10-24 13:08 ` Daniel Borkmann
2023-10-24 14:53 ` Andrii Nakryiko
2023-10-24 15:43 ` Daniel Borkmann
2023-10-22 20:57 ` [PATCH v4 bpf-next 3/7] bpf: enhance subregister bounds deduction logic Andrii Nakryiko
2023-10-23 3:20 ` Shung-Hsi Yu [this message]
2023-10-23 3:56 ` Shung-Hsi Yu
2023-10-23 16:23 ` Andrii Nakryiko
2023-10-24 12:05 ` Shung-Hsi Yu
2023-10-24 12:22 ` Shung-Hsi Yu
2023-10-24 15:31 ` Alexei Starovoitov
2023-10-27 8:16 ` Shung-Hsi Yu
2023-10-22 20:57 ` [PATCH v4 bpf-next 4/7] bpf: improve deduction of 64-bit bounds from 32-bit bounds Andrii Nakryiko
2023-10-22 20:57 ` [PATCH v4 bpf-next 5/7] bpf: try harder to deduce register bounds from different numeric domains Andrii Nakryiko
2023-10-22 20:57 ` [PATCH v4 bpf-next 6/7] bpf: drop knowledge-losing __reg_combine_{32,64}_into_{64,32} logic Andrii Nakryiko
2023-10-22 20:57 ` [PATCH v4 bpf-next 7/7] selftests/bpf: BPF register range bounds tester Andrii Nakryiko
2023-10-22 23:01 ` kernel test robot
2023-10-24 13:40 ` [PATCH v4 bpf-next 0/7] BPF register bounds logic and testing improvements patchwork-bot+netdevbpf
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=ZTXmjp7AtrRpHZzR@u94a \
--to=shung-hsi.yu@suse.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@meta.com \
--cc=martin.lau@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 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.