Skip to content

Commit f54c789

Browse files
borkmannAlexei Starovoitov
authored andcommitted
bpf: Fix precision tracking for unbounded scalars
Anatoly has been fuzzing with kBdysch harness and reported a hang in one of the outcomes. Upon closer analysis, it turns out that precise scalar value tracking is missing a few precision markings for unknown scalars: 0: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (b7) r0 = 0 1: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 1: (35) if r0 >= 0xf72e goto pc+0 --> only follow fallthrough 2: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 2: (35) if r0 >= 0x80fe0000 goto pc+0 --> only follow fallthrough 3: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 3: (14) w0 -= -536870912 4: R0_w=invP536870912 R1=ctx(id=0,off=0,imm=0) R10=fp0 4: (0f) r1 += r0 5: R0_w=invP536870912 R1_w=inv(id=0) R10=fp0 5: (55) if r1 != 0x104c1500 goto pc+0 --> push other branch for later analysis R0_w=invP536870912 R1_w=inv273421568 R10=fp0 6: R0_w=invP536870912 R1_w=inv273421568 R10=fp0 6: (b7) r0 = 0 7: R0=invP0 R1=inv273421568 R10=fp0 7: (76) if w1 s>= 0xffffff00 goto pc+3 --> only follow goto 11: R0=invP0 R1=inv273421568 R10=fp0 11: (95) exit 6: R0_w=invP536870912 R1_w=inv(id=0) R10=fp0 6: (b7) r0 = 0 propagating r0 7: safe processed 11 insns [...] In the analysis of the second path coming after the successful exit above, the path is being pruned at line 7. Pruning analysis found that both r0 are precise P0 and both R1 are non-precise scalars and given prior path with R1 as non-precise scalar succeeded, this one is therefore safe as well. However, problem is that given condition at insn 7 in the first run, we only followed goto and didn't push the other branch for later analysis, we've never walked the few insns in there and therefore dead-code sanitation rewrites it as goto pc-1, causing the hang depending on the skb address hitting these conditions. The issue is that R1 should have been marked as precise as well such that pruning enforces range check and conluded that new R1 is not in range of old R1. In insn 4, we mark R1 (skb) as unknown scalar via __mark_reg_unbounded() but not mark_reg_unbounded() and therefore regs->precise remains as false. Back in b5dc016 ("bpf: precise scalar_value tracking"), this was not the case since marking out of __mark_reg_unbounded() had this covered as well. Once in both are set as precise in 4 as they should have been, we conclude that given R1 was in prior fall-through path 0x104c1500 and now is completely unknown, the check at insn 7 concludes that we need to continue walking. Analysis after the fix: 0: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (b7) r0 = 0 1: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 1: (35) if r0 >= 0xf72e goto pc+0 2: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 2: (35) if r0 >= 0x80fe0000 goto pc+0 3: R0_w=invP0 R1=ctx(id=0,off=0,imm=0) R10=fp0 3: (14) w0 -= -536870912 4: R0_w=invP536870912 R1=ctx(id=0,off=0,imm=0) R10=fp0 4: (0f) r1 += r0 5: R0_w=invP536870912 R1_w=invP(id=0) R10=fp0 5: (55) if r1 != 0x104c1500 goto pc+0 R0_w=invP536870912 R1_w=invP273421568 R10=fp0 6: R0_w=invP536870912 R1_w=invP273421568 R10=fp0 6: (b7) r0 = 0 7: R0=invP0 R1=invP273421568 R10=fp0 7: (76) if w1 s>= 0xffffff00 goto pc+3 11: R0=invP0 R1=invP273421568 R10=fp0 11: (95) exit 6: R0_w=invP536870912 R1_w=invP(id=0) R10=fp0 6: (b7) r0 = 0 7: R0_w=invP0 R1_w=invP(id=0) R10=fp0 7: (76) if w1 s>= 0xffffff00 goto pc+3 R0_w=invP0 R1_w=invP(id=0) R10=fp0 8: R0_w=invP0 R1_w=invP(id=0) R10=fp0 8: (a5) if r0 < 0x2007002a goto pc+0 9: R0_w=invP0 R1_w=invP(id=0) R10=fp0 9: (57) r0 &= -16316416 10: R0_w=invP0 R1_w=invP(id=0) R10=fp0 10: (a6) if w0 < 0x1201 goto pc+0 11: R0_w=invP0 R1_w=invP(id=0) R10=fp0 11: (95) exit 11: R0=invP0 R1=invP(id=0) R10=fp0 11: (95) exit processed 16 insns [...] Fixes: 6754172 ("bpf: fix precision tracking in presence of bpf2bpf calls") Reported-by: Anatoly Trosinenko <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent c601747 commit f54c789

File tree

1 file changed

+22
-21
lines changed

1 file changed

+22
-21
lines changed

kernel/bpf/verifier.c

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,8 @@ static const int caller_saved[CALLER_SAVED_REGS] = {
907907
BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
908908
};
909909

910-
static void __mark_reg_not_init(struct bpf_reg_state *reg);
910+
static void __mark_reg_not_init(const struct bpf_verifier_env *env,
911+
struct bpf_reg_state *reg);
911912

912913
/* Mark the unknown part of a register (variable offset or scalar value) as
913914
* known to have the value @imm.
@@ -945,7 +946,7 @@ static void mark_reg_known_zero(struct bpf_verifier_env *env,
945946
verbose(env, "mark_reg_known_zero(regs, %u)\n", regno);
946947
/* Something bad happened, let's kill all regs */
947948
for (regno = 0; regno < MAX_BPF_REG; regno++)
948-
__mark_reg_not_init(regs + regno);
949+
__mark_reg_not_init(env, regs + regno);
949950
return;
950951
}
951952
__mark_reg_known_zero(regs + regno);
@@ -1054,7 +1055,8 @@ static void __mark_reg_unbounded(struct bpf_reg_state *reg)
10541055
}
10551056

10561057
/* Mark a register as having a completely unknown (scalar) value. */
1057-
static void __mark_reg_unknown(struct bpf_reg_state *reg)
1058+
static void __mark_reg_unknown(const struct bpf_verifier_env *env,
1059+
struct bpf_reg_state *reg)
10581060
{
10591061
/*
10601062
* Clear type, id, off, and union(map_ptr, range) and
@@ -1064,6 +1066,8 @@ static void __mark_reg_unknown(struct bpf_reg_state *reg)
10641066
reg->type = SCALAR_VALUE;
10651067
reg->var_off = tnum_unknown;
10661068
reg->frameno = 0;
1069+
reg->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks ?
1070+
true : false;
10671071
__mark_reg_unbounded(reg);
10681072
}
10691073

@@ -1074,19 +1078,16 @@ static void mark_reg_unknown(struct bpf_verifier_env *env,
10741078
verbose(env, "mark_reg_unknown(regs, %u)\n", regno);
10751079
/* Something bad happened, let's kill all regs except FP */
10761080
for (regno = 0; regno < BPF_REG_FP; regno++)
1077-
__mark_reg_not_init(regs + regno);
1081+
__mark_reg_not_init(env, regs + regno);
10781082
return;
10791083
}
1080-
regs += regno;
1081-
__mark_reg_unknown(regs);
1082-
/* constant backtracking is enabled for root without bpf2bpf calls */
1083-
regs->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks ?
1084-
true : false;
1084+
__mark_reg_unknown(env, regs + regno);
10851085
}
10861086

1087-
static void __mark_reg_not_init(struct bpf_reg_state *reg)
1087+
static void __mark_reg_not_init(const struct bpf_verifier_env *env,
1088+
struct bpf_reg_state *reg)
10881089
{
1089-
__mark_reg_unknown(reg);
1090+
__mark_reg_unknown(env, reg);
10901091
reg->type = NOT_INIT;
10911092
}
10921093

@@ -1097,10 +1098,10 @@ static void mark_reg_not_init(struct bpf_verifier_env *env,
10971098
verbose(env, "mark_reg_not_init(regs, %u)\n", regno);
10981099
/* Something bad happened, let's kill all regs except FP */
10991100
for (regno = 0; regno < BPF_REG_FP; regno++)
1100-
__mark_reg_not_init(regs + regno);
1101+
__mark_reg_not_init(env, regs + regno);
11011102
return;
11021103
}
1103-
__mark_reg_not_init(regs + regno);
1104+
__mark_reg_not_init(env, regs + regno);
11041105
}
11051106

11061107
#define DEF_NOT_SUBREG (0)
@@ -3234,7 +3235,7 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno,
32343235
}
32353236
if (state->stack[spi].slot_type[0] == STACK_SPILL &&
32363237
state->stack[spi].spilled_ptr.type == SCALAR_VALUE) {
3237-
__mark_reg_unknown(&state->stack[spi].spilled_ptr);
3238+
__mark_reg_unknown(env, &state->stack[spi].spilled_ptr);
32383239
for (j = 0; j < BPF_REG_SIZE; j++)
32393240
state->stack[spi].slot_type[j] = STACK_MISC;
32403241
goto mark;
@@ -3892,7 +3893,7 @@ static void __clear_all_pkt_pointers(struct bpf_verifier_env *env,
38923893
if (!reg)
38933894
continue;
38943895
if (reg_is_pkt_pointer_any(reg))
3895-
__mark_reg_unknown(reg);
3896+
__mark_reg_unknown(env, reg);
38963897
}
38973898
}
38983899

@@ -3920,7 +3921,7 @@ static void release_reg_references(struct bpf_verifier_env *env,
39203921
if (!reg)
39213922
continue;
39223923
if (reg->ref_obj_id == ref_obj_id)
3923-
__mark_reg_unknown(reg);
3924+
__mark_reg_unknown(env, reg);
39243925
}
39253926
}
39263927

@@ -4582,7 +4583,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
45824583
/* Taint dst register if offset had invalid bounds derived from
45834584
* e.g. dead branches.
45844585
*/
4585-
__mark_reg_unknown(dst_reg);
4586+
__mark_reg_unknown(env, dst_reg);
45864587
return 0;
45874588
}
45884589

@@ -4834,13 +4835,13 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
48344835
/* Taint dst register if offset had invalid bounds derived from
48354836
* e.g. dead branches.
48364837
*/
4837-
__mark_reg_unknown(dst_reg);
4838+
__mark_reg_unknown(env, dst_reg);
48384839
return 0;
48394840
}
48404841

48414842
if (!src_known &&
48424843
opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) {
4843-
__mark_reg_unknown(dst_reg);
4844+
__mark_reg_unknown(env, dst_reg);
48444845
return 0;
48454846
}
48464847

@@ -6982,15 +6983,15 @@ static void clean_func_state(struct bpf_verifier_env *env,
69826983
/* since the register is unused, clear its state
69836984
* to make further comparison simpler
69846985
*/
6985-
__mark_reg_not_init(&st->regs[i]);
6986+
__mark_reg_not_init(env, &st->regs[i]);
69866987
}
69876988

69886989
for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
69896990
live = st->stack[i].spilled_ptr.live;
69906991
/* liveness must not touch this stack slot anymore */
69916992
st->stack[i].spilled_ptr.live |= REG_LIVE_DONE;
69926993
if (!(live & REG_LIVE_READ)) {
6993-
__mark_reg_not_init(&st->stack[i].spilled_ptr);
6994+
__mark_reg_not_init(env, &st->stack[i].spilled_ptr);
69946995
for (j = 0; j < BPF_REG_SIZE; j++)
69956996
st->stack[i].slot_type[j] = STACK_INVALID;
69966997
}

0 commit comments

Comments
 (0)