Skip to content

Commit 4ede46e

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 5d5ea26 commit 4ede46e

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
@@ -14214,6 +14214,18 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1421414214
return 0;
1421514215
if (smin1 > smax2 || smax1 < smin2)
1421614216
return 0;
14217+
if (!is_jmp32) {
14218+
/* if 64-bit ranges are inconclusive, see if we can
14219+
* utilize 32-bit subrange knowledge to eliminate
14220+
* branches that can't be taken a priori
14221+
*/
14222+
if (reg1->u32_min_value > reg2->u32_max_value ||
14223+
reg1->u32_max_value < reg2->u32_min_value)
14224+
return 0;
14225+
if (reg1->s32_min_value > reg2->s32_max_value ||
14226+
reg1->s32_max_value < reg2->s32_min_value)
14227+
return 0;
14228+
}
1421714229
break;
1421814230
case BPF_JNE:
1421914231
/* constants, umin/umax and smin/smax checks would be
@@ -14226,6 +14238,18 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1422614238
return 1;
1422714239
if (smin1 > smax2 || smax1 < smin2)
1422814240
return 1;
14241+
if (!is_jmp32) {
14242+
/* if 64-bit ranges are inconclusive, see if we can
14243+
* utilize 32-bit subrange knowledge to eliminate
14244+
* branches that can't be taken a priori
14245+
*/
14246+
if (reg1->u32_min_value > reg2->u32_max_value ||
14247+
reg1->u32_max_value < reg2->u32_min_value)
14248+
return 1;
14249+
if (reg1->s32_min_value > reg2->s32_max_value ||
14250+
reg1->s32_max_value < reg2->s32_min_value)
14251+
return 1;
14252+
}
1422914253
break;
1423014254
case BPF_JSET:
1423114255
if (!is_reg_const(reg2, is_jmp32)) {

0 commit comments

Comments
 (0)