Thread (11 messages) 11 messages, 4 authors, 2d ago

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

[...]
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help