Merge branch 'bpf-range_within-must-check-cnum-ranges-instead-of-min-max-pairs'

Eduard Zingerman says:

====================
bpf: range_within() must check cnum ranges instead of min/max pairs

This is a follow-up for series [1].
is_state_visited() should check cnum's subset relations using
cnum-based operations, not min/max projections. See patch #1 for
detailed explanation and patch #2 for an example of buggy program
accepted by verifier w/o this fix.

Updated veristat performance numbers compared to master before [1]
merge follow. Measurements done for the same set of selftests/
sched_ext/meta/cilium programs as in [1].

Net increase:   98K insn 88 programs
Net decrease: -282K insn 52 programs

Raw stats filtered as -f insns_pct>1 -f !insns<10000:

========= selftests: master vs experiment =========

File  Program  Insns (A)  Insns (B)  Insns (DIFF)
----  -------  ---------  ---------  ------------

Total progs: 4665
total_insns diff min:   -0.44%
total_insns diff max:   52.94%
total_insns abs max old: 837,487
total_insns abs max new: 837,487
  -5 .. 0    %: 8
   0 .. 5    %: 4652
   5 .. 15   %: 1
  40 .. 50   %: 3
  50 .. 55   %: 1

========= scx: master vs experiment =========

File               Program          Insns (A)  Insns (B)  Insns     (DIFF)
-----------------  ---------------  ---------  ---------  ----------------
scx_layered.bpf.o  layered_enqueue      13718      14402     +684 (+4.99%)
scx_rusty.bpf.o    rusty_enqueue        39842      22053  -17789 (-44.65%)
scx_rusty.bpf.o    rusty_stopping       37738      19949  -17789 (-47.14%)
scx_wd40.bpf.o     wd40_stopping        37729      19880  -17849 (-47.31%)

Total progs: 376
total_insns diff min:  -47.31%
total_insns diff max:   19.61%
total_insns abs max old: 233,669
total_insns abs max new: 233,696
 -50 .. -40  %: 3
  -5 .. 0    %: 3
   0 .. 5    %: 367
   5 .. 15   %: 2
  15 .. 20   %: 1

========= meta: master vs experiment =========

File                                    Program              Insns (A)  Insns (B)  Insns     (DIFF)
--------------------------------------  -------------------  ---------  ---------  ----------------
<sandbox>                               test_file_open           88771     104160  +15389 (+17.34%)
<profiler>                              on_perf_event            48056      54544   +6488 (+13.50%)
<profiler>                              on_tracepoint_event      48059      54547   +6488 (+13.50%)
<profiler>                              on_perf_event            48056      54544   +6488 (+13.50%)
<profiler>                              on_tracepoint_event      48059      54547   +6488 (+13.50%)
<profiler>                              on_alloc                 50445      56933   +6488 (+12.86%)
<profiler>                              on_free                  50251      56739   +6488 (+12.91%)
<profiler>                              on_perf_event            48056      54544   +6488 (+13.50%)
<profiler>                              on_tracepoint_event      48059      54547   +6488 (+13.50%)
<profiler>                              future_iter_resume       50114      56602   +6488 (+12.95%)
<profiler>                              on_py_event              50042      56530   +6488 (+12.97%)
scx_layered_bpf_skel_genskel-bpf.bpf.o  layered_enqueue          13287      13963     +676 (+5.09%)
scx_layered_bpf_skel_genskel-bpf.bpf.o  layered_enqueue          13269      13945     +676 (+5.09%)
scx_layered_bpf_skel_genskel-bpf.bpf.o  layered_enqueue          13269      13945     +676 (+5.09%)
<firewall>                              ..._egress              222327     164648  -57679 (-25.94%)
<firewall>                              ..._tc_eg               222839     164772  -58067 (-26.06%)
<firewall>                              ..._egress              222327     164648  -57679 (-25.94%)
<firewall>                              ..._tc_eg               222839     164772  -58067 (-26.06%)

Total progs: 1540
total_insns diff min:  -26.06%
total_insns diff max:   37.25%
total_insns abs max old: 666,036
total_insns abs max new: 666,036
 -30 .. -20  %: 4
  -5 .. 0    %: 10
   0 .. 5    %: 1494
   5 .. 10   %: 10
  10 .. 15   %: 13
  15 .. 25   %: 5
  35 .. 40   %: 4

========= cilium: master vs experiment =========

File             Program                            Insns (A)  Insns (B)  Insns    (DIFF)
---------------  ---------------------------------  ---------  ---------  ---------------
bpf_host.o       tail_handle_ipv4_cont_from_host        20962      26024  +5062 (+24.15%)
bpf_host.o       tail_handle_ipv6_cont_from_host        17036      18672   +1636 (+9.60%)
bpf_host.o       tail_nodeport_nat_ingress_ipv4         20080      19858    -222 (-1.11%)
bpf_lxc.o        tail_nodeport_nat_ingress_ipv4         10697      10510    -187 (-1.75%)
bpf_overlay.o    tail_handle_inter_cluster_revsnat      11099      10857    -242 (-2.18%)
bpf_overlay.o    tail_nodeport_nat_ingress_ipv4         11951      11768    -183 (-1.53%)
bpf_wireguard.o  tail_nodeport_nat_ingress_ipv4         11993      11811    -182 (-1.52%)

Total progs: 134
total_insns diff min:   -3.32%
total_insns diff max:   24.15%
total_insns abs max old: 152,012
total_insns abs max new: 152,012
  -5 .. 0    %: 12
   0 .. 5    %: 120
   5 .. 15   %: 1
  20 .. 25   %: 1

[1] https://lore.kernel.org/bpf/fd376f47b9512daf669a87b23573f614ec146385.camel@gmail.com/T/
---
====================

Link: https://patch.msgid.link/20260425-cnum-range-within-v1-0-2fdca70cb09d@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov
2026-04-27 09:56:39 -07:00
4 changed files with 46 additions and 8 deletions

View File

@@ -48,6 +48,7 @@ bool cnum32_is_const(struct cnum32 cnum);
bool cnum32_is_empty(struct cnum32 cnum);
struct cnum32 cnum32_add(struct cnum32 a, struct cnum32 b);
struct cnum32 cnum32_negate(struct cnum32 a);
bool cnum32_is_subset(struct cnum32 outer, struct cnum32 inner);
/* Same as cnum32 but for 64-bit ranges */
struct cnum64 {
@@ -73,6 +74,7 @@ bool cnum64_is_const(struct cnum64 cnum);
bool cnum64_is_empty(struct cnum64 cnum);
struct cnum64 cnum64_add(struct cnum64 a, struct cnum64 b);
struct cnum64 cnum64_negate(struct cnum64 a);
bool cnum64_is_subset(struct cnum64 outer, struct cnum64 inner);
struct cnum32 cnum32_from_cnum64(struct cnum64 cnum);
struct cnum64 cnum64_cnum32_intersect(struct cnum64 a, struct cnum32 b);

View File

@@ -220,6 +220,20 @@ bool FN(is_const)(struct cnum_t cnum)
return cnum.size == 0;
}
bool FN(is_subset)(struct cnum_t bigger, struct cnum_t smaller)
{
if (FN(is_empty(smaller)))
return true;
if (FN(is_empty(bigger)))
return false;
/* rotate both arcs such that 'bigger' starts at origin, hence does not overflow */
smaller.base -= bigger.base;
bigger.base = 0;
if (FN(urange_overflow)(smaller) && bigger.size < UT_MAX)
return false;
return smaller.base + smaller.size <= bigger.size;
}
#undef EMPTY
#undef cnum_t
#undef ut

View File

@@ -2,6 +2,7 @@
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
#include <linux/bpf_verifier.h>
#include <linux/cnum.h>
#include <linux/filter.h>
#define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
@@ -301,14 +302,8 @@ int bpf_update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifier_s
static bool range_within(const struct bpf_reg_state *old,
const struct bpf_reg_state *cur)
{
return reg_umin(old) <= reg_umin(cur) &&
reg_umax(old) >= reg_umax(cur) &&
reg_smin(old) <= reg_smin(cur) &&
reg_smax(old) >= reg_smax(cur) &&
reg_u32_min(old) <= reg_u32_min(cur) &&
reg_u32_max(old) >= reg_u32_max(cur) &&
reg_s32_min(old) <= reg_s32_min(cur) &&
reg_s32_max(old) >= reg_s32_max(cur);
return cnum64_is_subset(old->r64, cur->r64) &&
cnum32_is_subset(old->r32, cur->r32);
}
/* If in the old state two registers had the same id, then they need to have

View File

@@ -2267,4 +2267,31 @@ __naked void deduce64_from_32_wrapping_32bit(void)
: __clobber_all);
}
/* Check that range_within() compares cnum ranges, not min/max projections. */
SEC("socket")
__failure __msg("div by zero")
__flag(BPF_F_TEST_STATE_FREQ)
__naked void range_within_cnum_cross_both_boundaries(void)
{
asm volatile (" \
call %[bpf_get_prandom_u32]; \
r1 = 0x80000020; \
if r0 > r1 goto 1f; \
r0 += 0x7FFFFFF0; /* PATH 1 */ \
goto 2f; \
1: call %[bpf_get_prandom_u32]; /* PATH 2 */ \
if r0 < 0x100 goto 3f; \
if r0 > 0x200 goto 3f; \
2: /* PATH 1: r0 ∈ [0x7FFFFFF0, U32_MAX] [0, 0x10] */ \
/* PATH 2: r0 ∈ [0x100, 0x200] */ \
if r0 != 0x100 goto 3f; /* True only on PATH 2 */ \
r0 /= 0; \
3: exit; \
"
:: __imm(bpf_map_lookup_elem),
__imm_addr(map_hash_8b),
__imm(bpf_get_prandom_u32)
: __clobber_all);
}
char _license[] SEC("license") = "GPL";