Skip to content

Commit 26e5e34

Browse files
pchaignoAlexei Starovoitov
authored andcommitted
selftests/bpf: Test cross-sign 64bits range refinement
This patch adds coverage for the new cross-sign 64bits range refinement logic. The three tests cover the cases when the u64 and s64 ranges overlap (1) in the negative portion of s64, (2) in the positive portion of s64, and (3) in both portions. The first test is a simplified version of a BPF program generated by syzkaller that caused an invariant violation [1]. It looks like syzkaller could not extract the reproducer itself (and therefore didn't report it to the mailing list), but I was able to extract it from the console logs of a crash. The principle is similar to the invariant violation described in commit 6279846 ("bpf: Forget ranges when refining tnum after JSET"): the verifier walks a dead branch, uses the condition to refine ranges, and ends up with inconsistent ranges. In this case, the dead branch is when we fallthrough on both jumps. The new refinement logic improves the bounds such that the second jump is properly detected as always-taken and the verifier doesn't end up walking a dead branch. The second and third tests are inspired by the first, but rely on condition jumps to prepare the bounds instead of ALU instructions. An R10 write is used to trigger a verifier error when the bounds can't be refined. Link: https://syzkaller.appspot.com/bug?extid=c711ce17dd78e5d4fdcf [1] Acked-by: Eduard Zingerman <[email protected]> Signed-off-by: Paul Chaignon <[email protected]> Link: https://lore.kernel.org/r/a0e17b00dab8dabcfa6f8384e7e151186efedfdd.1753695655.git.paul.chaignon@gmail.com Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent da653de commit 26e5e34

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed

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

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,4 +1550,122 @@ l0_%=: r0 = 0; \
15501550
: __clobber_all);
15511551
}
15521552

1553+
/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges
1554+
* overlap on the negative side. At instruction 7, the ranges look as follows:
1555+
*
1556+
* 0 umin=0xfffffcf1 umax=0xff..ff6e U64_MAX
1557+
* | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] |
1558+
* |----------------------------|------------------------------|
1559+
* |xxxxxxxxxx] [xxxxxxxxxxxx|
1560+
* 0 smax=0xeffffeee smin=-655 -1
1561+
*
1562+
* We should therefore deduce the following new bounds:
1563+
*
1564+
* 0 u64=[0xff..ffd71;0xff..ff6e] U64_MAX
1565+
* | [xxx] |
1566+
* |----------------------------|------------------------------|
1567+
* | [xxx] |
1568+
* 0 s64=[-655;-146] -1
1569+
*
1570+
* Without the deduction cross sign boundary, we end up with an invariant
1571+
* violation error.
1572+
*/
1573+
SEC("socket")
1574+
__description("bounds deduction cross sign boundary, negative overlap")
1575+
__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS)
1576+
__msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))")
1577+
__retval(0)
1578+
__naked void bounds_deduct_negative_overlap(void)
1579+
{
1580+
asm volatile(" \
1581+
call %[bpf_get_prandom_u32]; \
1582+
w3 = w0; \
1583+
w6 = (s8)w0; \
1584+
r0 = (s8)r0; \
1585+
if w6 >= 0xf0000000 goto l0_%=; \
1586+
r0 += r6; \
1587+
r6 += 400; \
1588+
r0 -= r6; \
1589+
if r3 < r0 goto l0_%=; \
1590+
l0_%=: r0 = 0; \
1591+
exit; \
1592+
" :
1593+
: __imm(bpf_get_prandom_u32)
1594+
: __clobber_all);
1595+
}
1596+
1597+
/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges
1598+
* overlap on the positive side. At instruction 3, the ranges look as follows:
1599+
*
1600+
* 0 umin=0 umax=0xffffffffffffff00 U64_MAX
1601+
* [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] |
1602+
* |----------------------------|------------------------------|
1603+
* |xxxxxxxx] [xxxxxxxx|
1604+
* 0 smax=127 smin=-128 -1
1605+
*
1606+
* We should therefore deduce the following new bounds:
1607+
*
1608+
* 0 u64=[0;127] U64_MAX
1609+
* [xxxxxxxx] |
1610+
* |----------------------------|------------------------------|
1611+
* [xxxxxxxx] |
1612+
* 0 s64=[0;127] -1
1613+
*
1614+
* Without the deduction cross sign boundary, the program is rejected due to
1615+
* the frame pointer write.
1616+
*/
1617+
SEC("socket")
1618+
__description("bounds deduction cross sign boundary, positive overlap")
1619+
__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS)
1620+
__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=127,var_off=(0x0; 0x7f))")
1621+
__retval(0)
1622+
__naked void bounds_deduct_positive_overlap(void)
1623+
{
1624+
asm volatile(" \
1625+
call %[bpf_get_prandom_u32]; \
1626+
r0 = (s8)r0; \
1627+
r1 = 0xffffffffffffff00; \
1628+
if r0 > r1 goto l0_%=; \
1629+
if r0 < 128 goto l0_%=; \
1630+
r10 = 0; \
1631+
l0_%=: r0 = 0; \
1632+
exit; \
1633+
" :
1634+
: __imm(bpf_get_prandom_u32)
1635+
: __clobber_all);
1636+
}
1637+
1638+
/* This test is the same as above, but the s64 and u64 ranges overlap in two
1639+
* places. At instruction 3, the ranges look as follows:
1640+
*
1641+
* 0 umin=0 umax=0xffffffffffffff80 U64_MAX
1642+
* [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] |
1643+
* |----------------------------|------------------------------|
1644+
* |xxxxxxxx] [xxxxxxxx|
1645+
* 0 smax=127 smin=-128 -1
1646+
*
1647+
* 0xffffffffffffff80 = (u64)-128. We therefore can't deduce anything new and
1648+
* the program should fail due to the frame pointer write.
1649+
*/
1650+
SEC("socket")
1651+
__description("bounds deduction cross sign boundary, two overlaps")
1652+
__failure __flag(BPF_F_TEST_REG_INVARIANTS)
1653+
__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=-128,smax=smax32=127,umax=0xffffffffffffff80)")
1654+
__msg("frame pointer is read only")
1655+
__naked void bounds_deduct_two_overlaps(void)
1656+
{
1657+
asm volatile(" \
1658+
call %[bpf_get_prandom_u32]; \
1659+
r0 = (s8)r0; \
1660+
r1 = 0xffffffffffffff80; \
1661+
if r0 > r1 goto l0_%=; \
1662+
if r0 < 128 goto l0_%=; \
1663+
r10 = 0; \
1664+
l0_%=: r0 = 0; \
1665+
exit; \
1666+
" :
1667+
: __imm(bpf_get_prandom_u32)
1668+
: __clobber_all);
1669+
}
1670+
15531671
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)