Extend the verifier_linked_scalars BPF selftest with a rX += rX test
such that the div-by-zero path is rejected in the fixed case.
# LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_linked_scalars
[...]
./test_progs -t verifier_linked_scalars
#612/1 verifier_linked_scalars/scalars: find linked scalars:OK
#612/2 verifier_linked_scalars/sync_linked_regs_preserves_id:OK
#612/3 verifier_linked_scalars/scalars_neg:OK
#612/4 verifier_linked_scalars/scalars_neg_sub:OK
#612/5 verifier_linked_scalars/scalars_neg_alu32_add:OK
#612/6 verifier_linked_scalars/scalars_neg_alu32_sub:OK
#612/7 verifier_linked_scalars/scalars_pos:OK
#612/8 verifier_linked_scalars/scalars_sub_neg_imm:OK
#612/9 verifier_linked_scalars/scalars_double_add:OK
#612/10 verifier_linked_scalars/scalars_sync_delta_overflow:OK
#612/11 verifier_linked_scalars/scalars_sync_delta_overflow_large_range:OK
#612/12 verifier_linked_scalars/scalars_alu32_big_offset:OK
#612/13 verifier_linked_scalars/scalars_alu32_basic:OK
#612/14 verifier_linked_scalars/scalars_alu32_wrap:OK
#612/15 verifier_linked_scalars/scalars_alu32_zext_linked_reg:OK
#612/16 verifier_linked_scalars/scalars_alu32_alu64_cross_type:OK
#612/17 verifier_linked_scalars/scalars_alu32_alu64_regsafe_pruning:OK
#612/18 verifier_linked_scalars/alu32_negative_offset:OK
#612/19 verifier_linked_scalars/spurious_precision_marks:OK
#612/20 verifier_linked_scalars/scalars_self_add_clears_id:OK
#612/21 verifier_linked_scalars/scalars_self_add_alu32_clears_id:OK
#612 verifier_linked_scalars:OK
Summary: 1/21 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260407192421.508817-3-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
return 0;
}
+/*
+ * Test that r += r (self-add, src_reg == dst_reg) clears the scalar ID
+ * so that sync_linked_regs() does not propagate an incorrect delta.
+ */
+SEC("socket")
+__failure
+__msg("div by zero")
+__naked void scalars_self_add_clears_id(void)
+{
+ asm volatile (" \
+ call %[bpf_get_prandom_u32]; \
+ r6 = r0; /* r6 unknown, id A */ \
+ r7 = r6; /* r7 linked to r6, id A */ \
+ call %[bpf_get_prandom_u32]; \
+ r8 = r0; /* r8 unknown, id B */ \
+ r9 = r8; /* r9 linked to r8, id B */ \
+ if r7 != 1 goto l_exit_%=; \
+ /* r7 == 1; sync propagates: r6 = 1 (known, id A) */ \
+ r6 += r6; /* r6 = 2; should clear id */ \
+ if r7 == r9 goto l_exit_%=; \
+ /* Bug: r6 synced to r7(1)+delta(2)=3; Fix: r6 = 2 */ \
+ if r6 == 3 goto l_exit_%=; \
+ r0 /= 0; \
+l_exit_%=: \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
+/* Same as above but with alu32 such that w6 += w6 also clears id. */
+SEC("socket")
+__failure
+__msg("div by zero")
+__naked void scalars_self_add_alu32_clears_id(void)
+{
+ asm volatile (" \
+ call %[bpf_get_prandom_u32]; \
+ w6 = w0; \
+ w7 = w6; \
+ call %[bpf_get_prandom_u32]; \
+ w8 = w0; \
+ w9 = w8; \
+ if w7 != 1 goto l_exit_%=; \
+ w6 += w6; \
+ if w7 == w9 goto l_exit_%=; \
+ if w6 == 3 goto l_exit_%=; \
+ r0 /= 0; \
+l_exit_%=: \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";