Skip to content

Commit 4621202

Browse files
anakryikoAlexei Starovoitov
authored and
Alexei Starovoitov
committed
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 811476e commit 4621202

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
@@ -14400,32 +14400,50 @@ static int is_branch_taken(struct bpf_reg_state *reg1, struct bpf_reg_state *reg
1440014400
return is_scalar_branch_taken(reg1, reg2, opcode, is_jmp32);
1440114401
}
1440214402

14403-
/* Adjusts the register min/max values in the case that the dst_reg is the
14404-
* variable register that we are working on, and src_reg is a constant or we're
14405-
* simply doing a BPF_K check.
14406-
* In JEQ/JNE cases we also adjust the var_off values.
14403+
/* Adjusts the register min/max values in the case that the dst_reg and
14404+
* src_reg are both SCALAR_VALUE registers (or we are simply doing a BPF_K
14405+
* check, in which case we havea fake SCALAR_VALUE representing insn->imm).
14406+
* Technically we can do similar adjustments for pointers to the same object,
14407+
* but we don't support that right now.
1440714408
*/
1440814409
static void reg_set_min_max(struct bpf_reg_state *true_reg1,
14410+
struct bpf_reg_state *true_reg2,
1440914411
struct bpf_reg_state *false_reg1,
14410-
u64 uval, u32 uval32,
14412+
struct bpf_reg_state *false_reg2,
1441114413
u8 opcode, bool is_jmp32)
1441214414
{
14413-
struct tnum false_32off = tnum_subreg(false_reg1->var_off);
14414-
struct tnum false_64off = false_reg1->var_off;
14415-
struct tnum true_32off = tnum_subreg(true_reg1->var_off);
14416-
struct tnum true_64off = true_reg1->var_off;
14417-
s64 sval = (s64)uval;
14418-
s32 sval32 = (s32)uval32;
14419-
14420-
/* If the dst_reg is a pointer, we can't learn anything about its
14421-
* variable offset from the compare (unless src_reg were a pointer into
14422-
* the same object, but we don't bother with that.
14423-
* Since false_reg1 and true_reg1 have the same type by construction, we
14424-
* only need to check one of them for pointerness.
14415+
struct tnum false_32off, false_64off;
14416+
struct tnum true_32off, true_64off;
14417+
u64 uval;
14418+
u32 uval32;
14419+
s64 sval;
14420+
s32 sval32;
14421+
14422+
/* If either register is a pointer, we can't learn anything about its
14423+
* variable offset from the compare (unless they were a pointer into
14424+
* the same object, but we don't bother with that).
1442514425
*/
14426-
if (__is_pointer_value(false, false_reg1))
14426+
if (false_reg1->type != SCALAR_VALUE || false_reg2->type != SCALAR_VALUE)
14427+
return;
14428+
14429+
/* we expect right-hand registers (src ones) to be constants, for now */
14430+
if (!is_reg_const(false_reg2, is_jmp32)) {
14431+
opcode = flip_opcode(opcode);
14432+
swap(true_reg1, true_reg2);
14433+
swap(false_reg1, false_reg2);
14434+
}
14435+
if (!is_reg_const(false_reg2, is_jmp32))
1442714436
return;
1442814437

14438+
false_32off = tnum_subreg(false_reg1->var_off);
14439+
false_64off = false_reg1->var_off;
14440+
true_32off = tnum_subreg(true_reg1->var_off);
14441+
true_64off = true_reg1->var_off;
14442+
uval = false_reg2->var_off.value;
14443+
uval32 = (u32)tnum_subreg(false_reg2->var_off).value;
14444+
sval = (s64)uval;
14445+
sval32 = (s32)uval32;
14446+
1442914447
switch (opcode) {
1443014448
/* JEQ/JNE comparison doesn't change the register equivalence.
1443114449
*
@@ -14562,22 +14580,6 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg1,
1456214580
}
1456314581
}
1456414582

14565-
/* Same as above, but for the case that dst_reg holds a constant and src_reg is
14566-
* the variable reg.
14567-
*/
14568-
static void reg_set_min_max_inv(struct bpf_reg_state *true_reg,
14569-
struct bpf_reg_state *false_reg,
14570-
u64 uval, u32 uval32,
14571-
u8 opcode, bool is_jmp32)
14572-
{
14573-
opcode = flip_opcode(opcode);
14574-
/* This uses zero as "not present in table"; luckily the zero opcode,
14575-
* BPF_JA, can't get here.
14576-
*/
14577-
if (opcode)
14578-
reg_set_min_max(true_reg, false_reg, uval, uval32, opcode, is_jmp32);
14579-
}
14580-
1458114583
/* Regs are known to be equal, so intersect their min/max/var_off */
1458214584
static void __reg_combine_min_max(struct bpf_reg_state *src_reg,
1458314585
struct bpf_reg_state *dst_reg)
@@ -14902,53 +14904,32 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1490214904
return -EFAULT;
1490314905
other_branch_regs = other_branch->frame[other_branch->curframe]->regs;
1490414906

14905-
/* detect if we are comparing against a constant value so we can adjust
14906-
* our min/max values for our dst register.
14907-
* this is only legit if both are scalars (or pointers to the same
14908-
* object, I suppose, see the PTR_MAYBE_NULL related if block below),
14909-
* because otherwise the different base pointers mean the offsets aren't
14910-
* comparable.
14911-
*/
1491214907
if (BPF_SRC(insn->code) == BPF_X) {
14913-
struct bpf_reg_state *src_reg = &regs[insn->src_reg];
14908+
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14909+
&other_branch_regs[insn->src_reg],
14910+
dst_reg, src_reg, opcode, is_jmp32);
1491414911

1491514912
if (dst_reg->type == SCALAR_VALUE &&
14916-
src_reg->type == SCALAR_VALUE) {
14917-
if (tnum_is_const(src_reg->var_off) ||
14918-
(is_jmp32 &&
14919-
tnum_is_const(tnum_subreg(src_reg->var_off))))
14920-
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14921-
dst_reg,
14922-
src_reg->var_off.value,
14923-
tnum_subreg(src_reg->var_off).value,
14924-
opcode, is_jmp32);
14925-
else if (tnum_is_const(dst_reg->var_off) ||
14926-
(is_jmp32 &&
14927-
tnum_is_const(tnum_subreg(dst_reg->var_off))))
14928-
reg_set_min_max_inv(&other_branch_regs[insn->src_reg],
14929-
src_reg,
14930-
dst_reg->var_off.value,
14931-
tnum_subreg(dst_reg->var_off).value,
14932-
opcode, is_jmp32);
14933-
else if (!is_jmp32 &&
14934-
(opcode == BPF_JEQ || opcode == BPF_JNE))
14935-
/* Comparing for equality, we can combine knowledge */
14936-
reg_combine_min_max(&other_branch_regs[insn->src_reg],
14937-
&other_branch_regs[insn->dst_reg],
14938-
src_reg, dst_reg, opcode);
14939-
if (src_reg->id &&
14940-
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
14941-
find_equal_scalars(this_branch, src_reg);
14942-
find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]);
14943-
}
14944-
14945-
}
14946-
} else if (dst_reg->type == SCALAR_VALUE) {
14913+
src_reg->type == SCALAR_VALUE &&
14914+
!is_jmp32 && (opcode == BPF_JEQ || opcode == BPF_JNE)) {
14915+
/* Comparing for equality, we can combine knowledge */
14916+
reg_combine_min_max(&other_branch_regs[insn->src_reg],
14917+
&other_branch_regs[insn->dst_reg],
14918+
src_reg, dst_reg, opcode);
14919+
}
14920+
} else /* BPF_SRC(insn->code) == BPF_K */ {
1494714921
reg_set_min_max(&other_branch_regs[insn->dst_reg],
14948-
dst_reg, insn->imm, (u32)insn->imm,
14949-
opcode, is_jmp32);
14922+
src_reg /* fake one */,
14923+
dst_reg, src_reg /* same fake one */,
14924+
opcode, is_jmp32);
1495014925
}
1495114926

14927+
if (BPF_SRC(insn->code) == BPF_X &&
14928+
src_reg->type == SCALAR_VALUE && src_reg->id &&
14929+
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
14930+
find_equal_scalars(this_branch, src_reg);
14931+
find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]);
14932+
}
1495214933
if (dst_reg->type == SCALAR_VALUE && dst_reg->id &&
1495314934
!WARN_ON_ONCE(dst_reg->id != other_branch_regs[insn->dst_reg].id)) {
1495414935
find_equal_scalars(this_branch, dst_reg);

0 commit comments

Comments
 (0)