Skip to content

Commit 74a12ef

Browse files
anakryikoKernel Patches Daemon
authored and
Kernel Patches Daemon
committed
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. Acked-by: Eduard Zingerman <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]>
1 parent 72c6d6e commit 74a12ef

File tree

1 file changed

+58
-40
lines changed

1 file changed

+58
-40
lines changed

kernel/bpf/verifier.c

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14261,82 +14261,99 @@ static int is_scalar_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_sta
1426114261
u8 opcode, bool is_jmp32)
1426214262
{
1426314263
struct tnum t1 = is_jmp32 ? tnum_subreg(reg1->var_off) : reg1->var_off;
14264+
struct tnum t2 = is_jmp32 ? tnum_subreg(reg2->var_off) : reg2->var_off;
1426414265
u64 umin1 = is_jmp32 ? (u64)reg1->u32_min_value : reg1->umin_value;
1426514266
u64 umax1 = is_jmp32 ? (u64)reg1->u32_max_value : reg1->umax_value;
1426614267
s64 smin1 = is_jmp32 ? (s64)reg1->s32_min_value : reg1->smin_value;
1426714268
s64 smax1 = is_jmp32 ? (s64)reg1->s32_max_value : reg1->smax_value;
14268-
u64 uval = is_jmp32 ? (u32)tnum_subreg(reg2->var_off).value : reg2->var_off.value;
14269-
s64 sval = is_jmp32 ? (s32)uval : (s64)uval;
14269+
u64 umin2 = is_jmp32 ? (u64)reg2->u32_min_value : reg2->umin_value;
14270+
u64 umax2 = is_jmp32 ? (u64)reg2->u32_max_value : reg2->umax_value;
14271+
s64 smin2 = is_jmp32 ? (s64)reg2->s32_min_value : reg2->smin_value;
14272+
s64 smax2 = is_jmp32 ? (s64)reg2->s32_max_value : reg2->smax_value;
1427014273

1427114274
switch (opcode) {
1427214275
case BPF_JEQ:
14273-
if (tnum_is_const(t1))
14274-
return !!tnum_equals_const(t1, uval);
14275-
else if (uval < umin1 || uval > umax1)
14276+
/* constants, umin/umax and smin/smax checks would be
14277+
* redundant in this case because they all should match
14278+
*/
14279+
if (tnum_is_const(t1) && tnum_is_const(t2))
14280+
return t1.value == t2.value;
14281+
/* non-overlapping ranges */
14282+
if (umin1 > umax2 || umax1 < umin2)
1427614283
return 0;
14277-
else if (sval < smin1 || sval > smax1)
14284+
if (smin1 > smax2 || smax1 < smin2)
1427814285
return 0;
1427914286
break;
1428014287
case BPF_JNE:
14281-
if (tnum_is_const(t1))
14282-
return !tnum_equals_const(t1, uval);
14283-
else if (uval < umin1 || uval > umax1)
14288+
/* constants, umin/umax and smin/smax checks would be
14289+
* redundant in this case because they all should match
14290+
*/
14291+
if (tnum_is_const(t1) && tnum_is_const(t2))
14292+
return t1.value != t2.value;
14293+
/* non-overlapping ranges */
14294+
if (umin1 > umax2 || umax1 < umin2)
1428414295
return 1;
14285-
else if (sval < smin1 || sval > smax1)
14296+
if (smin1 > smax2 || smax1 < smin2)
1428614297
return 1;
1428714298
break;
1428814299
case BPF_JSET:
14289-
if ((~t1.mask & t1.value) & uval)
14300+
if (!is_reg_const(reg2, is_jmp32)) {
14301+
swap(reg1, reg2);
14302+
swap(t1, t2);
14303+
}
14304+
if (!is_reg_const(reg2, is_jmp32))
14305+
return -1;
14306+
if ((~t1.mask & t1.value) & t2.value)
1429014307
return 1;
14291-
if (!((t1.mask | t1.value) & uval))
14308+
if (!((t1.mask | t1.value) & t2.value))
1429214309
return 0;
1429314310
break;
1429414311
case BPF_JGT:
14295-
if (umin1 > uval )
14312+
if (umin1 > umax2)
1429614313
return 1;
14297-
else if (umax1 <= uval)
14314+
else if (umax1 <= umin2)
1429814315
return 0;
1429914316
break;
1430014317
case BPF_JSGT:
14301-
if (smin1 > sval)
14318+
if (smin1 > smax2)
1430214319
return 1;
14303-
else if (smax1 <= sval)
14320+
else if (smax1 <= smin2)
1430414321
return 0;
1430514322
break;
1430614323
case BPF_JLT:
14307-
if (umax1 < uval)
14324+
if (umax1 < umin2)
1430814325
return 1;
14309-
else if (umin1 >= uval)
14326+
else if (umin1 >= umax2)
1431014327
return 0;
1431114328
break;
1431214329
case BPF_JSLT:
14313-
if (smax1 < sval)
14330+
if (smax1 < smin2)
1431414331
return 1;
14315-
else if (smin1 >= sval)
14332+
else if (smin1 >= smax2)
1431614333
return 0;
1431714334
break;
1431814335
case BPF_JGE:
14319-
if (umin1 >= uval)
14336+
if (umin1 >= umax2)
1432014337
return 1;
14321-
else if (umax1 < uval)
14338+
else if (umax1 < umin2)
1432214339
return 0;
1432314340
break;
1432414341
case BPF_JSGE:
14325-
if (smin1 >= sval)
14342+
if (smin1 >= smax2)
1432614343
return 1;
14327-
else if (smax1 < sval)
14344+
else if (smax1 < smin2)
1432814345
return 0;
1432914346
break;
1433014347
case BPF_JLE:
14331-
if (umax1 <= uval)
14348+
if (umax1 <= umin2)
1433214349
return 1;
14333-
else if (umin1 > uval)
14350+
else if (umin1 > umax2)
1433414351
return 0;
1433514352
break;
1433614353
case BPF_JSLE:
14337-
if (smax1 <= sval)
14354+
if (smax1 <= smin2)
1433814355
return 1;
14339-
else if (smin1 > sval)
14356+
else if (smin1 > smax2)
1434014357
return 0;
1434114358
break;
1434214359
}
@@ -14415,28 +14432,28 @@ static int is_pkt_ptr_branch_taken(struct bpf_reg_state *dst_reg,
1441514432
static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg2,
1441614433
u8 opcode, bool is_jmp32)
1441714434
{
14418-
u64 val;
14419-
1442014435
if (reg_is_pkt_pointer_any(reg1) && reg_is_pkt_pointer_any(reg2) && !is_jmp32)
1442114436
return is_pkt_ptr_branch_taken(reg1, reg2, opcode);
1442214437

14423-
/* try to make sure reg2 is a constant SCALAR_VALUE */
14424-
if (!is_reg_const(reg2, is_jmp32)) {
14425-
opcode = flip_opcode(opcode);
14426-
swap(reg1, reg2);
14427-
}
14428-
/* for now we expect reg2 to be a constant to make any useful decisions */
14429-
if (!is_reg_const(reg2, is_jmp32))
14430-
return -1;
14431-
val = reg_const_value(reg2, is_jmp32);
14438+
if (__is_pointer_value(false, reg1) || __is_pointer_value(false, reg2)) {
14439+
u64 val;
14440+
14441+
/* arrange that reg2 is a scalar, and reg1 is a pointer */
14442+
if (!is_reg_const(reg2, is_jmp32)) {
14443+
opcode = flip_opcode(opcode);
14444+
swap(reg1, reg2);
14445+
}
14446+
/* and ensure that reg2 is a constant */
14447+
if (!is_reg_const(reg2, is_jmp32))
14448+
return -1;
1443214449

14433-
if (__is_pointer_value(false, reg1)) {
1443414450
if (!reg_not_null(reg1))
1443514451
return -1;
1443614452

1443714453
/* If pointer is valid tests against zero will fail so we can
1443814454
* use this to direct branch taken.
1443914455
*/
14456+
val = reg_const_value(reg2, is_jmp32);
1444014457
if (val != 0)
1444114458
return -1;
1444214459

@@ -14450,6 +14467,7 @@ static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg
1445014467
}
1445114468
}
1445214469

14470+
/* now deal with two scalars, but not necessarily constants */
1445314471
return is_scalar_branch_taken(reg1, reg2, opcode, is_jmp32);
1445414472
}
1445514473

0 commit comments

Comments
 (0)