Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'optimize-bounds-refinement-by-reordering-deductions'

Paul Chaignon says:

====================
Optimize bounds refinement by reordering deductions

This patchset optimizes the bounds refinement (reg_bounds_sync) by
reordering deductions in __reg_deduce_bounds. This reordering allows us
to improve precision slightly while losing one call to
__reg_deduce_bounds.

The first patch from Eduard refactors the __reg_deduce_bounds
subfunctions, the second patch implements the reordering, and the last
one adds a selftest.

Changes in v3:
- Added first commit from Eduard that significantly helps with
readability of second commit.
- Reshuffled a bit more the functions in the second commit to improve
precision (Eduard).
- Rebased.
Changes in v2:
- Updated description to mention potential precision improvement and
to clarify the sequence of refinements (Shung-Hsi).
- Added the second patch.
- Rebased.
====================

Link: https://patch.msgid.link/cover.1773401138.git.paul.chaignon@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+44 -7
+11 -7
kernel/bpf/verifier.c
··· 2448 2448 } 2449 2449 2450 2450 /* Uses signed min/max values to inform unsigned, and vice-versa */ 2451 - static void __reg32_deduce_bounds(struct bpf_reg_state *reg) 2451 + static void deduce_bounds_32_from_64(struct bpf_reg_state *reg) 2452 2452 { 2453 2453 /* If upper 32 bits of u64/s64 range don't change, we can use lower 32 2454 2454 * bits to improve our u32/s32 boundaries. ··· 2518 2518 reg->s32_min_value = max_t(s32, reg->s32_min_value, (s32)reg->smin_value); 2519 2519 reg->s32_max_value = min_t(s32, reg->s32_max_value, (s32)reg->smax_value); 2520 2520 } 2521 + } 2522 + 2523 + static void deduce_bounds_32_from_32(struct bpf_reg_state *reg) 2524 + { 2521 2525 /* if u32 range forms a valid s32 range (due to matching sign bit), 2522 2526 * try to learn from that 2523 2527 */ ··· 2563 2559 } 2564 2560 } 2565 2561 2566 - static void __reg64_deduce_bounds(struct bpf_reg_state *reg) 2562 + static void deduce_bounds_64_from_64(struct bpf_reg_state *reg) 2567 2563 { 2568 2564 /* If u64 range forms a valid s64 range (due to matching sign bit), 2569 2565 * try to learn from that. Let's do a bit of ASCII art to see when ··· 2698 2694 } 2699 2695 } 2700 2696 2701 - static void __reg_deduce_mixed_bounds(struct bpf_reg_state *reg) 2697 + static void deduce_bounds_64_from_32(struct bpf_reg_state *reg) 2702 2698 { 2703 2699 /* Try to tighten 64-bit bounds from 32-bit knowledge, using 32-bit 2704 2700 * values on both sides of 64-bit range in hope to have tighter range. ··· 2767 2763 2768 2764 static void __reg_deduce_bounds(struct bpf_reg_state *reg) 2769 2765 { 2770 - __reg32_deduce_bounds(reg); 2771 - __reg64_deduce_bounds(reg); 2772 - __reg_deduce_mixed_bounds(reg); 2766 + deduce_bounds_64_from_64(reg); 2767 + deduce_bounds_32_from_64(reg); 2768 + deduce_bounds_32_from_32(reg); 2769 + deduce_bounds_64_from_32(reg); 2773 2770 } 2774 2771 2775 2772 /* Attempts to improve var_off based on unsigned min/max information */ ··· 2791 2786 /* We might have learned new bounds from the var_off. */ 2792 2787 __update_reg_bounds(reg); 2793 2788 /* We might have learned something about the sign bit. */ 2794 - __reg_deduce_bounds(reg); 2795 2789 __reg_deduce_bounds(reg); 2796 2790 __reg_deduce_bounds(reg); 2797 2791 /* We might have learned some bits from the bounds. */
+33
tools/testing/selftests/bpf/progs/verifier_bounds.c
··· 2037 2037 : __clobber_all); 2038 2038 } 2039 2039 2040 + /* After instruction 3, the u64 and s64 ranges look as follows: 2041 + * 0 umin=2 umax=0xff..ff00..03 U64_MAX 2042 + * | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | 2043 + * |----------------------------|------------------------------| 2044 + * |xx] [xxxxxxxxxxxxxxxxxxxxxxxxxxxx| 2045 + * 0 smax=2 smin=0x800..02 -1 2046 + * 2047 + * The two ranges can't be refined because they overlap in two places. Once we 2048 + * add an upper-bound to u64 at instruction 4, the refinement can happen. This 2049 + * test validates that this refinement does happen and is not overwritten by 2050 + * the less-precise 32bits ranges. 2051 + */ 2052 + SEC("socket") 2053 + __description("bounds refinement: 64bits ranges not overwritten by 32bits ranges") 2054 + __msg("3: (65) if r0 s> 0x2 {{.*}} R0=scalar(smin=0x8000000000000002,smax=2,umin=smin32=umin32=2,umax=0xffffffff00000003,smax32=umax32=3") 2055 + __msg("4: (25) if r0 > 0x13 {{.*}} R0=2") 2056 + __success __log_level(2) 2057 + __naked void refinement_32bounds_not_overwriting_64bounds(void *ctx) 2058 + { 2059 + asm volatile(" \ 2060 + call %[bpf_get_prandom_u32]; \ 2061 + if w0 < 2 goto +5; \ 2062 + if w0 > 3 goto +4; \ 2063 + if r0 s> 2 goto +3; \ 2064 + if r0 > 19 goto +2; \ 2065 + if r0 == 2 goto +1; \ 2066 + r10 = 0; \ 2067 + exit; \ 2068 + " : 2069 + : __imm(bpf_get_prandom_u32) 2070 + : __clobber_all); 2071 + } 2072 + 2040 2073 char _license[] SEC("license") = "GPL";