]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Reset register bounds before narrowing retval range in check_mem_access()
authorTristan Madani <tristan@talencesecurity.com>
Mon, 22 Jun 2026 23:01:22 +0000 (23:01 +0000)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 23 Jun 2026 00:11:46 +0000 (17:11 -0700)
When the BPF verifier processes a context load of an LSM hook return
value, it calls __mark_reg_s32_range() to narrow the register to the
hook's valid range. However, __mark_reg_s32_range() intersects the new
range with the register's existing bounds using max_t()/min_t() rather
than replacing them.

If the destination register carries stale bounds from a prior instruction
(e.g. BPF_MOV64_IMM), the intersection can produce a range narrower than
reality. The verifier then believes it knows the register's exact value,
while at runtime the actual hook return value is loaded, creating a
verifier/runtime mismatch that can be used to bypass BPF memory safety
checks.

The else branch already calls mark_reg_unknown() to reset register state
before any narrowing. Apply the same reset in the is_retval path so
stale bounds are cleared before __mark_reg_s32_range() intersects.

Fixes: 5d99e198be27 ("bpf, lsm: Add check for BPF LSM return value")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20260622230123.3695446-2-tristmd@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/verifier.c

index a2b348f980807304dbe9b8f0dbd201d639a96b7c..21a365d436a595202dcb392716228617b56ce7ba 100644 (file)
@@ -6201,6 +6201,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
                         */
                        if (info.reg_type == SCALAR_VALUE) {
                                if (info.is_retval && get_func_retval_range(env->prog, &range)) {
+                                       mark_reg_unknown(env, regs, value_regno);
                                        err = __mark_reg_s32_range(env, regs, value_regno,
                                                                   range.minval, range.maxval);
                                        if (err)