Skip to content

Commit 9cea90c

Browse files
author
Alexei Starovoitov
committedNov 15, 2023
Merge branch 'bpf-register-bounds-range-vs-range-support'
Andrii Nakryiko says: ==================== BPF register bounds range vs range support This patch set is a continuation of work started in [0]. It adds a big set of manual, auto-generated, and now also random test cases validating BPF verifier's register bounds tracking and deduction logic. First few patches generalize verifier's logic to handle conditional jumps and corresponding range adjustments in case when two non-const registers are compared to each other. Patch #1 generalizes reg_set_min_max() portion, while patch #2 does the same for is_branch_taken() part of the overall solution. Patch #3 improves equality and inequality for cases when BPF program code mixes 64-bit and 32-bit uses of the same register. Depending on specific sequence, it's possible to get to the point where u64/s64 bounds will be very generic (e.g., after signed 32-bit comparison), while we still keep pretty tight u32/s32 bounds. If in such state we proceed with 32-bit equality or inequality comparison, reg_set_min_max() might have to deal with adjusting s32 bounds for two registers that don't overlap, which breaks reg_set_min_max(). This doesn't manifest in <range> vs <const> cases, because if that happens reg_set_min_max() in effect will force s32 bounds to be a new "impossible" constant (from original smin32/smax32 bounds point of view). Things get tricky when we have <range> vs <range> adjustments, so instead of trying to somehow make sense out of such situations, it's best to detect such impossible situations and prune the branch that can't be taken in is_branch_taken() logic. This equality/inequality was the only such category of situations with auto-generated tests added later in the patch set. But when we start mixing arithmetic operations in different numeric domains and conditionals, things get even hairier. So, patch #4 adds sanity checking logic after all ALU/ALU64, JMP/JMP32, and LDX operations. By default, instead of failing verification, we conservatively reset range bounds to unknown values, reporting violation in verifier log (if verbose logs are requested). But to aid development, detection, and debugging, we also introduce a new test flag, BPF_F_TEST_SANITY_STRICT, which triggers verification failure on range sanity violation. Patch #11 sets BPF_F_TEST_SANITY_STRICT by default for test_progs and test_verifier. Patch #12 adds support for controlling this in veristat for testing with production BPF object files. Getting back to BPF verifier, patches #5 and #6 complete verifier's range tracking logic clean up. See respective patches for details. With kernel-side taken care of, we move to testing. We start with building a tester that validates existing <range> vs <scalar> verifier logic for range bounds. Patch #7 implements an initial version of such a tester. We guard millions of generated tests behind SLOW_TESTS=1 envvar requirement, but also have a relatively small number of tricky cases that came up during development and debugging of this work. Those will be executed as part of a normal test_progs run. Patch #8 simulates more nuanced JEQ/JNE logic we added to verifier in patch #3. Patch #9 adds <range> vs <range> "slow tests". Patch #10 is a completely new one, it adds a bunch of randomly generated cases to be run normally, without SLOW_TESTS=1 guard. This should help to get a bunch of cover, and hopefully find some remaining latent problems if verifier proactively as part of normal BPF CI runs. Finally, a tiny test which was, amazingly, an initial motivation for this whole work, is added in lucky patch #13, demonstrating how verifier is now smart enough to track actual number of elements in the array and won't require additional checks on loop iteration variable inside the bpf_for() open-coded iterator loop. [0] https://patchwork.kernel.org/project/netdevbpf/list/?series=798308&state=* v1->v2: - use x < y => y > x property to minimize reg_set_min_max (Eduard); - fix for JEQ/JNE logic in reg_bounds.c (Eduard); - split BPF_JSET and !BPF_JSET cases handling (Shung-Hsi); - adjustments to reg_bounds.c to make it easier to follow (Alexei); - added acks (Eduard, Shung-Hsi). ==================== Link: https://lore.kernel.org/r/20231112010609.848406-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 81427a6 + 882e3d8 commit 9cea90c

File tree

16 files changed

+2495
-307
lines changed

16 files changed

+2495
-307
lines changed
 

‎include/linux/bpf_verifier.h

+1
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ struct bpf_verifier_env {
602602
int stack_size; /* number of states to be processed */
603603
bool strict_alignment; /* perform strict pointer alignment checks */
604604
bool test_state_freq; /* test verifier with different pruning frequency */
605+
bool test_sanity_strict; /* fail verification on sanity violations */
605606
struct bpf_verifier_state *cur_state; /* current verifier state */
606607
struct bpf_verifier_state_list **explored_states; /* search pruning optimization */
607608
struct bpf_verifier_state_list *free_list;

‎include/linux/tnum.h

+4
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ int tnum_sbin(char *str, size_t size, struct tnum a);
106106
struct tnum tnum_subreg(struct tnum a);
107107
/* Returns the tnum with the lower 32-bit subreg cleared */
108108
struct tnum tnum_clear_subreg(struct tnum a);
109+
/* Returns the tnum with the lower 32-bit subreg in *reg* set to the lower
110+
* 32-bit subreg in *subreg*
111+
*/
112+
struct tnum tnum_with_subreg(struct tnum reg, struct tnum subreg);
109113
/* Returns the tnum with the lower 32-bit subreg set to value */
110114
struct tnum tnum_const_subreg(struct tnum a, u32 value);
111115
/* Returns true if 32-bit subreg @a is a known constant*/

‎include/uapi/linux/bpf.h

+3
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,9 @@ enum bpf_perf_event_type {
12001200
*/
12011201
#define BPF_F_XDP_DEV_BOUND_ONLY (1U << 6)
12021202

1203+
/* The verifier internal test flag. Behavior is undefined */
1204+
#define BPF_F_TEST_SANITY_STRICT (1U << 7)
1205+
12031206
/* link_create.kprobe_multi.flags used in LINK_CREATE command for
12041207
* BPF_TRACE_KPROBE_MULTI attach type to create return probe.
12051208
*/

‎kernel/bpf/syscall.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -2573,7 +2573,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
25732573
BPF_F_SLEEPABLE |
25742574
BPF_F_TEST_RND_HI32 |
25752575
BPF_F_XDP_HAS_FRAGS |
2576-
BPF_F_XDP_DEV_BOUND_ONLY))
2576+
BPF_F_XDP_DEV_BOUND_ONLY |
2577+
BPF_F_TEST_SANITY_STRICT))
25772578
return -EINVAL;
25782579

25792580
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&

‎kernel/bpf/tnum.c

+6-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,12 @@ struct tnum tnum_clear_subreg(struct tnum a)
208208
return tnum_lshift(tnum_rshift(a, 32), 32);
209209
}
210210

211+
struct tnum tnum_with_subreg(struct tnum reg, struct tnum subreg)
212+
{
213+
return tnum_or(tnum_clear_subreg(reg), tnum_subreg(subreg));
214+
}
215+
211216
struct tnum tnum_const_subreg(struct tnum a, u32 value)
212217
{
213-
return tnum_or(tnum_clear_subreg(a), tnum_const(value));
218+
return tnum_with_subreg(a, tnum_const(value));
214219
}

0 commit comments

Comments
 (0)
Please sign in to comment.