Skip to content

Commit 269d809

Browse files
pchaignogregkh
authored andcommitted
selftests/bpf: Test cross-sign 64bits range refinement
[ Upstream commit 26e5e34 ] 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 <eddyz87@gmail.com> Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> Link: https://lore.kernel.org/r/a0e17b00dab8dabcfa6f8384e7e151186efedfdd.1753695655.git.paul.chaignon@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent d7d7b48 commit 269d809

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

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

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,4 +1200,122 @@ l0_%=: r0 = 0; \
12001200
: __clobber_all);
12011201
}
12021202

1203+
/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges
1204+
* overlap on the negative side. At instruction 7, the ranges look as follows:
1205+
*
1206+
* 0 umin=0xfffffcf1 umax=0xff..ff6e U64_MAX
1207+
* | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] |
1208+
* |----------------------------|------------------------------|
1209+
* |xxxxxxxxxx] [xxxxxxxxxxxx|
1210+
* 0 smax=0xeffffeee smin=-655 -1
1211+
*
1212+
* We should therefore deduce the following new bounds:
1213+
*
1214+
* 0 u64=[0xff..ffd71;0xff..ff6e] U64_MAX
1215+
* | [xxx] |
1216+
* |----------------------------|------------------------------|
1217+
* | [xxx] |
1218+
* 0 s64=[-655;-146] -1
1219+
*
1220+
* Without the deduction cross sign boundary, we end up with an invariant
1221+
* violation error.
1222+
*/
1223+
SEC("socket")
1224+
__description("bounds deduction cross sign boundary, negative overlap")
1225+
__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS)
1226+
__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))")
1227+
__retval(0)
1228+
__naked void bounds_deduct_negative_overlap(void)
1229+
{
1230+
asm volatile(" \
1231+
call %[bpf_get_prandom_u32]; \
1232+
w3 = w0; \
1233+
w6 = (s8)w0; \
1234+
r0 = (s8)r0; \
1235+
if w6 >= 0xf0000000 goto l0_%=; \
1236+
r0 += r6; \
1237+
r6 += 400; \
1238+
r0 -= r6; \
1239+
if r3 < r0 goto l0_%=; \
1240+
l0_%=: r0 = 0; \
1241+
exit; \
1242+
" :
1243+
: __imm(bpf_get_prandom_u32)
1244+
: __clobber_all);
1245+
}
1246+
1247+
/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges
1248+
* overlap on the positive side. At instruction 3, the ranges look as follows:
1249+
*
1250+
* 0 umin=0 umax=0xffffffffffffff00 U64_MAX
1251+
* [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] |
1252+
* |----------------------------|------------------------------|
1253+
* |xxxxxxxx] [xxxxxxxx|
1254+
* 0 smax=127 smin=-128 -1
1255+
*
1256+
* We should therefore deduce the following new bounds:
1257+
*
1258+
* 0 u64=[0;127] U64_MAX
1259+
* [xxxxxxxx] |
1260+
* |----------------------------|------------------------------|
1261+
* [xxxxxxxx] |
1262+
* 0 s64=[0;127] -1
1263+
*
1264+
* Without the deduction cross sign boundary, the program is rejected due to
1265+
* the frame pointer write.
1266+
*/
1267+
SEC("socket")
1268+
__description("bounds deduction cross sign boundary, positive overlap")
1269+
__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS)
1270+
__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=127,var_off=(0x0; 0x7f))")
1271+
__retval(0)
1272+
__naked void bounds_deduct_positive_overlap(void)
1273+
{
1274+
asm volatile(" \
1275+
call %[bpf_get_prandom_u32]; \
1276+
r0 = (s8)r0; \
1277+
r1 = 0xffffffffffffff00; \
1278+
if r0 > r1 goto l0_%=; \
1279+
if r0 < 128 goto l0_%=; \
1280+
r10 = 0; \
1281+
l0_%=: r0 = 0; \
1282+
exit; \
1283+
" :
1284+
: __imm(bpf_get_prandom_u32)
1285+
: __clobber_all);
1286+
}
1287+
1288+
/* This test is the same as above, but the s64 and u64 ranges overlap in two
1289+
* places. At instruction 3, the ranges look as follows:
1290+
*
1291+
* 0 umin=0 umax=0xffffffffffffff80 U64_MAX
1292+
* [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] |
1293+
* |----------------------------|------------------------------|
1294+
* |xxxxxxxx] [xxxxxxxx|
1295+
* 0 smax=127 smin=-128 -1
1296+
*
1297+
* 0xffffffffffffff80 = (u64)-128. We therefore can't deduce anything new and
1298+
* the program should fail due to the frame pointer write.
1299+
*/
1300+
SEC("socket")
1301+
__description("bounds deduction cross sign boundary, two overlaps")
1302+
__failure __flag(BPF_F_TEST_REG_INVARIANTS)
1303+
__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=-128,smax=smax32=127,umax=0xffffffffffffff80)")
1304+
__msg("frame pointer is read only")
1305+
__naked void bounds_deduct_two_overlaps(void)
1306+
{
1307+
asm volatile(" \
1308+
call %[bpf_get_prandom_u32]; \
1309+
r0 = (s8)r0; \
1310+
r1 = 0xffffffffffffff80; \
1311+
if r0 > r1 goto l0_%=; \
1312+
if r0 < 128 goto l0_%=; \
1313+
r10 = 0; \
1314+
l0_%=: r0 = 0; \
1315+
exit; \
1316+
" :
1317+
: __imm(bpf_get_prandom_u32)
1318+
: __clobber_all);
1319+
}
1320+
12031321
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)