diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 7baeb831281f..8852c173c53e 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -1900,6 +1900,7 @@ bpf_global_subprog_stack_access_bytes(struct bpf_verifier_env *env, int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env); bool bpf_same_memory_origin(const struct bpf_reg_state *reg_a, const struct bpf_reg_state *reg_b); +void bpf_trim_pkt_range(struct bpf_reg_state *reg, s64 smax); int bpf_set_reg_range(struct bpf_verifier_env *env, struct bpf_reg_state *reg, struct cnum64 r64, struct cnum32 r32, u16 base, u16 step); bool bpf_reg_union_compatible(const struct bpf_reg_state *a, const struct bpf_reg_state *b); diff --git a/kernel/bpf/scev.c b/kernel/bpf/scev.c index 7b82fd79f9cc..1123a14e7389 100644 --- a/kernel/bpf/scev.c +++ b/kernel/bpf/scev.c @@ -2762,6 +2762,7 @@ static int union_any_reg(struct bpf_verifier_env *env, struct bpf_func_state *lo const struct bpf_reg_state *leaf_reg; struct scev *scev = env->scev; u32 l, r, leaf, order; + s64 smax = reg_smax(acc); s64 imm; int err; @@ -2785,10 +2786,13 @@ static int union_any_reg(struct bpf_verifier_env *env, struct bpf_func_state *lo verifier_bug(env, "scev ANY union has an unsupported leaf"); return -EFAULT; } + smax = max(smax, reg_smax(leaf_reg)); err = bpf_reg_union(env, acc, leaf_reg); if (err) return err; } + /* All leaves have the same range. It goes with the largest offset of all leaves. */ + bpf_trim_pkt_range(acc, smax); return 0; } @@ -2837,6 +2841,9 @@ int bpf_widen_scev_regs(struct bpf_verifier_env *env, struct bpf_verifier_state bpf_log(log, "\n"); } scratch_widened_reg_id(env, reg); + /* "at pkt_end" and "beyond pkt_end" are not valid in all iterations */ + if (type_is_pkt_pointer(reg->type)) + reg->range = max(reg->range, 0); err = bpf_set_reg_range(env, reg, bounds.r64, bounds.r32, bounds.base, bounds.step); if (err) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 26a5ee0708a3..a90bc6c4033a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -17736,6 +17736,18 @@ bool bpf_same_memory_origin(const struct bpf_reg_state *reg_a, } } +/* + * 'range - smax' bytes from a packet pointer are in the packet. reg_bounds_sync() can make smax + * smaller than @smax: with a step of 8, or with 3 zero bits in var_off, 58 becomes 56. Make the + * range smaller by the same number, so that the number of bytes does not grow. umax is not + * sufficient: it is U64_MAX when the offset can be negative. + */ +void bpf_trim_pkt_range(struct bpf_reg_state *reg, s64 smax) +{ + if (type_is_pkt_pointer(reg->type) && reg->range > 0) + reg->range = max_t(s64, reg->range - (smax - reg_smax(reg)), 0); +} + int bpf_set_reg_range(struct bpf_verifier_env *env, struct bpf_reg_state *reg, struct cnum64 r64, struct cnum32 r32, u16 base, u16 step) { @@ -17745,6 +17757,7 @@ int bpf_set_reg_range(struct bpf_verifier_env *env, struct bpf_reg_state *reg, reg->base = base; reg->var_off = tnum_unknown; reg_bounds_sync(reg); /* this should infer the tnum alignment */ + bpf_trim_pkt_range(reg, cnum64_smax(r64)); return reg_bounds_sanity_check(env, reg, "bpf_set_reg_range"); }