Skip to content

Commit 4a04d13

Browse files
borkmannAlexei Starovoitov
authored andcommitted
selftests/bpf: Add a test cases for sync_linked_regs regarding zext propagation
Add multiple test cases for linked register tracking with alu32 ops: - Add a test that checks sync_linked_regs() regarding reg->id (the linked target register) for BPF_ADD_CONST32 rather than known_reg->id (the branch register). - Add a test case for linked register tracking that exposes the cross-type sync_linked_regs() bug. One register uses alu32 (w7 += 1, BPF_ADD_CONST32) and another uses alu64 (r8 += 2, BPF_ADD_CONST64), both linked to the same base register. - Add a test case that exercises regsafe() path pruning when two execution paths reach the same program point with linked registers carrying different ADD_CONST flags (BPF_ADD_CONST32 from alu32 vs BPF_ADD_CONST64 from alu64). This particular test passes with and without the fix since the pruning will fail due to different ranges, but it would still be useful to carry this one as a regression test for the unreachable div by zero. With the fix applied all the tests pass: # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_linked_scalars [...] ./test_progs -t verifier_linked_scalars #602/1 verifier_linked_scalars/scalars: find linked scalars:OK #602/2 verifier_linked_scalars/sync_linked_regs_preserves_id:OK #602/3 verifier_linked_scalars/scalars_neg:OK #602/4 verifier_linked_scalars/scalars_neg_sub:OK #602/5 verifier_linked_scalars/scalars_neg_alu32_add:OK #602/6 verifier_linked_scalars/scalars_neg_alu32_sub:OK #602/7 verifier_linked_scalars/scalars_pos:OK #602/8 verifier_linked_scalars/scalars_sub_neg_imm:OK #602/9 verifier_linked_scalars/scalars_double_add:OK #602/10 verifier_linked_scalars/scalars_sync_delta_overflow:OK #602/11 verifier_linked_scalars/scalars_sync_delta_overflow_large_range:OK #602/12 verifier_linked_scalars/scalars_alu32_big_offset:OK #602/13 verifier_linked_scalars/scalars_alu32_basic:OK #602/14 verifier_linked_scalars/scalars_alu32_wrap:OK #602/15 verifier_linked_scalars/scalars_alu32_zext_linked_reg:OK #602/16 verifier_linked_scalars/scalars_alu32_alu64_cross_type:OK #602/17 verifier_linked_scalars/scalars_alu32_alu64_regsafe_pruning:OK #602/18 verifier_linked_scalars/alu32_negative_offset:OK #602/19 verifier_linked_scalars/spurious_precision_marks:OK #602 verifier_linked_scalars:OK Summary: 1/19 PASSED, 0 SKIPPED, 0 FAILED Co-developed-by: Puranjay Mohan <[email protected]> Signed-off-by: Puranjay Mohan <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent bc308be commit 4a04d13

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

tools/testing/selftests/bpf/progs/verifier_linked_scalars.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,114 @@ l0_%=: \
348348
: __clobber_all);
349349
}
350350

351+
/*
352+
* Test that sync_linked_regs() checks reg->id (the linked target register)
353+
* for BPF_ADD_CONST32 rather than known_reg->id (the branch register).
354+
*/
355+
SEC("socket")
356+
__success
357+
__naked void scalars_alu32_zext_linked_reg(void)
358+
{
359+
asm volatile (" \
360+
call %[bpf_get_prandom_u32]; \
361+
w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \
362+
r7 = r6; /* linked: same id as r6 */ \
363+
w7 += 1; /* alu32: r7.id |= BPF_ADD_CONST32 */ \
364+
r8 = 0xFFFFffff ll; \
365+
if r6 < r8 goto l0_%=; \
366+
/* r6 in [0xFFFFFFFF, 0xFFFFFFFF] */ \
367+
/* sync_linked_regs: known_reg=r6, reg=r7 */ \
368+
/* CPU: w7 = (u32)(0xFFFFFFFF + 1) = 0, zext -> r7 = 0 */ \
369+
/* With fix: r7 64-bit = [0, 0] (zext applied) */ \
370+
/* Without fix: r7 64-bit = [0x100000000] (no zext) */ \
371+
r7 >>= 32; \
372+
if r7 == 0 goto l0_%=; \
373+
r0 /= 0; /* unreachable with fix */ \
374+
l0_%=: \
375+
r0 = 0; \
376+
exit; \
377+
" :
378+
: __imm(bpf_get_prandom_u32)
379+
: __clobber_all);
380+
}
381+
382+
/*
383+
* Test that sync_linked_regs() skips propagation when one register used
384+
* alu32 (BPF_ADD_CONST32) and the other used alu64 (BPF_ADD_CONST64).
385+
* The delta relationship doesn't hold across different ALU widths.
386+
*/
387+
SEC("socket")
388+
__failure __msg("div by zero")
389+
__naked void scalars_alu32_alu64_cross_type(void)
390+
{
391+
asm volatile (" \
392+
call %[bpf_get_prandom_u32]; \
393+
w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \
394+
r7 = r6; /* linked: same id as r6 */ \
395+
w7 += 1; /* alu32: BPF_ADD_CONST32, delta = 1 */ \
396+
r8 = r6; /* linked: same id as r6 */ \
397+
r8 += 2; /* alu64: BPF_ADD_CONST64, delta = 2 */ \
398+
r9 = 0xFFFFffff ll; \
399+
if r7 < r9 goto l0_%=; \
400+
/* r7 = 0xFFFFFFFF */ \
401+
/* sync: known_reg=r7 (ADD_CONST32), reg=r8 (ADD_CONST64) */ \
402+
/* Without fix: r8 = zext(0xFFFFFFFF + 1) = 0 */ \
403+
/* With fix: r8 stays [2, 0x100000001] (r8 >= 2) */ \
404+
if r8 > 0 goto l1_%=; \
405+
goto l0_%=; \
406+
l1_%=: \
407+
r0 /= 0; /* div by zero */ \
408+
l0_%=: \
409+
r0 = 0; \
410+
exit; \
411+
" :
412+
: __imm(bpf_get_prandom_u32)
413+
: __clobber_all);
414+
}
415+
416+
/*
417+
* Test that regsafe() prevents pruning when two paths reach the same program
418+
* point with linked registers carrying different ADD_CONST flags (one
419+
* BPF_ADD_CONST32 from alu32, another BPF_ADD_CONST64 from alu64).
420+
*/
421+
SEC("socket")
422+
__failure __msg("div by zero")
423+
__flag(BPF_F_TEST_STATE_FREQ)
424+
__naked void scalars_alu32_alu64_regsafe_pruning(void)
425+
{
426+
asm volatile (" \
427+
call %[bpf_get_prandom_u32]; \
428+
w6 = w0; /* r6 in [0, 0xFFFFFFFF] */ \
429+
r7 = r6; /* linked: same id as r6 */ \
430+
/* Get another random value for the path branch */ \
431+
call %[bpf_get_prandom_u32]; \
432+
if r0 > 0 goto l_pathb_%=; \
433+
/* Path A: alu32 */ \
434+
w7 += 1; /* BPF_ADD_CONST32, delta = 1 */\
435+
goto l_merge_%=; \
436+
l_pathb_%=: \
437+
/* Path B: alu64 */ \
438+
r7 += 1; /* BPF_ADD_CONST64, delta = 1 */\
439+
l_merge_%=: \
440+
/* Merge point: regsafe() compares path B against cached path A. */ \
441+
/* Narrow r6 to trigger sync_linked_regs for r7 */ \
442+
r9 = 0xFFFFffff ll; \
443+
if r6 < r9 goto l0_%=; \
444+
/* r6 = 0xFFFFFFFF */ \
445+
/* sync: r7 = 0xFFFFFFFF + 1 = 0x100000000 */ \
446+
/* Path A: zext -> r7 = 0 */ \
447+
/* Path B: no zext -> r7 = 0x100000000 */ \
448+
r7 >>= 32; \
449+
if r7 == 0 goto l0_%=; \
450+
r0 /= 0; /* div by zero on path B */ \
451+
l0_%=: \
452+
r0 = 0; \
453+
exit; \
454+
" :
455+
: __imm(bpf_get_prandom_u32)
456+
: __clobber_all);
457+
}
458+
351459
SEC("socket")
352460
__success
353461
void alu32_negative_offset(void)

0 commit comments

Comments
 (0)