Skip to content

Commit e688c3d

Browse files
Alexei Starovoitovborkmann
Alexei Starovoitov
authored andcommitted
bpf: Fix register equivalence tracking.
The 64-bit JEQ/JNE handling in reg_set_min_max() was clearing reg->id in either true or false branch. In the case 'if (reg->id)' check was done on the other branch the counter part register would have reg->id == 0 when called into find_equal_scalars(). In such case the helper would incorrectly identify other registers with id == 0 as equivalent and propagate the state incorrectly. Fix it by preserving ID across reg_set_min_max(). In other words any kind of comparison operator on the scalar register should preserve its ID to recognize: r1 = r2 if (r1 == 20) { #1 here both r1 and r2 == 20 } else if (r2 < 20) { #2 here both r1 and r2 < 20 } The patch is addressing #1 case. The #2 was working correctly already. Fixes: 7574883 ("bpf: Propagate scalar ranges through register assignments.") Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Acked-by: John Fastabend <[email protected]> Tested-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent ccdf7fa commit e688c3d

File tree

2 files changed

+51
-13
lines changed

2 files changed

+51
-13
lines changed

kernel/bpf/verifier.c

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,14 +1010,9 @@ static const int caller_saved[CALLER_SAVED_REGS] = {
10101010
static void __mark_reg_not_init(const struct bpf_verifier_env *env,
10111011
struct bpf_reg_state *reg);
10121012

1013-
/* Mark the unknown part of a register (variable offset or scalar value) as
1014-
* known to have the value @imm.
1015-
*/
1016-
static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
1013+
/* This helper doesn't clear reg->id */
1014+
static void ___mark_reg_known(struct bpf_reg_state *reg, u64 imm)
10171015
{
1018-
/* Clear id, off, and union(map_ptr, range) */
1019-
memset(((u8 *)reg) + sizeof(reg->type), 0,
1020-
offsetof(struct bpf_reg_state, var_off) - sizeof(reg->type));
10211016
reg->var_off = tnum_const(imm);
10221017
reg->smin_value = (s64)imm;
10231018
reg->smax_value = (s64)imm;
@@ -1030,6 +1025,17 @@ static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
10301025
reg->u32_max_value = (u32)imm;
10311026
}
10321027

1028+
/* Mark the unknown part of a register (variable offset or scalar value) as
1029+
* known to have the value @imm.
1030+
*/
1031+
static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
1032+
{
1033+
/* Clear id, off, and union(map_ptr, range) */
1034+
memset(((u8 *)reg) + sizeof(reg->type), 0,
1035+
offsetof(struct bpf_reg_state, var_off) - sizeof(reg->type));
1036+
___mark_reg_known(reg, imm);
1037+
}
1038+
10331039
static void __mark_reg32_known(struct bpf_reg_state *reg, u64 imm)
10341040
{
10351041
reg->var_off = tnum_const_subreg(reg->var_off, imm);
@@ -7001,14 +7007,18 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg,
70017007
struct bpf_reg_state *reg =
70027008
opcode == BPF_JEQ ? true_reg : false_reg;
70037009

7004-
/* For BPF_JEQ, if this is false we know nothing Jon Snow, but
7005-
* if it is true we know the value for sure. Likewise for
7006-
* BPF_JNE.
7010+
/* JEQ/JNE comparison doesn't change the register equivalence.
7011+
* r1 = r2;
7012+
* if (r1 == 42) goto label;
7013+
* ...
7014+
* label: // here both r1 and r2 are known to be 42.
7015+
*
7016+
* Hence when marking register as known preserve it's ID.
70077017
*/
70087018
if (is_jmp32)
70097019
__mark_reg32_known(reg, val32);
70107020
else
7011-
__mark_reg_known(reg, val);
7021+
___mark_reg_known(reg, val);
70127022
break;
70137023
}
70147024
case BPF_JSET:
@@ -7551,7 +7561,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
75517561
reg_combine_min_max(&other_branch_regs[insn->src_reg],
75527562
&other_branch_regs[insn->dst_reg],
75537563
src_reg, dst_reg, opcode);
7554-
if (src_reg->id) {
7564+
if (src_reg->id &&
7565+
!WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
75557566
find_equal_scalars(this_branch, src_reg);
75567567
find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]);
75577568
}
@@ -7563,7 +7574,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
75637574
opcode, is_jmp32);
75647575
}
75657576

7566-
if (dst_reg->type == SCALAR_VALUE && dst_reg->id) {
7577+
if (dst_reg->type == SCALAR_VALUE && dst_reg->id &&
7578+
!WARN_ON_ONCE(dst_reg->id != other_branch_regs[insn->dst_reg].id)) {
75677579
find_equal_scalars(this_branch, dst_reg);
75687580
find_equal_scalars(other_branch, &other_branch_regs[insn->dst_reg]);
75697581
}

tools/testing/selftests/bpf/verifier/regalloc.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,29 @@
241241
.result = ACCEPT,
242242
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
243243
},
244+
{
245+
"regalloc, spill, JEQ",
246+
.insns = {
247+
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
248+
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
249+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
250+
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
251+
BPF_LD_MAP_FD(BPF_REG_1, 0),
252+
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
253+
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), /* spill r0 */
254+
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0),
255+
/* The verifier will walk the rest twice with r0 == 0 and r0 == map_value */
256+
BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32),
257+
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
258+
BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 20, 0),
259+
/* The verifier will walk the rest two more times with r0 == 20 and r0 == unknown */
260+
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -8), /* fill r3 with map_value */
261+
BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 1), /* skip ldx if map_value == NULL */
262+
/* Buggy verifier will think that r3 == 20 here */
263+
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0), /* read from map_value */
264+
BPF_EXIT_INSN(),
265+
},
266+
.fixup_map_hash_48b = { 4 },
267+
.result = ACCEPT,
268+
.prog_type = BPF_PROG_TYPE_TRACEPOINT,
269+
},

0 commit comments

Comments
 (0)