From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 588E246D2DA; Tue, 21 Jul 2026 17:37:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784655450; cv=none; b=ZnY8DFa0PdVBymfvpN0yc/rX4ctQHT0Wm5EVccPY5hLGSG1UaOJh+TWiT5HD4XMfhyNPdh+vN/t/YSvoJ5+n0FyKC7CDqTe/mSqqGMAmHeFwI2HNStCV2fcxDyMo22mknqGkGfsieZXFdw/MGqXj4mYY+I0t+BmvCYirMDPB42Q= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784655450; c=relaxed/simple; bh=6u3+RvF6teSkk0WTDwh6Fom/7Jq6Kn+FENAtMYGeUiA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pMA8eztwzQyFhkBSiVTaqqveVJ4r81xhhnyywscZzwIbhelnjwGEJ+h56AcoWNZWI8gQj8trjbutHE6pyYgbN6OjLFk+F76QQUakEl8sv8WXR1b3Bsoeo+8/h3iApuShljC3bnLFNRNYc1/ZxVE91bVFnhP+0U0DepHHKxxCl1A= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=j8LDAi8/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="j8LDAi8/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B245C1F000E9; Tue, 21 Jul 2026 17:37:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1784655449; bh=IdJ9ZKHjEpylMiNE1Aos7gfZkqn1cq6xcSXui9DyPCs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=j8LDAi8/9Gey9w/HW5/ZQnFXm9OiEkFFGghE6gFvPsFyv00T6A+G64VXoqjtXqX4k NkuA4+9Lbjb6p9PLRoqer0YjBundRWLR4nQ20Nti9kKDIt19kXY+unGoMkXAYDmuO2 FCRI+FsjD8VjXTlF65HfNsOeNfMrZ2LLUNsJTqGk= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, STAR Labs SG , Daniel Borkmann , Alexei Starovoitov , Shung-Hsi Yu , Sasha Levin Subject: [PATCH 6.18 0014/1611] bpf: Clear delta when clearing reg id for non-{add,sub} ops Date: Tue, 21 Jul 2026 17:02:10 +0200 Message-ID: <20260721152515.110287185@linuxfoundation.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260721152514.750365251@linuxfoundation.org> References: <20260721152514.750365251@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Daniel Borkmann commit 1b327732c84640c1e3da487eefe9d00cc9f2dd34 upstream. When a non-{add,sub} alu op such as xor is performed on a scalar register that previously had a BPF_ADD_CONST delta, the else path in adjust_reg_min_max_vals() only clears dst_reg->id but leaves dst_reg->delta unchanged. This stale delta can propagate via assign_scalar_id_before_mov() when the register is later used in a mov. It gets a fresh id but keeps the stale delta from the old (now-cleared) BPF_ADD_CONST. This stale delta can later propagate leading to a verifier-vs- runtime value mismatch. The clear_id label already correctly clears both delta and id. Make the else path consistent by also zeroing the delta when id is cleared. More generally, this introduces a helper clear_scalar_id() which internally takes care of zeroing. There are various other locations in the verifier where only the id is cleared. By using the helper we catch all current and future locations. Fixes: 98d7ca374ba4 ("bpf: Track delta between "linked" registers.") Reported-by: STAR Labs SG Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/r/20260407192421.508817-2-daniel@iogearbox.net Signed-off-by: Alexei Starovoitov [shung-hsi.yu: `delta` field was called `off` before commit 3d91c618aca4 ("bpf: rename bpf_reg_state->off to bpf_reg_state->delta"), and clear_singular_ids() does not exists before commit b2a0aa3a8739 ("bpf: Clear singular ids for scalars in is_state_visited()")] Signed-off-by: Shung-Hsi Yu Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 50 ++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3dcf591acd50d2..bf8cadd19593b2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5013,27 +5013,30 @@ static bool __is_pointer_value(bool allow_ptr_leaks, return reg->type != SCALAR_VALUE; } +static void clear_scalar_id(struct bpf_reg_state *reg) +{ + reg->id = 0; + reg->off = 0; +} + static void assign_scalar_id_before_mov(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg) { if (src_reg->type != SCALAR_VALUE) return; - - if (src_reg->id & BPF_ADD_CONST) { - /* - * The verifier is processing rX = rY insn and - * rY->id has special linked register already. - * Cleared it, since multiple rX += const are not supported. - */ - src_reg->id = 0; - src_reg->off = 0; - } - + /* + * The verifier is processing rX = rY insn and + * rY->id has special linked register already. + * Cleared it, since multiple rX += const are not supported. + */ + if (src_reg->id & BPF_ADD_CONST) + clear_scalar_id(src_reg); + /* + * Ensure that src_reg has a valid ID that will be copied to + * dst_reg and then will be used by sync_linked_regs() to + * propagate min/max range. + */ if (!src_reg->id && !tnum_is_const(src_reg->var_off)) - /* Ensure that src_reg has a valid ID that will be copied to - * dst_reg and then will be used by sync_linked_regs() to - * propagate min/max range. - */ src_reg->id = ++env->id_gen; } @@ -5466,7 +5469,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, * coerce_reg_to_size will adjust the boundaries. */ if (get_reg_width(reg) > size * BITS_PER_BYTE) - state->regs[dst_regno].id = 0; + clear_scalar_id(&state->regs[dst_regno]); } else { int spill_cnt = 0, zero_cnt = 0; @@ -15476,7 +15479,7 @@ static void scalar_byte_swap(struct bpf_reg_state *dst_reg, struct bpf_insn *ins * any existing ties and avoid incorrect bounds propagation. */ if (need_bswap || insn->imm == 16 || insn->imm == 32) - dst_reg->id = 0; + clear_scalar_id(dst_reg); if (need_bswap) { if (insn->imm == 16) @@ -15845,8 +15848,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * we cannot accumulate another val into rx->off. */ clear_id: - dst_reg->off = 0; - dst_reg->id = 0; + clear_scalar_id(dst_reg); } else { if (alu32) dst_reg->id |= BPF_ADD_CONST32; @@ -15859,7 +15861,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * Make sure ID is cleared otherwise dst_reg min/max could be * incorrectly propagated into other registers by sync_linked_regs() */ - dst_reg->id = 0; + clear_scalar_id(dst_reg); } return 0; } @@ -15990,7 +15992,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); if (!no_sext) - dst_reg->id = 0; + clear_scalar_id(dst_reg); coerce_reg_to_size_sx(dst_reg, insn->off >> 3); dst_reg->subreg_def = DEF_NOT_SUBREG; } else { @@ -16016,7 +16018,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) * propagated into src_reg by sync_linked_regs() */ if (!is_src_reg_u32) - dst_reg->id = 0; + clear_scalar_id(dst_reg); dst_reg->subreg_def = env->insn_idx + 1; } else { /* case: W1 = (s8, s16)W2 */ @@ -16026,7 +16028,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); if (!no_sext) - dst_reg->id = 0; + clear_scalar_id(dst_reg); dst_reg->subreg_def = env->insn_idx + 1; coerce_subreg_to_size_sx(dst_reg, insn->off >> 3); } @@ -16856,7 +16858,7 @@ static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_st e->is_reg = is_reg; e->regno = spi_or_reg; } else { - reg->id = 0; + clear_scalar_id(reg); } } -- 2.53.0