selftests/bpf: Test maybe_fork_scalars() at the head of a counted loop

The insn that forks is the head of a loop that scalar evolution counted.
The state with 0 that maybe_fork_scalars() splits off is within the state
that is kept at the head. The access is out of bounds only in the state
with 0.

Without the previous patch the verifier accepts the prog.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261027190314.1440199-18-alexei.starovoitov@gmail.com
This commit is contained in:
Alexei Starovoitov 2026-10-27 19:03:12 +00:00
parent 19caa38daf
commit df9e4a87a8
No known key found for this signature in database

View File

@ -2159,6 +2159,47 @@ l0_%=: r0 = 0; \
: __clobber_all);
}
/*
* The insn that forks is the head of a loop that scalar evolution counted. The state with
* [-1, 0] is kept there, and the state with 0 that is split off is within it.
*/
SEC("socket")
__description("maybe_fork_scalars: AND at the head of a counted loop rejects OOB")
__failure __msg("invalid access to map value")
__naked void and_scalar_fork_at_long_loop_head_rejects_oob(void)
{
asm volatile (" \
r1 = 0; \
*(u64*)(r10 - 8) = r1; \
r2 = r10; \
r2 += -8; \
r1 = %[map_hash_8b] ll; \
call %[bpf_map_lookup_elem]; \
if r0 == 0 goto l0_%=; \
r9 = r0; \
r7 = 0; \
r6 = -1; \
l1_%=: r6 &= 8; \
/* r6 is 8 (current) or 0 (pushed) */ \
r6 ^= 8; \
/* pushed path: r6 = 8, OOB for value_size=8 */ \
r1 = r9; \
r1 += r6; \
r0 = *(u8*)(r1 + 0); \
r1 = *(u64*)(r9 + 0); \
r6 = -1; \
if r1 == 0 goto l2_%=; \
r6 = 0; \
l2_%=: r7 += 1; \
if r7 < 100 goto l1_%=; \
l0_%=: r0 = 0; \
exit; \
" :
: __imm(bpf_map_lookup_elem),
__imm_addr(map_hash_8b)
: __clobber_all);
}
/*
* Last jump can be detected as always taken because the intersection of R5 and
* R7 32bit tnums produces a constant that isn't within R7's s32 bounds.