maybe_fork_scalars() is called for both BPF_AND and BPF_OR when the source operand is a constant. When dst has signed range [-1, 0], it forks the verifier state, setting the pushed path dst to 0 and the current path dst to -1. For BPF_AND this is correct: 0 & K == 0. For BPF_OR this is wrong: 0 | K == K, not 0. The pushed path therefore tracks dst as 0 when the runtime value is K, producing an exploitable verifier/runtime divergence that allows out-of-bounds map access. Fix this by passing the opcode into maybe_fork_scalars() so it can set the correct value for the pushed state: - BPF_AND: pushed dst = 0 (unchanged) - BPF_OR: pushed dst = insn->imm (the constant K) The bug was introduced by commit bffacdb80b93 ("bpf: Recognize special arithmetic shift in the verifier"). Fixes: bffacdb80b93 ("bpf: Recognize special arithmetic shift in the verifier") Signed-off-by: Daniel Wade --- kernel/bpf/verifier.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a9e2380327..2e644f489e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14193,7 +14193,7 @@ static bool is_safe_to_compute_dst_reg_range(struct bpf_insn *insn, } static int maybe_fork_scalars(struct bpf_verifier_env *env, struct bpf_insn *insn, - struct bpf_reg_state *dst_reg) + struct bpf_reg_state *dst_reg, u8 opcode) { struct bpf_verifier_state *branch; struct bpf_reg_state *regs; @@ -14211,11 +14211,16 @@ static int maybe_fork_scalars(struct bpf_verifier_env *env, struct bpf_insn *ins return PTR_ERR(branch); regs = branch->frame[branch->curframe]->regs; + /* For AND: 0 & K == 0, so pushed dst = 0 is correct. + * For OR: 0 | K == K, so pushed dst must be set to K. + */ if (alu32) { - __mark_reg32_known(®s[insn->dst_reg], 0); + __mark_reg32_known(®s[insn->dst_reg], + opcode == BPF_OR ? (u32)insn->imm : 0); __mark_reg32_known(dst_reg, -1ull); } else { - __mark_reg_known(®s[insn->dst_reg], 0); + __mark_reg_known(®s[insn->dst_reg], + opcode == BPF_OR ? insn->imm : 0); __mark_reg_known(dst_reg, -1ull); } return 0; @@ -14277,7 +14282,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, break; case BPF_AND: if (tnum_is_const(src_reg.var_off)) { - ret = maybe_fork_scalars(env, insn, dst_reg); + ret = maybe_fork_scalars(env, insn, dst_reg, BPF_AND); if (ret) return ret; } @@ -14287,7 +14292,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, break; case BPF_OR: if (tnum_is_const(src_reg.var_off)) { - ret = maybe_fork_scalars(env, insn, dst_reg); + ret = maybe_fork_scalars(env, insn, dst_reg, BPF_OR); if (ret) return ret; } -- 2.43.0