Skip to content

Commit 6995ee6

Browse files
anakryikoKernel Patches Daemon
authored and
Kernel Patches Daemon
committed
bpf: enhance BPF_JEQ/BPF_JNE is_branch_taken logic
Use 32-bit subranges to prune some 64-bit BPF_JEQ/BPF_JNE conditions that otherwise would be "inconclusive" (i.e., is_branch_taken() would return -1). This can happen, for example, when registers are initialized as 64-bit u64/s64, then compared for inequality as 32-bit subregisters, and then followed by 64-bit equality/inequality check. That 32-bit inequality can establish some pattern for lower 32 bits of a register (e.g., s< 0 condition determines whether the bit #31 is zero or not), while overall 64-bit value could be anything (according to a value range representation). This is not a fancy quirky special case, but actually a handling that's necessary to prevent correctness issue with BPF verifier's range tracking: set_range_min_max() assumes that register ranges are non-overlapping, and if that condition is not guaranteed by is_branch_taken() we can end up with invalid ranges, where min > max. [0] https://lore.kernel.org/bpf/CACkBjsY2q1_fUohD7hRmKGqv1MV=eP2f6XK8kjkYNw7BaiF8iQ@mail.gmail.com/ Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: Eduard Zingerman <[email protected]>
1 parent de0122e commit 6995ee6

File tree

1 file changed

+24
-0
lines changed

1 file changed

+24
-0
lines changed

kernel/bpf/verifier.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14246,6 +14246,18 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1424614246
return 0;
1424714247
if (smin1 > smax2 || smax1 < smin2)
1424814248
return 0;
14249+
if (!is_jmp32) {
14250+
/* if 64-bit ranges are inconclusive, see if we can
14251+
* utilize 32-bit subrange knowledge to eliminate
14252+
* branches that can't be taken a priori
14253+
*/
14254+
if (reg1->u32_min_value > reg2->u32_max_value ||
14255+
reg1->u32_max_value < reg2->u32_min_value)
14256+
return 0;
14257+
if (reg1->s32_min_value > reg2->s32_max_value ||
14258+
reg1->s32_max_value < reg2->s32_min_value)
14259+
return 0;
14260+
}
1424914261
break;
1425014262
case BPF_JNE:
1425114263
/* constants, umin/umax and smin/smax checks would be
@@ -14258,6 +14270,18 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1425814270
return 1;
1425914271
if (smin1 > smax2 || smax1 < smin2)
1426014272
return 1;
14273+
if (!is_jmp32) {
14274+
/* if 64-bit ranges are inconclusive, see if we can
14275+
* utilize 32-bit subrange knowledge to eliminate
14276+
* branches that can't be taken a priori
14277+
*/
14278+
if (reg1->u32_min_value > reg2->u32_max_value ||
14279+
reg1->u32_max_value < reg2->u32_min_value)
14280+
return 1;
14281+
if (reg1->s32_min_value > reg2->s32_max_value ||
14282+
reg1->s32_max_value < reg2->s32_min_value)
14283+
return 1;
14284+
}
1426114285
break;
1426214286
case BPF_JSET:
1426314287
if (!is_reg_const(reg2, is_jmp32)) {

0 commit comments

Comments
 (0)