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

selftests/bpf: trigger verifier.c:maybe_exit_scc() for a speculative state

This is a test case minimized from a syzbot reproducer from [1].
The test case triggers verifier.c:maybe_exit_scc() w/o
preceding call to verifier.c:maybe_enter_scc() on a speculative
symbolic execution path.

Here is verifier log for the test case:

Live regs before insn:
0: .......... (b7) r0 = 100
1 1: 0......... (7b) *(u64 *)(r10 -512) = r0
1 2: 0......... (b5) if r0 <= 0x0 goto pc-2
3: 0......... (95) exit
0: R1=ctx() R10=fp0
0: (b7) r0 = 100 ; R0_w=100
1: (7b) *(u64 *)(r10 -512) = r0 ; R0_w=100 R10=fp0 fp-512_w=100
2: (b5) if r0 <= 0x0 goto pc-2
mark_precise: ...
2: R0_w=100
3: (95) exit

from 2 to 1 (speculative execution): R0_w=scalar() R1=ctx() R10=fp0 fp-512_w=100
1: R0_w=scalar() R1=ctx() R10=fp0 fp-512_w=100
1: (7b) *(u64 *)(r10 -512) = r0
processed 5 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

- Non-speculative execution path 0-3 does not allocate any checkpoints
(and hence does not call maybe_enter_scc()), and schedules a
speculative jump from 2 to 1.
- Speculative execution path stops immediately because of an infinite
loop detection and triggers verifier.c:update_branch_counts() ->
maybe_exit_scc() calls.

[1] https://lore.kernel.org/bpf/68c85acd.050a0220.2ff435.03a4.GAE@google.com/

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250916212251.3490455-2-eddyz87@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Eduard Zingerman and committed by
Alexei Starovoitov
a24a2dda a3c73d62

+21
+21
tools/testing/selftests/bpf/progs/verifier_loops1.c
··· 283 283 : __clobber_all); 284 284 } 285 285 286 + /* 287 + * This test case triggered a bug in verifier.c:maybe_exit_scc(). 288 + * Speculative execution path reaches stack access instruction, 289 + * stops and triggers maybe_exit_scc() w/o accompanying maybe_enter_scc() call. 290 + */ 291 + SEC("socket") 292 + __arch_x86_64 293 + __caps_unpriv(CAP_BPF) 294 + __naked void maybe_exit_scc_bug1(void) 295 + { 296 + asm volatile ( 297 + "r0 = 100;" 298 + "1:" 299 + /* Speculative execution path reaches and stops here. */ 300 + "*(u64 *)(r10 - 512) = r0;" 301 + /* Condition is always false, but verifier speculatively executes the true branch. */ 302 + "if r0 <= 0x0 goto 1b;" 303 + "exit;" 304 + ::: __clobber_all); 305 + } 306 + 286 307 char _license[] SEC("license") = "GPL";