Re: [PATCH stable 6.6.y v3 0/4] bpf: linked scalar precision fixes
From: Zhenzhong Wu <hidden>
Date: 2026-06-19 04:01:56
Also in:
bpf, lkml, stable
Sorry for the late reply. For v3, I only ran the targeted test_progs coverage and missed the legacy test_verifier coverage, so I did not catch the expectation mismatch in precise.c. Thanks for the detailed analysis. I have rechecked this and will send v4 shortly with the precise.c fix included. On Tue, Jun 16, 2026 at 1:22 PM Shung-Hsi Yu [off-list ref] wrote:
On Tue, Jun 16, 2026 at 12:51:34AM +0200, Paul Chaignon wrote:quoted
On Mon, Jun 15, 2026 at 12:58:37AM +0800, Zhenzhong Wu wrote:quoted
Hi, This v3 targets 6.6.y and changes the backport strategy based on review feedback on v2.[...]quoted
Relevant QEMU selftest results on 6.6.y with this backport: verifier_scalar_ids passed all 18 subtests, including the newly backported linked-scalar precision tests and the related check_ids_in_regsafe tests.The first patch in this backport series is actually breaking the "precise: test 1" selftest from test_verifier. You can see the full error at [1]. I haven't yet checked if it's the test or the backport that needs to be adjusted.I had a quick look, and believe it was that test that needs to be adjusted to include r9 into the precise register set. So unless Sasha have other preference, I suggest Zhenzhong send a v4, with changes to tools/testing/selftests/bpf/verifier/precise.c (including "r9" the the expected verifier output) merged into "bpf: Track equal scalars history on per-instruction level". --- The program under test is: 00: BPF_MOV64_IMM(BPF_REG_0, 1), 01: BPF_LD_MAP_FD(BPF_REG_6, 0), 03: BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), 04: BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP), 05: BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 06: BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 0), 07: BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 08: BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 09: BPF_EXIT_INSN(), 10: BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), 11: BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), 12: BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP), 13: BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 14: BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 15: BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 16: BPF_EXIT_INSN(), 17: BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), 18: BPF_ALU64_REG(BPF_SUB, BPF_REG_9, BPF_REG_8), /* map_value_ptr -= map_value_ptr */ 19: BPF_MOV64_REG(BPF_REG_2, BPF_REG_9), 20: BPF_JMP_IMM(BPF_JLT, BPF_REG_2, 8, 1), 21: BPF_EXIT_INSN(), 22: BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1), /* R2=scalar(umin=1, umax=8) */ 23: BPF_MOV64_REG(BPF_REG_1, BPF_REG_FP), 24: BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), 25: BPF_MOV64_IMM(BPF_REG_3, 0), 26: BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel), 27: BPF_EXIT_INSN(), The test was expecting the following line in the verifier log that was shown during the backtracking start at instruction 26 (call bpf_probe_read_kernel#113) mark_precise: frame0: regs=r2 stack= before 20: (a5) if r2 < 0x8 goto pc+1 mark_precise: frame0: parent state regs=r2 stack=: ... mark_precise: frame0: last_idx 19 first_idx 10 ... But after applying the patchset, we now got an additional register r9 in the precise set: mark_precise: frame0: regs=r2 stack= before 20: (a5) if r2 < 0x8 goto pc+1 mark_precise: frame0: parent state regs=r2,r9 stack=: .... mark_precise: frame0: last_idx 19 first_idx 10 ... The additional r9 in the precise set seems actually correct, this is because r2 and r9 share the same scalar ID at instruction 20 (before the link got broken in instruction 21), and hence at that point, both register should be marked as precise. --- In upstream the test already has the expected verifier log to include r9, and hence no failure, but it simply comes from the fact that r2 and r9 maintain a link even after instruction 22 (r2 += 1). commit 98d7ca374ba4b39e7535613d40e159f09ca14da2 Author: Alexei Starovoitov [off-list ref] Date: Wed Jun 12 18:38:13 2024 -0700 bpf: Track delta between "linked" registers. ... --- a/tools/testing/selftests/bpf/verifier/precise.c +++ b/tools/testing/selftests/bpf/verifier/precise.c @@ -39,12 +39,12 @@ .result = VERBOSE_ACCEPT, .errstr = "mark_precise: frame0: last_idx 26 first_idx 20\ - mark_precise: frame0: regs=r2 stack= before 25\ - mark_precise: frame0: regs=r2 stack= before 24\ - mark_precise: frame0: regs=r2 stack= before 23\ - mark_precise: frame0: regs=r2 stack= before 22\ - mark_precise: frame0: regs=r2 stack= before 20\ - mark_precise: frame0: parent state regs=r2 stack=:\ + mark_precise: frame0: regs=r2,r9 stack= before 25\ + mark_precise: frame0: regs=r2,r9 stack= before 24\ + mark_precise: frame0: regs=r2,r9 stack= before 23\ + mark_precise: frame0: regs=r2,r9 stack= before 22\ + mark_precise: frame0: regs=r2,r9 stack= before 20\ + mark_precise: frame0: parent state regs=r2,r9 stack=:\ mark_precise: frame0: last_idx 19 first_idx 10\ mark_precise: frame0: regs=r2,r9 stack= before 19\ mark_precise: frame0: regs=r9 stack= before 18\ ... --- Full test log below #492/p precise: test 1 FAIL Unexpected verifier log! EXP: mark_precise: frame0: parent state regs=r2 stack=: RES: func#0 @0 0: R1=ctx(off=0,imm=0) R10=fp0 0: (b7) r0 = 1 ; R0_w=1 1: (18) r6 = 0xffff9eb644619000 ; R6_w=map_ptr(off=0,ks=4,vs=48,imm=0) 3: (bf) r1 = r6 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R6_w=map_ptr(off=0,ks=4,vs=48,imm=0) 4: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 5: (07) r2 += -8 ; R2_w=fp-8 6: (7a) *(u64 *)(r10 -8) = 0 ; R10=fp0 fp-8_w=00000000 7: (85) call bpf_map_lookup_elem#1 ; R0_w=map_value_or_null(id=1,off=0,ks=4,vs=48,imm=0) 8: (55) if r0 != 0x0 goto pc+1 ; R0_w=0 9: (95) exit from 8 to 10: R0=map_value(off=0,ks=4,vs=48,imm=0) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R10=fp0 fp-8=0000mmmm 10: R0=map_value(off=0,ks=4,vs=48,imm=0) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R10=fp0 fp-8=0000mmmm 10: (bf) r9 = r0 ; R0=map_value(off=0,ks=4,vs=48,imm=0) R9_w=map_value(off=0,ks=4,vs=48,imm=0) 11: (bf) r1 = r6 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R6=map_ptr(off=0,ks=4,vs=48,imm=0) 12: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 13: (07) r2 += -8 ; R2_w=fp-8 14: (85) call bpf_map_lookup_elem#1 ; R0_w=map_value_or_null(id=2,off=0,ks=4,vs=48,imm=0) 15: (55) if r0 != 0x0 goto pc+1 ; R0_w=0 16: (95) exit from 15 to 17: R0_w=map_value(off=0,ks=4,vs=48,imm=0) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R9_w=map_value(off=0,ks=4,vs=48,imm=0) R10=fp0 fp-8=0000mmmm 17: R0_w=map_value(off=0,ks=4,vs=48,imm=0) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R9_w=map_value(off=0,ks=4,vs=48,imm=0) R10=fp0 fp-8=0000mmmm 17: (bf) r8 = r0 ; R0_w=map_value(off=0,ks=4,vs=48,imm=0) R8_w=map_value(off=0,ks=4,vs=48,imm=0) 18: (1f) r9 -= r8 ; R8_w=map_value(off=0,ks=4,vs=48,imm=0) R9_w=scalar() 19: (bf) r2 = r9 ; R2=scalar(id=3) R9=scalar(id=3) 20: (a5) if r2 < 0x8 goto pc+1 ; R2=scalar(id=3,umin=8) 21: (95) exit from 20 to 22: R0=map_value(off=0,ks=4,vs=48,imm=0) R2=scalar(id=3,umax=7,var_off=(0x0; 0x7)) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R8=map_value(off=0,ks=4,vs=48,imm=0) R9=scalar(id=3,umax=7,var_off=(0x0; 0x7)) R10=fp0 fp-8=0000mmmm 22: R0=map_value(off=0,ks=4,vs=48,imm=0) R2=scalar(id=3,umax=7,var_off=(0x0; 0x7)) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R8=map_value(off=0,ks=4,vs=48,imm=0) R9=scalar(id=3,umax=7,var_off=(0x0; 0x7)) R10=fp0 fp-8=0000mmmm 22: (07) r2 += 1 ; R2_w=scalar(umin=1,umax=8,var_off=(0x0; 0xf)) 23: (bf) r1 = r10 ; R1_w=fp0 R10=fp0 24: (07) r1 += -8 ; R1_w=fp-8 25: (b7) r3 = 0 ; R3_w=0 26: (85) call bpf_probe_read_kernel#113 mark_precise: frame0: last_idx 26 first_idx 20 subseq_idx -1 mark_precise: frame0: regs=r2 stack= before 25: (b7) r3 = 0 mark_precise: frame0: regs=r2 stack= before 24: (07) r1 += -8 mark_precise: frame0: regs=r2 stack= before 23: (bf) r1 = r10 mark_precise: frame0: regs=r2 stack= before 22: (07) r2 += 1 mark_precise: frame0: regs=r2 stack= before 20: (a5) if r2 < 0x8 goto pc+1 mark_precise: frame0: parent state regs=r2,r9 stack=: R0_rw=map_value(off=0,ks=4,vs=48,imm=0) R2_rw=Pscalar(id=3) R6=map_ptr(off=0,ks=4,vs=48,imm=0) R8_w=map_value(off=0,ks=4,vs=48,imm=0) R9_w=Pscalar(id=3) R10=fp0 fp-8_r=0000mmmm mark_precise: frame0: last_idx 19 first_idx 10 subseq_idx 20 mark_precise: frame0: regs=r2,r9 stack= before 19: (bf) r2 = r9 mark_precise: frame0: regs=r9 stack= before 18: (1f) r9 -= r8 mark_precise: frame0: regs=r8,r9 stack= before 17: (bf) r8 = r0 mark_precise: frame0: regs=r0,r9 stack= before 15: (55) if r0 != 0x0 goto pc+1 mark_precise: frame0: regs=r0,r9 stack= before 14: (85) call bpf_map_lookup_elem#1 mark_precise: frame0: regs=r9 stack= before 13: (07) r2 += -8 mark_precise: frame0: regs=r9 stack= before 12: (bf) r2 = r10 mark_precise: frame0: regs=r9 stack= before 11: (bf) r1 = r6 mark_precise: frame0: regs=r9 stack= before 10: (bf) r9 = r0 mark_precise: frame0: parent state regs= stack=: R0_rw=map_value(off=0,ks=4,vs=48,imm=0) R6_rw=map_ptr(off=0,ks=4,vs=48,imm=0) R10=fp0 fp-8_rw=0000mmmm 27: R0_w=scalar() 27: (95) exit processed 27 insns (limit 1000000) max_states_per_insn 0 total_states 2 peak_states 2 mark_read 1 [...]