* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-05 19:48 ` [PATCH bpf v2 1/2] " Eduard Zingerman
@ 2026-03-05 20:28 ` bot+bpf-ci
2026-03-05 20:31 ` Eduard Zingerman
2026-03-05 20:51 ` Emil Tsalapatis
2026-03-06 0:13 ` Paul Chaignon
2 siblings, 1 reply; 19+ messages in thread
From: bot+bpf-ci @ 2026-03-05 20:28 UTC (permalink / raw)
To: eddyz87, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87, emil,
arighi, shung-hsi.yu, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 891 bytes --]
> Reported-by: Andrea Righi <arighi@nvidia.com>
> Reported-by: Emil Tsalapatis <emil@etsalapatis.com>
> Closes: https://lore.kernel.org/bpf/aakqucg4vcujVwif@gpd4/T/
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit fixes a missing refinement that was noted as a known gap
when the 64-bit version was added. Paul Chaignon's commit message for
00bf8d0c6c9b stated: "The same logic can probably apply to the s32/u32
ranges, but this patch doesn't implement that change."
Should a Fixes: tag be included to reference that commit? The
suggested tag would be:
Fixes: 00bf8d0c6c9b ("bpf: Improve bounds when s64 crosses sign boundary")
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22734151945
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-05 20:28 ` bot+bpf-ci
@ 2026-03-05 20:31 ` Eduard Zingerman
0 siblings, 0 replies; 19+ messages in thread
From: Eduard Zingerman @ 2026-03-05 20:31 UTC (permalink / raw)
To: bot+bpf-ci, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, emil, arighi,
shung-hsi.yu, martin.lau, clm, ihor.solodrai
On Thu, 2026-03-05 at 20:28 +0000, bot+bpf-ci@kernel.org wrote:
> > Reported-by: Andrea Righi <arighi@nvidia.com>
> > Reported-by: Emil Tsalapatis <emil@etsalapatis.com>
> > Closes: https://lore.kernel.org/bpf/aakqucg4vcujVwif@gpd4/T/
> > Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
>
> This commit fixes a missing refinement that was noted as a known gap
> when the 64-bit version was added. Paul Chaignon's commit message for
> 00bf8d0c6c9b stated: "The same logic can probably apply to the s32/u32
> ranges, but this patch doesn't implement that change."
>
> Should a Fixes: tag be included to reference that commit? The
> suggested tag would be:
>
> Fixes: 00bf8d0c6c9b ("bpf: Improve bounds when s64 crosses sign boundary")
I disagree with this assessment, there is nothing wrong with 00bf8d0c6c9b.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-05 19:48 ` [PATCH bpf v2 1/2] " Eduard Zingerman
2026-03-05 20:28 ` bot+bpf-ci
@ 2026-03-05 20:51 ` Emil Tsalapatis
2026-03-06 0:13 ` Paul Chaignon
2 siblings, 0 replies; 19+ messages in thread
From: Emil Tsalapatis @ 2026-03-05 20:51 UTC (permalink / raw)
To: Eduard Zingerman, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, arighi,
shung-hsi.yu
On Thu Mar 5, 2026 at 2:48 PM EST, Eduard Zingerman wrote:
> Same as in __reg64_deduce_bounds(), refine s32/u32 ranges
> in __reg32_deduce_bounds() in the following situations:
>
> - s32 range crosses U32_MAX/0 boundary, positive part of the s32 range
> overlaps with u32 range:
>
> 0 U32_MAX
> | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> |----------------------------|----------------------------|
> |xxxxx s32 range xxxxxxxxx] [xxxxxxx|
> 0 S32_MAX S32_MIN -1
>
> - s32 range crosses U32_MAX/0 boundary, negative part of the s32 range
> overlaps with u32 range:
>
> 0 U32_MAX
> | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> |----------------------------|----------------------------|
> |xxxxxxxxx] [xxxxxxxxxxxx s32 range |
> 0 S32_MAX S32_MIN -1
>
> - No refinement if ranges overlap in two intervals.
>
> This helps for e.g. consider the following program:
>
> call %[bpf_get_prandom_u32];
> w0 &= 0xffffffff;
> if w0 < 0x3 goto 1f; // on fall-through u32 range [3..U32_MAX]
> if w0 s> 0x1 goto 1f; // on fall-through s32 range [S32_MIN..1]
> if w0 s< 0x0 goto 1f; // range can be narrowed to [S32_MIN..-1]
> r10 = 0;
> 1: ...;
>
> The reg_bounds.c selftest is updated to incorporate identical logic,
> refinement based on non-overflowing range halves:
>
> ((x ∩ [0, smax]) ∩ (y ∩ [0, smax])) ∪
> ((x ∩ [smin,-1]) ∩ (y ∩ [smin,-1]))
>
> Reported-by: Andrea Righi <arighi@nvidia.com>
> Reported-by: Emil Tsalapatis <emil@etsalapatis.com>
> Closes: https://lore.kernel.org/bpf/aakqucg4vcujVwif@gpd4/T/
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> ---
> kernel/bpf/verifier.c | 24 +++++++++
> .../testing/selftests/bpf/prog_tests/reg_bounds.c | 62 ++++++++++++++++++++--
> 2 files changed, 82 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 401d6c4960eccfa90893660b7d8aece859787f7f..f960b382fdb3d4a4f5f2a66a525c2f594de529ff 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -2511,6 +2511,30 @@ static void __reg32_deduce_bounds(struct bpf_reg_state *reg)
> if ((u32)reg->s32_min_value <= (u32)reg->s32_max_value) {
> reg->u32_min_value = max_t(u32, reg->s32_min_value, reg->u32_min_value);
> reg->u32_max_value = min_t(u32, reg->s32_max_value, reg->u32_max_value);
> + } else {
> + if (reg->u32_max_value < (u32)reg->s32_min_value) {
> + /* See __reg64_deduce_bounds() for detailed explanation.
> + * Refine ranges in the following situation:
> + *
> + * 0 U32_MAX
> + * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> + * |----------------------------|----------------------------|
> + * |xxxxx s32 range xxxxxxxxx] [xxxxxxx|
> + * 0 S32_MAX S32_MIN -1
> + */
> + reg->s32_min_value = (s32)reg->u32_min_value;
> + reg->u32_max_value = min_t(u32, reg->u32_max_value, reg->s32_max_value);
> + } else if ((u32)reg->s32_max_value < reg->u32_min_value) {
> + /*
> + * 0 U32_MAX
> + * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> + * |----------------------------|----------------------------|
> + * |xxxxxxxxx] [xxxxxxxxxxxx s32 range |
> + * 0 S32_MAX S32_MIN -1
> + */
> + reg->s32_max_value = (s32)reg->u32_max_value;
> + reg->u32_min_value = max_t(u32, reg->u32_min_value, reg->s32_min_value);
> + }
> }
> }
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
> }
> }
>
> -static struct range range_improve(enum num_t t, struct range old, struct range new)
> +static struct range range_intersection(enum num_t t, struct range old, struct range new)
> {
> return range(t, max_t(t, old.a, new.a), min_t(t, old.b, new.b));
> }
>
> +/*
> + * Result is precise when 'x' and 'y' overlap or form a continuous range,
> + * result is an over-approximation if 'x' and 'y' do not overlap.
> + */
> +static struct range range_union(enum num_t t, struct range x, struct range y)
> +{
> + if (!is_valid_range(t, x))
> + return y;
> + if (!is_valid_range(t, y))
> + return x;
> + return range(t, min_t(t, x.a, y.a), max_t(t, x.b, y.b));
> +}
> +
> +/*
> + * This function attempts to improve x range intersecting it with y.
> + * range_cast(... to_t ...) looses precision for ranges that pass to_t
> + * min/max boundaries. To avoid such precision loses this function
> + * splits both x and y into halves corresponding to non-overflowing
> + * sub-ranges: [0, smin] and [smax, -1].
> + * Final result is computed as follows:
> + *
> + * ((x ∩ [0, smax]) ∩ (y ∩ [0, smax])) ∪
> + * ((x ∩ [smin,-1]) ∩ (y ∩ [smin,-1]))
> + *
> + * Precision might still be lost if final union is not a continuous range.
> + */
> +static struct range range_refine_in_halves(enum num_t x_t, struct range x,
> + enum num_t y_t, struct range y)
> +{
> + struct range x_pos, x_neg, y_pos, y_neg, r_pos, r_neg;
> + u64 smax, smin, neg_one;
> +
> + if (t_is_32(x_t)) {
> + smax = (u64)(u32)S32_MAX;
> + smin = (u64)(u32)S32_MIN;
> + neg_one = (u64)(u32)(s32)(-1);
> + } else {
> + smax = (u64)S64_MAX;
> + smin = (u64)S64_MIN;
> + neg_one = U64_MAX;
> + }
> + x_pos = range_intersection(x_t, x, range(x_t, 0, smax));
> + x_neg = range_intersection(x_t, x, range(x_t, smin, neg_one));
> + y_pos = range_intersection(y_t, y, range(x_t, 0, smax));
> + y_neg = range_intersection(y_t, y, range(y_t, smin, neg_one));
> + r_pos = range_intersection(x_t, x_pos, range_cast(y_t, x_t, y_pos));
> + r_neg = range_intersection(x_t, x_neg, range_cast(y_t, x_t, y_neg));
> + return range_union(x_t, r_pos, r_neg);
> +
> +}
> +
> static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t, struct range y)
> {
> struct range y_cast;
>
> + if (t_is_32(x_t) == t_is_32(y_t))
> + x = range_refine_in_halves(x_t, x, y_t, y);
> +
> y_cast = range_cast(y_t, x_t, y);
>
> /* If we know that
> @@ -444,7 +498,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> */
> if (x_t == S64 && y_t == S32 && y_cast.a <= S32_MAX && y_cast.b <= S32_MAX &&
> (s64)x.a >= S32_MIN && (s64)x.b <= S32_MAX)
> - return range_improve(x_t, x, y_cast);
> + return range_intersection(x_t, x, y_cast);
>
> /* the case when new range knowledge, *y*, is a 32-bit subregister
> * range, while previous range knowledge, *x*, is a full register
> @@ -462,7 +516,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> x_swap = range(x_t, swap_low32(x.a, y_cast.a), swap_low32(x.b, y_cast.b));
> if (!is_valid_range(x_t, x_swap))
> return x;
> - return range_improve(x_t, x, x_swap);
> + return range_intersection(x_t, x, x_swap);
> }
>
> if (!t_is_32(x_t) && !t_is_32(y_t) && x_t != y_t) {
> @@ -480,7 +534,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> }
>
> /* otherwise, plain range cast and intersection works */
> - return range_improve(x_t, x, y_cast);
> + return range_intersection(x_t, x, y_cast);
> }
>
> /* =======================
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-05 19:48 ` [PATCH bpf v2 1/2] " Eduard Zingerman
2026-03-05 20:28 ` bot+bpf-ci
2026-03-05 20:51 ` Emil Tsalapatis
@ 2026-03-06 0:13 ` Paul Chaignon
2026-03-06 0:18 ` Eduard Zingerman
2 siblings, 1 reply; 19+ messages in thread
From: Paul Chaignon @ 2026-03-06 0:13 UTC (permalink / raw)
To: Eduard Zingerman
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
emil, arighi, shung-hsi.yu
On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
[...]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 401d6c4960eccfa90893660b7d8aece859787f7f..f960b382fdb3d4a4f5f2a66a525c2f594de529ff 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -2511,6 +2511,30 @@ static void __reg32_deduce_bounds(struct bpf_reg_state *reg)
> if ((u32)reg->s32_min_value <= (u32)reg->s32_max_value) {
> reg->u32_min_value = max_t(u32, reg->s32_min_value, reg->u32_min_value);
> reg->u32_max_value = min_t(u32, reg->s32_max_value, reg->u32_max_value);
> + } else {
> + if (reg->u32_max_value < (u32)reg->s32_min_value) {
> + /* See __reg64_deduce_bounds() for detailed explanation.
> + * Refine ranges in the following situation:
> + *
> + * 0 U32_MAX
> + * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> + * |----------------------------|----------------------------|
> + * |xxxxx s32 range xxxxxxxxx] [xxxxxxx|
> + * 0 S32_MAX S32_MIN -1
> + */
> + reg->s32_min_value = (s32)reg->u32_min_value;
> + reg->u32_max_value = min_t(u32, reg->u32_max_value, reg->s32_max_value);
> + } else if ((u32)reg->s32_max_value < reg->u32_min_value) {
> + /*
> + * 0 U32_MAX
> + * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> + * |----------------------------|----------------------------|
> + * |xxxxxxxxx] [xxxxxxxxxxxx s32 range |
> + * 0 S32_MAX S32_MIN -1
> + */
> + reg->s32_max_value = (s32)reg->u32_max_value;
> + reg->u32_min_value = max_t(u32, reg->u32_min_value, reg->s32_min_value);
> + }
Looks good to me. I also ran it through Agni and __reg_deduce_bounds
(aka special instruction BPF_SYNC2) is still found to be sound.
> }
> }
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
> }
> }
>
> -static struct range range_improve(enum num_t t, struct range old, struct range new)
> +static struct range range_intersection(enum num_t t, struct range old, struct range new)
> {
> return range(t, max_t(t, old.a, new.a), min_t(t, old.b, new.b));
> }
>
> +/*
> + * Result is precise when 'x' and 'y' overlap or form a continuous range,
> + * result is an over-approximation if 'x' and 'y' do not overlap.
> + */
> +static struct range range_union(enum num_t t, struct range x, struct range y)
> +{
> + if (!is_valid_range(t, x))
> + return y;
> + if (!is_valid_range(t, y))
> + return x;
> + return range(t, min_t(t, x.a, y.a), max_t(t, x.b, y.b));
> +}
> +
> +/*
> + * This function attempts to improve x range intersecting it with y.
> + * range_cast(... to_t ...) looses precision for ranges that pass to_t
> + * min/max boundaries. To avoid such precision loses this function
> + * splits both x and y into halves corresponding to non-overflowing
> + * sub-ranges: [0, smin] and [smax, -1].
> + * Final result is computed as follows:
> + *
> + * ((x ∩ [0, smax]) ∩ (y ∩ [0, smax])) ∪
> + * ((x ∩ [smin,-1]) ∩ (y ∩ [smin,-1]))
> + *
> + * Precision might still be lost if final union is not a continuous range.
> + */
> +static struct range range_refine_in_halves(enum num_t x_t, struct range x,
> + enum num_t y_t, struct range y)
> +{
> + struct range x_pos, x_neg, y_pos, y_neg, r_pos, r_neg;
> + u64 smax, smin, neg_one;
> +
> + if (t_is_32(x_t)) {
> + smax = (u64)(u32)S32_MAX;
> + smin = (u64)(u32)S32_MIN;
> + neg_one = (u64)(u32)(s32)(-1);
> + } else {
> + smax = (u64)S64_MAX;
> + smin = (u64)S64_MIN;
> + neg_one = U64_MAX;
> + }
> + x_pos = range_intersection(x_t, x, range(x_t, 0, smax));
> + x_neg = range_intersection(x_t, x, range(x_t, smin, neg_one));
> + y_pos = range_intersection(y_t, y, range(x_t, 0, smax));
> + y_neg = range_intersection(y_t, y, range(y_t, smin, neg_one));
> + r_pos = range_intersection(x_t, x_pos, range_cast(y_t, x_t, y_pos));
> + r_neg = range_intersection(x_t, x_neg, range_cast(y_t, x_t, y_neg));
> + return range_union(x_t, r_pos, r_neg);
> +
> +}
> +
> static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t, struct range y)
> {
> struct range y_cast;
>
> + if (t_is_32(x_t) == t_is_32(y_t))
> + x = range_refine_in_halves(x_t, x, y_t, y);
Don't we usually put changes to this file in a separate commit, as for
test changes in general?
Also I believe with these changes, we can now revert commit da653de268d3
("selftests/bpf: Update reg_bound range refinement logic").
> +
> y_cast = range_cast(y_t, x_t, y);
>
> /* If we know that
> @@ -444,7 +498,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> */
> if (x_t == S64 && y_t == S32 && y_cast.a <= S32_MAX && y_cast.b <= S32_MAX &&
> (s64)x.a >= S32_MIN && (s64)x.b <= S32_MAX)
> - return range_improve(x_t, x, y_cast);
> + return range_intersection(x_t, x, y_cast);
>
> /* the case when new range knowledge, *y*, is a 32-bit subregister
> * range, while previous range knowledge, *x*, is a full register
> @@ -462,7 +516,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> x_swap = range(x_t, swap_low32(x.a, y_cast.a), swap_low32(x.b, y_cast.b));
> if (!is_valid_range(x_t, x_swap))
> return x;
> - return range_improve(x_t, x, x_swap);
> + return range_intersection(x_t, x, x_swap);
> }
>
> if (!t_is_32(x_t) && !t_is_32(y_t) && x_t != y_t) {
> @@ -480,7 +534,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> }
>
> /* otherwise, plain range cast and intersection works */
> - return range_improve(x_t, x, y_cast);
> + return range_intersection(x_t, x, y_cast);
> }
>
> /* =======================
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-06 0:13 ` Paul Chaignon
@ 2026-03-06 0:18 ` Eduard Zingerman
2026-03-06 0:24 ` Paul Chaignon
0 siblings, 1 reply; 19+ messages in thread
From: Eduard Zingerman @ 2026-03-06 0:18 UTC (permalink / raw)
To: Paul Chaignon
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
emil, arighi, shung-hsi.yu
On Fri, 2026-03-06 at 01:13 +0100, Paul Chaignon wrote:
> On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
>
> [...]
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 401d6c4960eccfa90893660b7d8aece859787f7f..f960b382fdb3d4a4f5f2a66a525c2f594de529ff 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -2511,6 +2511,30 @@ static void __reg32_deduce_bounds(struct bpf_reg_state *reg)
> > if ((u32)reg->s32_min_value <= (u32)reg->s32_max_value) {
> > reg->u32_min_value = max_t(u32, reg->s32_min_value, reg->u32_min_value);
> > reg->u32_max_value = min_t(u32, reg->s32_max_value, reg->u32_max_value);
> > + } else {
> > + if (reg->u32_max_value < (u32)reg->s32_min_value) {
> > + /* See __reg64_deduce_bounds() for detailed explanation.
> > + * Refine ranges in the following situation:
> > + *
> > + * 0 U32_MAX
> > + * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> > + * |----------------------------|----------------------------|
> > + * |xxxxx s32 range xxxxxxxxx] [xxxxxxx|
> > + * 0 S32_MAX S32_MIN -1
> > + */
> > + reg->s32_min_value = (s32)reg->u32_min_value;
> > + reg->u32_max_value = min_t(u32, reg->u32_max_value, reg->s32_max_value);
> > + } else if ((u32)reg->s32_max_value < reg->u32_min_value) {
> > + /*
> > + * 0 U32_MAX
> > + * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
> > + * |----------------------------|----------------------------|
> > + * |xxxxxxxxx] [xxxxxxxxxxxx s32 range |
> > + * 0 S32_MAX S32_MIN -1
> > + */
> > + reg->s32_max_value = (s32)reg->u32_max_value;
> > + reg->u32_min_value = max_t(u32, reg->u32_min_value, reg->s32_min_value);
> > + }
>
> Looks good to me. I also ran it through Agni and __reg_deduce_bounds
> (aka special instruction BPF_SYNC2) is still found to be sound.
Great, thank you!
>
> > }
> > }
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
> > }
> > }
> >
> > -static struct range range_improve(enum num_t t, struct range old, struct range new)
> > +static struct range range_intersection(enum num_t t, struct range old, struct range new)
> > {
> > return range(t, max_t(t, old.a, new.a), min_t(t, old.b, new.b));
> > }
> >
> > +/*
> > + * Result is precise when 'x' and 'y' overlap or form a continuous range,
> > + * result is an over-approximation if 'x' and 'y' do not overlap.
> > + */
> > +static struct range range_union(enum num_t t, struct range x, struct range y)
> > +{
> > + if (!is_valid_range(t, x))
> > + return y;
> > + if (!is_valid_range(t, y))
> > + return x;
> > + return range(t, min_t(t, x.a, y.a), max_t(t, x.b, y.b));
> > +}
> > +
> > +/*
> > + * This function attempts to improve x range intersecting it with y.
> > + * range_cast(... to_t ...) looses precision for ranges that pass to_t
> > + * min/max boundaries. To avoid such precision loses this function
> > + * splits both x and y into halves corresponding to non-overflowing
> > + * sub-ranges: [0, smin] and [smax, -1].
> > + * Final result is computed as follows:
> > + *
> > + * ((x ∩ [0, smax]) ∩ (y ∩ [0, smax])) ∪
> > + * ((x ∩ [smin,-1]) ∩ (y ∩ [smin,-1]))
> > + *
> > + * Precision might still be lost if final union is not a continuous range.
> > + */
> > +static struct range range_refine_in_halves(enum num_t x_t, struct range x,
> > + enum num_t y_t, struct range y)
> > +{
> > + struct range x_pos, x_neg, y_pos, y_neg, r_pos, r_neg;
> > + u64 smax, smin, neg_one;
> > +
> > + if (t_is_32(x_t)) {
> > + smax = (u64)(u32)S32_MAX;
> > + smin = (u64)(u32)S32_MIN;
> > + neg_one = (u64)(u32)(s32)(-1);
> > + } else {
> > + smax = (u64)S64_MAX;
> > + smin = (u64)S64_MIN;
> > + neg_one = U64_MAX;
> > + }
> > + x_pos = range_intersection(x_t, x, range(x_t, 0, smax));
> > + x_neg = range_intersection(x_t, x, range(x_t, smin, neg_one));
> > + y_pos = range_intersection(y_t, y, range(x_t, 0, smax));
> > + y_neg = range_intersection(y_t, y, range(y_t, smin, neg_one));
> > + r_pos = range_intersection(x_t, x_pos, range_cast(y_t, x_t, y_pos));
> > + r_neg = range_intersection(x_t, x_neg, range_cast(y_t, x_t, y_neg));
> > + return range_union(x_t, r_pos, r_neg);
> > +
> > +}
> > +
> > static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t, struct range y)
> > {
> > struct range y_cast;
> >
> > + if (t_is_32(x_t) == t_is_32(y_t))
> > + x = range_refine_in_halves(x_t, x, y_t, y);
>
> Don't we usually put changes to this file in a separate commit, as for
> test changes in general?
W/o this change the reg_bounds.c tests would fail.
So far we tried to keep selftests passing for every commit,
to help with any potential bisecting.
>
> Also I believe with these changes, we can now revert commit da653de268d3
> ("selftests/bpf: Update reg_bound range refinement logic").
Makes sense, thank you for pointing this out.
> > +
> > y_cast = range_cast(y_t, x_t, y);
> >
> > /* If we know that
> > @@ -444,7 +498,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> > */
> > if (x_t == S64 && y_t == S32 && y_cast.a <= S32_MAX && y_cast.b <= S32_MAX &&
> > (s64)x.a >= S32_MIN && (s64)x.b <= S32_MAX)
> > - return range_improve(x_t, x, y_cast);
> > + return range_intersection(x_t, x, y_cast);
> >
> > /* the case when new range knowledge, *y*, is a 32-bit subregister
> > * range, while previous range knowledge, *x*, is a full register
> > @@ -462,7 +516,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> > x_swap = range(x_t, swap_low32(x.a, y_cast.a), swap_low32(x.b, y_cast.b));
> > if (!is_valid_range(x_t, x_swap))
> > return x;
> > - return range_improve(x_t, x, x_swap);
> > + return range_intersection(x_t, x, x_swap);
> > }
> >
> > if (!t_is_32(x_t) && !t_is_32(y_t) && x_t != y_t) {
> > @@ -480,7 +534,7 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,
> > }
> >
> > /* otherwise, plain range cast and intersection works */
> > - return range_improve(x_t, x, y_cast);
> > + return range_intersection(x_t, x, y_cast);
> > }
> >
> > /* =======================
> >
> > --
> > 2.53.0
> >
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-06 0:18 ` Eduard Zingerman
@ 2026-03-06 0:24 ` Paul Chaignon
2026-03-12 6:45 ` Shung-Hsi Yu
0 siblings, 1 reply; 19+ messages in thread
From: Paul Chaignon @ 2026-03-06 0:24 UTC (permalink / raw)
To: Eduard Zingerman
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
emil, arighi, shung-hsi.yu
On Thu, Mar 05, 2026 at 04:18:42PM -0800, Eduard Zingerman wrote:
> On Fri, 2026-03-06 at 01:13 +0100, Paul Chaignon wrote:
> > On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
[...]
> > > diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> > > --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
[...]
> > Don't we usually put changes to this file in a separate commit, as for
> > test changes in general?
>
> W/o this change the reg_bounds.c tests would fail.
> So far we tried to keep selftests passing for every commit,
> to help with any potential bisecting.
Ah, I didn't know that. Definitely makes sense for bisecting.
I probably broke it at least once :(
[...]
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-06 0:24 ` Paul Chaignon
@ 2026-03-12 6:45 ` Shung-Hsi Yu
2026-03-17 15:37 ` Paul Chaignon
0 siblings, 1 reply; 19+ messages in thread
From: Shung-Hsi Yu @ 2026-03-12 6:45 UTC (permalink / raw)
To: Paul Chaignon
Cc: Eduard Zingerman, bpf, ast, andrii, daniel, martin.lau,
kernel-team, yonghong.song, emil, arighi
On Fri, Mar 06, 2026 at 01:24:28AM +0100, Paul Chaignon wrote:
> On Thu, Mar 05, 2026 at 04:18:42PM -0800, Eduard Zingerman wrote:
> > On Fri, 2026-03-06 at 01:13 +0100, Paul Chaignon wrote:
> > > On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
>
> [...]
>
> > > > diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> > > > --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
>
> [...]
>
> > > Don't we usually put changes to this file in a separate commit, as for
> > > test changes in general?
> >
> > W/o this change the reg_bounds.c tests would fail.
> > So far we tried to keep selftests passing for every commit,
> > to help with any potential bisecting.
Similarly we have stable AUTOSEL tends to just pick up kernel/bpf/
changes and not the selftests ones. e.g. both 76e954155b45 ("bpf:
Introduce tnum_step to step through tnum's members") and efc11a667878
("bpf: Improve bounds when tnum has a single possible value") got picked
for 6.18.17-rc1, but not 024cea2d647e "selftests/bpf: Avoid
simplification of crafted bounds test".
> Ah, I didn't know that. Definitely makes sense for bisecting.
> I probably broke it at least once :(
>
> [...]
>
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-12 6:45 ` Shung-Hsi Yu
@ 2026-03-17 15:37 ` Paul Chaignon
2026-03-19 7:03 ` Shung-Hsi Yu
0 siblings, 1 reply; 19+ messages in thread
From: Paul Chaignon @ 2026-03-17 15:37 UTC (permalink / raw)
To: Shung-Hsi Yu
Cc: Eduard Zingerman, bpf, ast, andrii, daniel, martin.lau,
kernel-team, yonghong.song, emil, arighi
On Thu, Mar 12, 2026 at 02:45:47PM +0800, Shung-Hsi Yu wrote:
> On Fri, Mar 06, 2026 at 01:24:28AM +0100, Paul Chaignon wrote:
> > On Thu, Mar 05, 2026 at 04:18:42PM -0800, Eduard Zingerman wrote:
> > > On Fri, 2026-03-06 at 01:13 +0100, Paul Chaignon wrote:
> > > > On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
> >
> > [...]
> >
> > > > > diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> > > > > --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
> >
> > [...]
> >
> > > > Don't we usually put changes to this file in a separate commit, as for
> > > > test changes in general?
> > >
> > > W/o this change the reg_bounds.c tests would fail.
> > > So far we tried to keep selftests passing for every commit,
> > > to help with any potential bisecting.
>
> Similarly we have stable AUTOSEL tends to just pick up kernel/bpf/
> changes and not the selftests ones. e.g. both 76e954155b45 ("bpf:
> Introduce tnum_step to step through tnum's members") and efc11a667878
> ("bpf: Improve bounds when tnum has a single possible value") got picked
> for 6.18.17-rc1, but not 024cea2d647e "selftests/bpf: Avoid
> simplification of crafted bounds test".
I can send the selftest backports to the stable ml (unless you already
are on it?)
Do you know if there's anything I could have done differently to have
the selftests picked up? Or maybe something we could change in AUTOSEL?
>
> > Ah, I didn't know that. Definitely makes sense for bisecting.
> > I probably broke it at least once :(
> >
> > [...]
> >
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-17 15:37 ` Paul Chaignon
@ 2026-03-19 7:03 ` Shung-Hsi Yu
2026-03-19 10:21 ` Paul Chaignon
0 siblings, 1 reply; 19+ messages in thread
From: Shung-Hsi Yu @ 2026-03-19 7:03 UTC (permalink / raw)
To: Paul Chaignon
Cc: Eduard Zingerman, bpf, ast, andrii, daniel, martin.lau,
kernel-team, yonghong.song, emil, arighi
On Tue, Mar 17, 2026 at 04:37:59PM +0100, Paul Chaignon wrote:
> On Thu, Mar 12, 2026 at 02:45:47PM +0800, Shung-Hsi Yu wrote:
> > On Fri, Mar 06, 2026 at 01:24:28AM +0100, Paul Chaignon wrote:
> > > On Thu, Mar 05, 2026 at 04:18:42PM -0800, Eduard Zingerman wrote:
> > > > On Fri, 2026-03-06 at 01:13 +0100, Paul Chaignon wrote:
> > > > > On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
> > >
> > > [...]
> > >
> > > > > > diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > > index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> > > > > > --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > > +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > > @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
> > >
> > > [...]
> > >
> > > > > Don't we usually put changes to this file in a separate commit, as for
> > > > > test changes in general?
> > > >
> > > > W/o this change the reg_bounds.c tests would fail.
> > > > So far we tried to keep selftests passing for every commit,
> > > > to help with any potential bisecting.
> >
> > Similarly we have stable AUTOSEL tends to just pick up kernel/bpf/
> > changes and not the selftests ones. e.g. both 76e954155b45 ("bpf:
> > Introduce tnum_step to step through tnum's members") and efc11a667878
> > ("bpf: Improve bounds when tnum has a single possible value") got picked
> > for 6.18.17-rc1, but not 024cea2d647e "selftests/bpf: Avoid
> > simplification of crafted bounds test".
>
> I can send the selftest backports to the stable ml (unless you already
> are on it?)
Requested in https://lore.kernel.org/all/xbjybmha7fdnnm5gn6p6jyppf4ud2r72rfabvej6egg545ozsu@a4qj43d3iu36/
We should be good now.
> Do you know if there's anything I could have done differently to have
> the selftests picked up? ...
In this specific case above it would to be have the selftest change
along with the kernel/bpf/verifier.c changes, all in one commit, like
Eduard has reasoned.
> Or maybe something we could change in AUTOSEL?
We can argue that the selftest was dependency that should go together,
and that AUTOSEL should pick it up, but IIRC it is design to only pick
up dependencies that comes before a commit. OTOH it would be nice to
have it try picking up selftests in the same series whenever it picks a
commit from there, but that sounds like a bigger ask.
...
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH bpf v2 1/2] bpf: refine u32/s32 bounds when ranges cross min/max boundary
2026-03-19 7:03 ` Shung-Hsi Yu
@ 2026-03-19 10:21 ` Paul Chaignon
0 siblings, 0 replies; 19+ messages in thread
From: Paul Chaignon @ 2026-03-19 10:21 UTC (permalink / raw)
To: Shung-Hsi Yu
Cc: Eduard Zingerman, bpf, ast, andrii, daniel, martin.lau,
kernel-team, yonghong.song, emil, arighi
On Thu, Mar 19, 2026 at 03:03:07PM +0800, Shung-Hsi Yu wrote:
> On Tue, Mar 17, 2026 at 04:37:59PM +0100, Paul Chaignon wrote:
> > On Thu, Mar 12, 2026 at 02:45:47PM +0800, Shung-Hsi Yu wrote:
> > > On Fri, Mar 06, 2026 at 01:24:28AM +0100, Paul Chaignon wrote:
> > > > On Thu, Mar 05, 2026 at 04:18:42PM -0800, Eduard Zingerman wrote:
> > > > > On Fri, 2026-03-06 at 01:13 +0100, Paul Chaignon wrote:
> > > > > > On Thu, Mar 05, 2026 at 11:48:22AM -0800, Eduard Zingerman wrote:
> > > >
> > > > [...]
> > > >
> > > > > > > diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > > > index 0322f817d07be5d003c17dd7cedfa3aa4197678e..04938d0d431b38e086b50fe28b99e4ad2682742e 100644
> > > > > > > --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > > > +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c
> > > > > > > @@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, struct range x)
> > > >
> > > > [...]
> > > >
> > > > > > Don't we usually put changes to this file in a separate commit, as for
> > > > > > test changes in general?
> > > > >
> > > > > W/o this change the reg_bounds.c tests would fail.
> > > > > So far we tried to keep selftests passing for every commit,
> > > > > to help with any potential bisecting.
> > >
> > > Similarly we have stable AUTOSEL tends to just pick up kernel/bpf/
> > > changes and not the selftests ones. e.g. both 76e954155b45 ("bpf:
> > > Introduce tnum_step to step through tnum's members") and efc11a667878
> > > ("bpf: Improve bounds when tnum has a single possible value") got picked
> > > for 6.18.17-rc1, but not 024cea2d647e "selftests/bpf: Avoid
> > > simplification of crafted bounds test".
> >
> > I can send the selftest backports to the stable ml (unless you already
> > are on it?)
>
> Requested in https://lore.kernel.org/all/xbjybmha7fdnnm5gn6p6jyppf4ud2r72rfabvej6egg545ozsu@a4qj43d3iu36/
>
> We should be good now.
Thanks!
>
> > Do you know if there's anything I could have done differently to have
> > the selftests picked up? ...
>
> In this specific case above it would to be have the selftest change
> along with the kernel/bpf/verifier.c changes, all in one commit, like
> Eduard has reasoned.
I realized after sending that I was probably unclear. I meant anything I
could have done to have the *new* selftests picked up. From your answer
below, it sounds like no.
>
> > Or maybe something we could change in AUTOSEL?
>
> We can argue that the selftest was dependency that should go together,
> and that AUTOSEL should pick it up, but IIRC it is design to only pick
> up dependencies that comes before a commit. OTOH it would be nice to
> have it try picking up selftests in the same series whenever it picks a
> commit from there, but that sounds like a bigger ask.
Agree it would be nice but probably too much load for stable, even
assuming we have a fully-working stable BPF CI :(
>
> ...
^ permalink raw reply [flat|nested] 19+ messages in thread