Skip to content

Commit 5d5ea26

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 <[email protected]> Acked-by: Eduard Zingerman <[email protected]>
1 parent 91d2f93 commit 5d5ea26

File tree

1 file changed

+63
-40
lines changed

1 file changed

+63
-40
lines changed

kernel/bpf/verifier.c

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14187,82 +14187,104 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1418714187
u8 opcode, bool is_jmp32)
1418814188
{
1418914189
struct tnum t1 = is_jmp32 ? tnum_subreg(reg1->var_off) : reg1->var_off;
14190+
struct tnum t2 = is_jmp32 ? tnum_subreg(reg2->var_off) : reg2->var_off;
1419014191
u64 umin1 = is_jmp32 ? (u64)reg1->u32_min_value : reg1->umin_value;
1419114192
u64 umax1 = is_jmp32 ? (u64)reg1->u32_max_value : reg1->umax_value;
1419214193
s64 smin1 = is_jmp32 ? (s64)reg1->s32_min_value : reg1->smin_value;
1419314194
s64 smax1 = is_jmp32 ? (s64)reg1->s32_max_value : reg1->smax_value;
14194-
u64 uval = is_jmp32 ? (u32)tnum_subreg(reg2->var_off).value : reg2->var_off.value;
14195-
s64 sval = is_jmp32 ? (s32)uval : (s64)uval;
14195+
u64 umin2 = is_jmp32 ? (u64)reg2->u32_min_value : reg2->umin_value;
14196+
u64 umax2 = is_jmp32 ? (u64)reg2->u32_max_value : reg2->umax_value;
14197+
s64 smin2 = is_jmp32 ? (s64)reg2->s32_min_value : reg2->smin_value;
14198+
s64 smax2 = is_jmp32 ? (s64)reg2->s32_max_value : reg2->smax_value;
1419614199

1419714200
switch (opcode) {
1419814201
case BPF_JEQ:
14199-
if (tnum_is_const(t1))
14200-
return !!tnum_equals_const(t1, uval);
14201-
else if (uval < umin1 || uval > umax1)
14202+
/* constants, umin/umax and smin/smax checks would be
14203+
* redundant in this case because they all should match
14204+
*/
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 smin1 == smin2;
14212+
/* non-overlapping ranges */
14213+
if (umin1 > umax2 || umax1 < umin2)
1420214214
return 0;
14203-
else if (sval < smin1 || sval > smax1)
14215+
if (smin1 > smax2 || smax1 < smin2)
1420414216
return 0;
1420514217
break;
1420614218
case BPF_JNE:
14207-
if (tnum_is_const(t1))
14208-
return !tnum_equals_const(t1, uval);
14209-
else if (uval < umin1 || uval > umax1)
14219+
/* constants, umin/umax and smin/smax checks would be
14220+
* redundant in this case because they all should match
14221+
*/
14222+
if (tnum_is_const(t1) && tnum_is_const(t2))
14223+
return t1.value != t2.value;
14224+
/* non-overlapping ranges */
14225+
if (umin1 > umax2 || umax1 < umin2)
1421014226
return 1;
14211-
else if (sval < smin1 || sval > smax1)
14227+
if (smin1 > smax2 || smax1 < smin2)
1421214228
return 1;
1421314229
break;
1421414230
case BPF_JSET:
14215-
if ((~t1.mask & t1.value) & uval)
14231+
if (!is_reg_const(reg2, is_jmp32)) {
14232+
swap(reg1, reg2);
14233+
swap(t1, t2);
14234+
}
14235+
if (!is_reg_const(reg2, is_jmp32))
14236+
return -1;
14237+
if ((~t1.mask & t1.value) & t2.value)
1421614238
return 1;
14217-
if (!((t1.mask | t1.value) & uval))
14239+
if (!((t1.mask | t1.value) & t2.value))
1421814240
return 0;
1421914241
break;
1422014242
case BPF_JGT:
14221-
if (umin1 > uval )
14243+
if (umin1 > umax2)
1422214244
return 1;
14223-
else if (umax1 <= uval)
14245+
else if (umax1 <= umin2)
1422414246
return 0;
1422514247
break;
1422614248
case BPF_JSGT:
14227-
if (smin1 > sval)
14249+
if (smin1 > smax2)
1422814250
return 1;
14229-
else if (smax1 <= sval)
14251+
else if (smax1 <= smin2)
1423014252
return 0;
1423114253
break;
1423214254
case BPF_JLT:
14233-
if (umax1 < uval)
14255+
if (umax1 < umin2)
1423414256
return 1;
14235-
else if (umin1 >= uval)
14257+
else if (umin1 >= umax2)
1423614258
return 0;
1423714259
break;
1423814260
case BPF_JSLT:
14239-
if (smax1 < sval)
14261+
if (smax1 < smin2)
1424014262
return 1;
14241-
else if (smin1 >= sval)
14263+
else if (smin1 >= smax2)
1424214264
return 0;
1424314265
break;
1424414266
case BPF_JGE:
14245-
if (umin1 >= uval)
14267+
if (umin1 >= umax2)
1424614268
return 1;
14247-
else if (umax1 < uval)
14269+
else if (umax1 < umin2)
1424814270
return 0;
1424914271
break;
1425014272
case BPF_JSGE:
14251-
if (smin1 >= sval)
14273+
if (smin1 >= smax2)
1425214274
return 1;
14253-
else if (smax1 < sval)
14275+
else if (smax1 < smin2)
1425414276
return 0;
1425514277
break;
1425614278
case BPF_JLE:
14257-
if (umax1 <= uval)
14279+
if (umax1 <= umin2)
1425814280
return 1;
14259-
else if (umin1 > uval)
14281+
else if (umin1 > umax2)
1426014282
return 0;
1426114283
break;
1426214284
case BPF_JSLE:
14263-
if (smax1 <= sval)
14285+
if (smax1 <= smin2)
1426414286
return 1;
14265-
else if (smin1 > sval)
14287+
else if (smin1 > smax2)
1426614288
return 0;
1426714289
break;
1426814290
}
@@ -14341,28 +14363,28 @@ static int is_pkt_ptr_branch_taken(struct bpf_reg_state *dst_reg,
1434114363
static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2,
1434214364
u8 opcode, bool is_jmp32)
1434314365
{
14344-
u64 val;
14345-
1434614366
if (reg_is_pkt_pointer_any(reg1) && reg_is_pkt_pointer_any(reg2) && !is_jmp32)
1434714367
return is_pkt_ptr_branch_taken(reg1, reg2, opcode);
1434814368

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

14359-
if (__is_pointer_value(false, reg1)) {
1436014381
if (!reg_not_null(reg1))
1436114382
return -1;
1436214383

1436314384
/* If pointer is valid tests against zero will fail so we can
1436414385
* use this to direct branch taken.
1436514386
*/
14387+
val = reg_const_value(reg2, is_jmp32);
1436614388
if (val != 0)
1436714389
return -1;
1436814390

@@ -14376,6 +14398,7 @@ static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg
1437614398
}
1437714399
}
1437814400

14401+
/* now deal with two scalars, but not necessarily constants */
1437914402
return is_scalar_branch_taken(reg1, reg2, opcode, is_jmp32);
1438014403
}
1438114404

0 commit comments

Comments
 (0)