Skip to content

Commit d23070e

Browse files
anakryikoKernel Patches Daemon
authored andcommitted
bpf: generalize is_scalar_branch_taken() logic
Generalize is_branch_taken logic for SCALAR_VALUE register to handle cases when both registers are not constants. Previously supported <range> vs <scalar> cases are a natural subset of more generic <range> vs <range> set of cases. Generalized logic relies on straightforward segment intersection checks. Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
1 parent d6d742b commit d23070e

File tree

1 file changed

+64
-40
lines changed

1 file changed

+64
-40
lines changed

kernel/bpf/verifier.c

Lines changed: 64 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14189,82 +14189,105 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1418914189
u8 opcode, bool is_jmp32)
1419014190
{
1419114191
struct tnum t1 = is_jmp32 ? tnum_subreg(reg1->var_off) : reg1->var_off;
14192+
struct tnum t2 = is_jmp32 ? tnum_subreg(reg2->var_off) : reg2->var_off;
1419214193
u64 umin1 = is_jmp32 ? (u64)reg1->u32_min_value : reg1->umin_value;
1419314194
u64 umax1 = is_jmp32 ? (u64)reg1->u32_max_value : reg1->umax_value;
1419414195
s64 smin1 = is_jmp32 ? (s64)reg1->s32_min_value : reg1->smin_value;
1419514196
s64 smax1 = is_jmp32 ? (s64)reg1->s32_max_value : reg1->smax_value;
14196-
u64 val = is_jmp32 ? (u32)tnum_subreg(reg2->var_off).value : reg2->var_off.value;
14197-
s64 sval = is_jmp32 ? (s32)val : (s64)val;
14197+
u64 umin2 = is_jmp32 ? (u64)reg2->u32_min_value : reg2->umin_value;
14198+
u64 umax2 = is_jmp32 ? (u64)reg2->u32_max_value : reg2->umax_value;
14199+
s64 smin2 = is_jmp32 ? (s64)reg2->s32_min_value : reg2->smin_value;
14200+
s64 smax2 = is_jmp32 ? (s64)reg2->s32_max_value : reg2->smax_value;
1419814201

1419914202
switch (opcode) {
1420014203
case BPF_JEQ:
14201-
if (tnum_is_const(t1))
14202-
return !!tnum_equals_const(t1, val);
14203-
else if (val < umin1 || val > umax1)
14204+
/* const tnums */
14205+
if (tnum_is_const(t1) && tnum_is_const(t2))
14206+
return t1.value == t2.value;
14207+
/* const ranges */
14208+
if (umin1 == umax1 && umin2 == umax2)
14209+
return umin1 == umin2;
14210+
if (smin1 == smax1 && smin2 == smax2)
14211+
return umin1 == umin2;
14212+
/* non-overlapping ranges */
14213+
if (umin1 > umax2 || umax1 < umin2)
1420414214
return 0;
14205-
else if (sval < smin1 || sval > smax1)
14215+
if (smin1 > smax2 || smax1 < smin2)
1420614216
return 0;
1420714217
break;
1420814218
case BPF_JNE:
14209-
if (tnum_is_const(t1))
14210-
return !tnum_equals_const(t1, val);
14211-
else if (val < umin1 || val > umax1)
14219+
/* const tnums */
14220+
if (tnum_is_const(t1) && tnum_is_const(t2))
14221+
return t1.value != t2.value;
14222+
/* const ranges */
14223+
if (umin1 == umax1 && umin2 == umax2)
14224+
return umin1 != umin2;
14225+
if (smin1 == smax1 && smin2 == smax2)
14226+
return umin1 != umin2;
14227+
/* non-overlapping ranges */
14228+
if (umin1 > umax2 || umax1 < umin2)
1421214229
return 1;
14213-
else if (sval < smin1 || sval > smax1)
14230+
if (smin1 > smax2 || smax1 < smin2)
1421414231
return 1;
1421514232
break;
1421614233
case BPF_JSET:
14217-
if ((~t1.mask & t1.value) & val)
14234+
if (!is_reg_const(reg2, is_jmp32)) {
14235+
swap(reg1, reg2);
14236+
swap(t1, t2);
14237+
}
14238+
if (!is_reg_const(reg2, is_jmp32))
14239+
return -1;
14240+
if ((~t1.mask & t1.value) & t2.value)
1421814241
return 1;
14219-
if (!((t1.mask | t1.value) & val))
14242+
if (!((t1.mask | t1.value) & t2.value))
1422014243
return 0;
1422114244
break;
1422214245
case BPF_JGT:
14223-
if (umin1 > val )
14246+
if (umin1 > umax2)
1422414247
return 1;
14225-
else if (umax1 <= val)
14248+
else if (umax1 <= umin2)
1422614249
return 0;
1422714250
break;
1422814251
case BPF_JSGT:
14229-
if (smin1 > sval)
14252+
if (smin1 > smax2)
1423014253
return 1;
14231-
else if (smax1 <= sval)
14254+
else if (smax1 <= smin2)
1423214255
return 0;
1423314256
break;
1423414257
case BPF_JLT:
14235-
if (umax1 < val)
14258+
if (umax1 < umin2)
1423614259
return 1;
14237-
else if (umin1 >= val)
14260+
else if (umin1 >= umax2)
1423814261
return 0;
1423914262
break;
1424014263
case BPF_JSLT:
14241-
if (smax1 < sval)
14264+
if (smax1 < smin2)
1424214265
return 1;
14243-
else if (smin1 >= sval)
14266+
else if (smin1 >= smax2)
1424414267
return 0;
1424514268
break;
1424614269
case BPF_JGE:
14247-
if (umin1 >= val)
14270+
if (umin1 >= umax2)
1424814271
return 1;
14249-
else if (umax1 < val)
14272+
else if (umax1 < umin2)
1425014273
return 0;
1425114274
break;
1425214275
case BPF_JSGE:
14253-
if (smin1 >= sval)
14276+
if (smin1 >= smax2)
1425414277
return 1;
14255-
else if (smax1 < sval)
14278+
else if (smax1 < smin2)
1425614279
return 0;
1425714280
break;
1425814281
case BPF_JLE:
14259-
if (umax1 <= val)
14282+
if (umax1 <= umin2)
1426014283
return 1;
14261-
else if (umin1 > val)
14284+
else if (umin1 > umax2)
1426214285
return 0;
1426314286
break;
1426414287
case BPF_JSLE:
14265-
if (smax1 <= sval)
14288+
if (smax1 <= smin2)
1426614289
return 1;
14267-
else if (smin1 > sval)
14290+
else if (smin1 > smax2)
1426814291
return 0;
1426914292
break;
1427014293
}
@@ -14343,28 +14366,28 @@ static int is_pkt_ptr_branch_taken(struct bpf_reg_state *dst_reg,
1434314366
static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2,
1434414367
u8 opcode, bool is_jmp32)
1434514368
{
14346-
u64 val;
14347-
1434814369
if (reg_is_pkt_pointer_any(reg1) && reg_is_pkt_pointer_any(reg2) && !is_jmp32)
1434914370
return is_pkt_ptr_branch_taken(reg1, reg2, opcode);
1435014371

14351-
/* try to make sure reg2 is a constant SCALAR_VALUE */
14352-
if (!is_reg_const(reg2, is_jmp32)) {
14353-
opcode = flip_opcode(opcode);
14354-
swap(reg1, reg2);
14355-
}
14356-
/* for now we expect reg2 to be a constant to make any useful decisions */
14357-
if (!is_reg_const(reg2, is_jmp32))
14358-
return -1;
14359-
val = reg_const_value(reg2, is_jmp32);
14372+
if (__is_pointer_value(false, reg1) || __is_pointer_value(false, reg2)) {
14373+
u64 val;
14374+
14375+
/* arrange that reg2 is a scalar, and reg1 is a pointer */
14376+
if (!is_reg_const(reg2, is_jmp32)) {
14377+
opcode = flip_opcode(opcode);
14378+
swap(reg1, reg2);
14379+
}
14380+
/* and ensure that reg2 is a constant */
14381+
if (!is_reg_const(reg2, is_jmp32))
14382+
return -1;
1436014383

14361-
if (__is_pointer_value(false, reg1)) {
1436214384
if (!reg_not_null(reg1))
1436314385
return -1;
1436414386

1436514387
/* If pointer is valid tests against zero will fail so we can
1436614388
* use this to direct branch taken.
1436714389
*/
14390+
val = reg_const_value(reg2, is_jmp32);
1436814391
if (val != 0)
1436914392
return -1;
1437014393

@@ -14378,6 +14401,7 @@ static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg
1437814401
}
1437914402
}
1438014403

14404+
/* now deal with two scalars, but not necessarily constants */
1438114405
return is_scalar_branch_taken(reg1, reg2, opcode, is_jmp32);
1438214406
}
1438314407

0 commit comments

Comments
 (0)