Re: [PATCH bpf-next v2 3/4] selftests/bpf: Test cross-sign 64bits range refinement

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2025-07-24 at 15:43 +0200, Paul Chaignon wrote:
> This patch adds coverage for the new cross-sign 64bits range refinement
> logic. The three tests cover the cases when the u64 and s64 ranges
> overlap (1) in the negative portion of s64, (2) in the positive portion
> of s64, and (3) in both portions.
> 
> The first test is a simplified version of a BPF program generated by
> syzkaller that caused an invariant violation [1]. It looks like
> syzkaller could not extract the reproducer itself (and therefore didn't
> report it to the mailing list), but I was able to extract it from the
> console logs of a crash.
> 
> The principle is similar to the invariant violation described in
> 6279846b9b25 ("bpf: Forget ranges when refining tnum after JSET"): the
> verifier walks a dead branch, uses the condition to refine ranges, and
> ends up with inconsistent ranges. In this case, the dead branch is when
> we fallthrough on both jumps. The new refinement logic improves the
> bounds such that the second jump is properly detected as always-taken
> and the verifier doesn't end up walking a dead branch.
> 
> The second and third tests are inspired by the first, but rely on
> condition jumps to prepare the bounds instead of ALU instructions. An
> R10 write is used to trigger a verifier error when the bounds can't be
> refined.
> 
> Link: https://syzkaller.appspot.com/bug?extid=c711ce17dd78e5d4fdcf [1]
> Signed-off-by: Paul Chaignon <paul.chaignon@xxxxxxxxx>
> ---

Hi Paul,

Thank you for adding the tests, I think the patch looks good.

>  .../selftests/bpf/progs/verifier_bounds.c     | 118 ++++++++++++++++++
>  1 file changed, 118 insertions(+)
> 
> diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c
> index 63b533ca4933..dd4e3e9f41d3 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_bounds.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c
> @@ -1550,4 +1550,122 @@ l0_%=:	r0 = 0;				\
>  	: __clobber_all);
>  }
>  
> +/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges
> + * overlap on the negative side. At instruction 7, the ranges look as follows:
> + *
> + * 0          umin=0xfffffcf1                 umax=0xff..ff6e  U64_MAX
> + * |                [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]        |
> + * |----------------------------|------------------------------|
> + * |xxxxxxxxxx]                                   [xxxxxxxxxxxx|
> + * 0    smax=0xeffffeee                       smin=-655        -1
> + *
> + * We should therefore deduce the following new bounds:
> + *
> + * 0                             u64=[0xff..ffd71;0xff..ff6e]  U64_MAX
> + * |                                              [xxx]        |
> + * |----------------------------|------------------------------|
> + * |                                              [xxx]        |
> + * 0                                        s64=[-655;-146]    -1
> + *
> + * Without the deduction cross sign boundary, we end up with an invariant
> + * violation error.
> + */
> +SEC("socket")
> +__description("bounds deduction cross sign boundary, negative overlap")
> +__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS)
> +__msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))")

Interesting, note the difference: smin=-655, smin32=-783.
There is a code to infer s32 range from s46 range in this situation in
__reg32_deduce_bounds(), but it looks like a third __reg_deduce_bounds
call is needed to trigger it. E.g. the following patch removes the
difference for me:

  diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
  index f0a41f1596b6..87050d17baf9 100644
  --- a/kernel/bpf/verifier.c
  +++ b/kernel/bpf/verifier.c
  @@ -2686,6 +2686,7 @@ static void reg_bounds_sync(struct bpf_reg_state *reg)
          /* We might have learned something about the sign bit. */
          __reg_deduce_bounds(reg);
          __reg_deduce_bounds(reg);
  +       __reg_deduce_bounds(reg);
          /* We might have learned some bits from the bounds. */
          __reg_bound_offset(reg);
          /* Intersecting with the old var_off might have improved our bounds

> +__retval(0)
> +__naked void bounds_deduct_negative_overlap(void)
> +{
> +	asm volatile("			\
> +	call %[bpf_get_prandom_u32];	\
> +	w3 = w0;			\
> +	w6 = (s8)w0;			\
> +	r0 = (s8)r0;			\
> +	if w6 >= 0xf0000000 goto l0_%=;	\
> +	r0 += r6;			\
> +	r6 += 400;			\
> +	r0 -= r6;			\
> +	if r3 < r0 goto l0_%=;		\
> +l0_%=:	r0 = 0;				\
> +	exit;				\
> +"	:
> +	: __imm(bpf_get_prandom_u32)
> +	: __clobber_all);
> +}

[...]





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux