Skip to content

Commit 9a14d62

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: generalize reg_set_min_max() to handle two sets of two registers
Change reg_set_min_max() to take FALSE/TRUE sets of two registers each, instead of assuming that we are always comparing to a constant. For now we still assume that right-hand side registers are constants (and make sure that's the case by swapping src/dst regs, if necessary), but subsequent patches will remove this limitation. reg_set_min_max() is now called unconditionally for any register comparison, so that might include pointer vs pointer. This makes it consistent with is_branch_taken() generality. But we currently only support adjustments based on SCALAR vs SCALAR comparisons, so reg_set_min_max() has to guard itself againts pointers. Taking two by two registers allows to further unify and simplify check_cond_jmp_op() logic. We utilize fake register for BPF_K conditional jump case, just like with is_branch_taken() part. Acked-by: Eduard Zingerman <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 4c61728 commit 9a14d62

File tree

1 file changed

+56
-75
lines changed

1 file changed

+56
-75
lines changed

kernel/bpf/verifier.c

Lines changed: 56 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -14379,32 +14379,50 @@ static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg
1437914379
return is_scalar_branch_taken(reg1, reg2, opcode, is_jmp32);
1438014380
}
1438114381

14382-
/* Adjusts the register min/max values in the case that the dst_reg is the
14383-
* variable register that we are working on, and src_reg is a constant or we're
14384-
* simply doing a BPF_K check.
14385-
* In JEQ/JNE cases we also adjust the var_off values.
14382+
/* Adjusts the register min/max values in the case that the dst_reg and
14383+
* src_reg are both SCALAR_VALUE registers (or we are simply doing a BPF_K
14384+
* check, in which case we havea fake SCALAR_VALUE representing insn->imm).
14385+
* Technically we can do similar adjustments for pointers to the same object,
14386+
* but we don't support that right now.
1438614387
*/
1438714388
static void reg_set_min_max(struct bpf_reg_state *true_reg1,
14389+
struct bpf_reg_state *true_reg2,
1438814390
struct bpf_reg_state *false_reg1,
14389-
u64 uval, u32 uval32,
14391+
struct bpf_reg_state *false_reg2,
1439014392
u8 opcode, bool is_jmp32)
1439114393
{
14392-
struct tnum false_32off = tnum_subreg(false_reg1->var_off);
14393-
struct tnum false_64off = false_reg1->var_off;
14394-
struct tnum true_32off = tnum_subreg(true_reg1->var_off);
14395-
struct tnum true_64off = true_reg1->var_off;
14396-
s64 sval = (s64)uval;
14397-
s32 sval32 = (s32)uval32;
14398-
14399-
/* If the dst_reg is a pointer, we can't learn anything about its
14400-
* variable offset from the compare (unless src_reg were a pointer into
14401-
* the same object, but we don't bother with that.
14402-
* Since false_reg1 and true_reg1 have the same type by construction, we
14403-
* only need to check one of them for pointerness.
14394+
struct tnum false_32off, false_64off;
14395+
struct tnum true_32off, true_64off;
14396+
u64 uval;
14397+
u32 uval32;
14398+
s64 sval;
14399+
s32 sval32;
14400+
14401+
/* If either register is a pointer, we can't learn anything about its
14402+
* variable offset from the compare (unless they were a pointer into
14403+
* the same object, but we don't bother with that).
1440414404
*/
14405-
if (__is_pointer_value(false, false_reg1))
14405+
if (false_reg1->type != SCALAR_VALUE || false_reg2->type != SCALAR_VALUE)
14406+
return;
14407+
14408+
/* we expect right-hand registers (src ones) to be constants, for now */
14409+
if (!is_reg_const(false_reg2, is_jmp32)) {
14410+
opcode = flip_opcode(opcode);
14411+
swap(true_reg1, true_reg2);
14412+
swap(false_reg1, false_reg2);
14413+
}
14414+
if (!is_reg_const(false_reg2, is_jmp32))
1440614415
return;
1440714416

14417+
false_32off = tnum_subreg(false_reg1->var_off);
14418+
false_64off = false_reg1->var_off;
14419+
true_32off = tnum_subreg(true_reg1->var_off);
14420+
true_64off = true_reg1->var_off;
14421+
uval = false_reg2->var_off.value;
14422+
uval32 = (u32)tnum_subreg(false_reg2->var_off).value;
14423+
sval = (s64)uval;
14424+
sval32 = (s32)uval32;
14425+
1440814426
switch (opcode) {
1440914427
/* JEQ/JNE comparison doesn't change the register equivalence.
1441014428
*
@@ -14541,22 +14559,6 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg1,
1454114559
}
1454214560
}
1454314561

14544-
/* Same as above, but for the case that dst_reg holds a constant and src_reg is
14545-
* the variable reg.
14546-
*/
14547-
static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
14548-
struct bpf_reg_state *false_reg,
14549-
u64 uval, u32 uval32,
14550-
u8 opcode, bool is_jmp32)
14551-
{
14552-
opcode = flip_opcode(opcode);
14553-
/* This uses zero as "not present in table"; luckily the zero opcode,
14554-
* BPF_JA, can't get here.
14555-
*/
14556-
if (opcode)
14557-
reg_set_min_max(true_reg, false_reg, uval, uval32, opcode, is_jmp32);
14558-
}
14559-
1456014562
/* Regs are known to be equal, so intersect their min/max/var_off */
1456114563
static void __reg_combine_min_max(struct bpf_reg_state *src_reg,
1456214564
struct bpf_reg_state *dst_reg)
@@ -14881,53 +14883,32 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1488114883
return -EFAULT;
1488214884
other_branch_regs = other_branch->frame[other_branch->curframe]->regs;
1488314885

14884-
/* detect if we are comparing against a constant value so we can adjust
14885-
* our min/max values for our dst register.
14886-
* this is only legit if both are scalars (or pointers to the same
14887-
* object, I suppose, see the PTR_MAYBE_NULL related if block below),
14888-
* because otherwise the different base pointers mean the offsets aren't
14889-
* comparable.
14890-
*/
1489114886
if (BPF_SRC(insn->code) == BPF_X) {
14892-
struct bpf_reg_state *src_reg = &regs[insn->src_reg];
14887+
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14888+
&other_branch_regs[insn->src_reg],
14889+
dst_reg, src_reg, opcode, is_jmp32);
1489314890

1489414891
if (dst_reg->type == SCALAR_VALUE &&
14895-
src_reg->type == SCALAR_VALUE) {
14896-
if (tnum_is_const(src_reg->var_off) ||
14897-
(is_jmp32 &&
14898-
tnum_is_const(tnum_subreg(src_reg->var_off))))
14899-
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14900-
dst_reg,
14901-
src_reg->var_off.value,
14902-
tnum_subreg(src_reg->var_off).value,
14903-
opcode, is_jmp32);
14904-
else if (tnum_is_const(dst_reg->var_off) ||
14905-
(is_jmp32 &&
14906-
tnum_is_const(tnum_subreg(dst_reg->var_off))))
14907-
reg_set_min_max_inv(&other_branch_regs[insn->src_reg],
14908-
src_reg,
14909-
dst_reg->var_off.value,
14910-
tnum_subreg(dst_reg->var_off).value,
14911-
opcode, is_jmp32);
14912-
else if (!is_jmp32 &&
14913-
(opcode == BPF_JEQ || opcode == BPF_JNE))
14914-
/* Comparing for equality, we can combine knowledge */
14915-
reg_combine_min_max(&other_branch_regs[insn->src_reg],
14916-
&other_branch_regs[insn->dst_reg],
14917-
src_reg, dst_reg, opcode);
14918-
if (src_reg->id &&
14919-
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
14920-
find_equal_scalars(this_branch, src_reg);
14921-
find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]);
14922-
}
14923-
14924-
}
14925-
} else if (dst_reg->type == SCALAR_VALUE) {
14892+
src_reg->type == SCALAR_VALUE &&
14893+
!is_jmp32 && (opcode == BPF_JEQ || opcode == BPF_JNE)) {
14894+
/* Comparing for equality, we can combine knowledge */
14895+
reg_combine_min_max(&other_branch_regs[insn->src_reg],
14896+
&other_branch_regs[insn->dst_reg],
14897+
src_reg, dst_reg, opcode);
14898+
}
14899+
} else /* BPF_SRC(insn->code) == BPF_K */ {
1492614900
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14927-
dst_reg, insn->imm, (u32)insn->imm,
14928-
opcode, is_jmp32);
14901+
src_reg /* fake one */,
14902+
dst_reg, src_reg /* same fake one */,
14903+
opcode, is_jmp32);
1492914904
}
1493014905

14906+
if (BPF_SRC(insn->code) == BPF_X &&
14907+
src_reg->type == SCALAR_VALUE && src_reg->id &&
14908+
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
14909+
find_equal_scalars(this_branch, src_reg);
14910+
find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]);
14911+
}
1493114912
if (dst_reg->type == SCALAR_VALUE && dst_reg->id &&
1493214913
!WARN_ON_ONCE(dst_reg->id != other_branch_regs[insn->dst_reg].id)) {
1493314914
find_equal_scalars(this_branch, dst_reg);

0 commit comments

Comments
 (0)