From nobody Sun Feb 8 23:32:15 2026 Received: from mx-rz-3.rrze.uni-erlangen.de (mx-rz-3.rrze.uni-erlangen.de [131.188.11.22]) (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 CC05D334C27; Tue, 27 Jan 2026 12:10:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=131.188.11.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769515845; cv=none; b=CV0T78LlAu8N6OBXzeq19m4YZzS8yIjXLCP0SzrQCwX9z+3qY7Dzf/1Aw4dt0s460ax9YGPKWCuIoUt75CVOBtamqq+LEOUXMa8iGp1LdgNFxr78UqzFhNHFL6dUpXTmwwVUkU1P4/dCz0cqb4jwUkumGKG4LRsdvf6A5sdjCUk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769515845; c=relaxed/simple; bh=dr2eJx0AGMOiACOfHv2a++ANA/ZfYMyl7pXJI+5g3vA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=O/PAAi0SNUp4qrP0a2vP5GrFj/anyM32rDDUmpUXfoA5qxF+oAFgqbZ6llLCleTqlKbK21uEcWiOImgZfLEyBY3qoVDi6VkZDKCsZ/YWYLtsWE7JVwXKXf+Wp186azKFzR77vwo/PPp0xyhb9M1dZgO2lAiMCgO//WkxAkzi40I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=fau.de; spf=pass smtp.mailfrom=fau.de; dkim=pass (2048-bit key) header.d=fau.de header.i=@fau.de header.b=OZq/i/CR; arc=none smtp.client-ip=131.188.11.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=fau.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=fau.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fau.de header.i=@fau.de header.b="OZq/i/CR" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fau.de; s=fau-2021; t=1769515386; bh=HUnxlQMGA5QnlWkBCPNLQetlCj3hCWlm8SxfDSPkKW4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:To:CC: Subject; b=OZq/i/CRcW4ASwJQXBCKdzE/5+t9cBJjkg7ckp9GiAHf4mcorHRi2sxt2JtK/Ue5g d80dnhc7UC+Fam3ze8gU3UqIbDtcwwmMofKVNOvc8OlwHBd2lInn9v3h1Dyiv9eHr/ ggu9evRaIDqvYiQYVFGQSGeocYpLXxOA360abRj/huLgyNAclbRGC/3991HPFRFz4t i6R+LI6NhePmut8brz+TnRtacirNcsYCH1WW98p2QMgcqmdrzsCYzwuJLgBv0NiQ0o 9hhSqtRkqk6NE8zvrh3nuoIM+SRStjmqBjhNmuPtpaQcgoN0UyZflhP4eah38AHTGH UUXcZves1Pa+g== Received: from mx-rz-smart.rrze.uni-erlangen.de (mx-rz-smart.rrze.uni-erlangen.de [IPv6:2001:638:a000:1025::1e]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mx-rz-3.rrze.uni-erlangen.de (Postfix) with ESMTPS id 4f0kZ23WN4z1yJf; Tue, 27 Jan 2026 13:03:06 +0100 (CET) X-Virus-Scanned: amavisd-new at boeck5.rrze.uni-erlangen.de (RRZE) X-RRZE-Flag: Not-Spam X-RRZE-Submit-IP: 10.20.40.230 Received: from luis-tp.pool.uni-erlangen.de (faustaff-010-020-040-230.pool.uni-erlangen.de [10.20.40.230]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: U2FsdGVkX1/Bo9po6XFoCqGF6taN//KOcDOxeuewqqk=) by smtp-auth.uni-erlangen.de (Postfix) with ESMTPSA id 4f0kYy6dCZz1yD8; Tue, 27 Jan 2026 13:03:02 +0100 (CET) From: Luis Gerhorst To: Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Luis Gerhorst , Ihor Solodrai , Kumar Kartikeya Dwivedi , bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org Cc: Yinhao Hu , Kaiyan Mei , Dongliang Mu Subject: [PATCH bpf-next 1/2] bpf: Fix verifier_bug_if to account for BPF_CALL Date: Tue, 27 Jan 2026 12:59:11 +0100 Message-ID: <20260127115912.3026761-2-luis.gerhorst@fau.de> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260127115912.3026761-1-luis.gerhorst@fau.de> References: <20260127115912.3026761-1-luis.gerhorst@fau.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The BPF verifier assumes `insn_aux->nospec_result` is only set for direct memory writes (e.g., `*(u32*)(r1+off) =3D r2`). However, the assertion fails to account for helper calls (e.g., `bpf_skb_load_bytes_relative`) that perform writes to stack memory. Make the check more precise to resolve this. The problem is that `BPF_CALL` instructions have `BPF_CLASS(insn->code) =3D=3D BPF_JMP`, which triggers the warning check: - Helpers like `bpf_skb_load_bytes_relative` write to stack memory - `check_helper_call()` loops through `meta.access_size`, calling `check_mem_access(..., BPF_WRITE)` - `check_stack_write()` sets `insn_aux->nospec_result =3D 1` - Since `BPF_CALL` is encoded as `BPF_JMP | BPF_CALL`, the warning fires Execution flow: ``` 1. Drop capabilities =E2=86=92 Enable Spectre mitigation 2. Load BPF program =E2=94=94=E2=94=80> do_check() =E2=94=9C=E2=94=80> check_cond_jmp_op() =E2=86=92 Marks dead branch = as speculative =E2=94=82 =E2=94=94=E2=94=80> push_stack(..., speculative=3Dtrue) =E2=94=9C=E2=94=80> pop_stack() =E2=86=92 state->speculative =3D 1 =E2=94=9C=E2=94=80> check_helper_call() =E2=86=92 Processes helper i= n dead branch =E2=94=82 =E2=94=94=E2=94=80> check_mem_access(..., BPF_WRITE) =E2=94=82 =E2=94=94=E2=94=80> insn_aux->nospec_result =3D 1 =E2=94=94=E2=94=80> Checks: state->speculative && insn_aux->nospec_r= esult =E2=94=94=E2=94=80> BPF_CLASS(insn->code) =3D=3D BPF_JMP =E2=86= =92 WARNING ``` To fix the assert, it would be nice to be able to reuse bpf_insn_successors() here, but bpf_insn_successors()->cnt is not exactly what we want as it may also be 1 for BPF_JA. Instead, we could check opcode_info.can_jump, but then we would have to share the table between the functions. This would mean moving the table out of the function and adding bpf_opcode_info(). As the verifier_bug_if() only runs for insns with nospec_result set, the impact on verification time would likely still be negligible. However, I assume sharing bpf_opcode_info() between liveness.c and verifier.c will not be worth it. It seems as only adjust_jmp_off() could also be simplified using it, and there imm/off is touched. Thus it is maybe better to rely on exact opcode/class matching there. Therefore, to avoid this sharing only for a verifier_bug_if(), just check the opcode. This should now cover all opcodes for which can_jump in bpf_insn_successors() is true. Parts of the description and example are taken from the bug report. Fixes: dadb59104c64 ("bpf: Fix aux usage after do_check_insn()") Signed-off-by: Luis Gerhorst Reported-by: Yinhao Hu Reported-by: Kaiyan Mei Reported-by: Dongliang Mu Closes: https://lore.kernel.org/bpf/7678017d-b760-4053-a2d8-a6879b0dbeeb@hu= st.edu.cn/ --- kernel/bpf/verifier.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c2f2650db9fd..e7ff8394e0da 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -21065,17 +21065,19 @@ static int do_check(struct bpf_verifier_env *env) * may skip a nospec patched-in after the jump. This can * currently never happen because nospec_result is only * used for the write-ops - * `*(size*)(dst_reg+off)=3Dsrc_reg|imm32` which must - * never skip the following insn. Still, add a warning - * to document this in case nospec_result is used - * elsewhere in the future. + * `*(size*)(dst_reg+off)=3Dsrc_reg|imm32` and helper + * calls. These must never skip the following insn + * (i.e., bpf_insn_successors()'s opcode_info.can_jump + * is false). Still, add a warning to document this in + * case nospec_result is used elsewhere in the future. * * All non-branch instructions have a single * fall-through edge. For these, nospec_result should * already work. */ - if (verifier_bug_if(BPF_CLASS(insn->code) =3D=3D BPF_JMP || - BPF_CLASS(insn->code) =3D=3D BPF_JMP32, env, + if (verifier_bug_if((BPF_CLASS(insn->code) =3D=3D BPF_JMP || + BPF_CLASS(insn->code) =3D=3D BPF_JMP32) && + BPF_OP(insn->code) !=3D BPF_CALL, env, "speculation barrier after jump instruction may not have the desi= red effect")) return -EFAULT; process_bpf_exit: --=20 2.52.0 From nobody Sun Feb 8 23:32:15 2026 Received: from mx-rz-3.rrze.uni-erlangen.de (mx-rz-3.rrze.uni-erlangen.de [131.188.11.22]) (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 79DEB346766; Tue, 27 Jan 2026 12:06:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=131.188.11.22 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769515586; cv=none; b=GwLKEqBorq24d2cmpPJpI5+F7BUt6CU5gm0sK/c39eAFTwwFr6UumeixDtuV7WbaUSCvdZu4Y9RmHfGoYwIijuihqWXPax+rntnYip6J0gTGxiLh6dlzpuPFJPGtRr1DFYsWcW+gkIrIoA9fgNqq+6GhXCSjmn7qZcOYF3nA9IA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769515586; c=relaxed/simple; bh=Yb9lZxivsGxPrQaK/QH2O9mUVmg2N9TGodDCbSjnzfA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SAIuFoXMxPGvzWaNEYoQDNXeRoBMNpvXddfrhKnUY6SJPd+AJHbM7xWNIbHtJ72Ygb9PtbtGRUsesaI1iTZQUR/LpR9hgmto8tHDnRLy69T1k5Y2CdCisv1UUj/MXl/qbvqK/kDCUd3wjffqZPw8XC/bfVq/6JGSf2jaXUqbRG0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=fau.de; spf=pass smtp.mailfrom=fau.de; dkim=pass (2048-bit key) header.d=fau.de header.i=@fau.de header.b=tVr8aQMN; arc=none smtp.client-ip=131.188.11.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=fau.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=fau.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fau.de header.i=@fau.de header.b="tVr8aQMN" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fau.de; s=fau-2021; t=1769515581; bh=aV5FAftEjO1JhW71J3QNsn4ERdcp6E4DHdmuR2vlPKc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:To:CC: Subject; b=tVr8aQMN6GPqz6aWv7w0JJ8ynnVdDgkjVjrkv4y4LS3GbbRhVthqhff7V7+18h4HP cp7ls0DWyfGwYXj+rIIlylbDi9kjSpf1vfI01WkQAwVpQ795Wnz8trguKTBHeiz60A biSXk6wGCj3IO33bXxL8YuoCBrbsuCW8CnjNvmOqS+/hbRiPpgsBceTywNpgTQ/5o5 rvLnlDgOiPDqZJ3sRHrRDuPTrk3U6qfV8VOY4/Uvx1JYSG4emXA/dc0JoNJtBprYqL NJUZ4ZLbzZk/j6v7SJf/wZJyeRkvf723EAtcDYAIELReZN6TBWKJYbngDeajHYK5MU LdrRD3XGiVNgQ== Received: from mx-rz-smart.rrze.uni-erlangen.de (mx-rz-smart.rrze.uni-erlangen.de [IPv6:2001:638:a000:1025::1e]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mx-rz-3.rrze.uni-erlangen.de (Postfix) with ESMTPS id 4f0kdn4DH3z1yP9; Tue, 27 Jan 2026 13:06:21 +0100 (CET) X-Virus-Scanned: amavisd-new at boeck4.rrze.uni-erlangen.de (RRZE) X-RRZE-Flag: Not-Spam X-RRZE-Submit-IP: 10.20.40.230 Received: from luis-tp.pool.uni-erlangen.de (faustaff-010-020-040-230.pool.uni-erlangen.de [10.20.40.230]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: U2FsdGVkX1/2XH7rkSWgg4COYI3k9STRuQfFZEP4ut4=) by smtp-auth.uni-erlangen.de (Postfix) with ESMTPSA id 4f0kdk4Fn4z1yGv; Tue, 27 Jan 2026 13:06:18 +0100 (CET) From: Luis Gerhorst To: Alexei Starovoitov , Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Luis Gerhorst , Ihor Solodrai , Kumar Kartikeya Dwivedi , bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org Cc: Yinhao Hu , Kaiyan Mei , Dongliang Mu Subject: [PATCH bpf-next 2/2] bpf: Test nospec after dead stack write in helper Date: Tue, 27 Jan 2026 12:59:12 +0100 Message-ID: <20260127115912.3026761-3-luis.gerhorst@fau.de> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260127115912.3026761-1-luis.gerhorst@fau.de> References: <20260127115912.3026761-1-luis.gerhorst@fau.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Without the fix from the previous commit, the selftest fails: $ ./tools/testing/selftests/bpf/vmtest.sh -- \ ./test_progs -t verifier_unpriv [...] run_subtest:PASS:obj_open_mem 0 nsec libbpf: BTF loading error: -EPERM libbpf: Error loading .BTF into kernel: -EPERM. BTF is optional, ignoring. libbpf: prog 'unpriv_nospec_after_helper_stack_write': BPF program load fai= led: -EFAULT libbpf: prog 'unpriv_nospec_after_helper_stack_write': failed to load: -EFA= ULT libbpf: failed to load object 'verifier_unpriv' run_subtest:FAIL:unexpected_load_failure unexpected error: -14 (errno 14) VERIFIER LOG: =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D 0: R1=3Dctx() R10=3Dfp0 0: (b7) r0 =3D 0 ; R0=3DP0 1: (55) if r0 !=3D 0x1 goto pc+6 2: R0=3DPscalar() R1=3Dctx() R10=3Dfp0 2: (b7) r2 =3D 0 ; R2=3DP0 3: (bf) r3 =3D r10 ; R3=3Dfp0 R10=3Dfp0 4: (07) r3 +=3D -16 ; R3=3Dfp-16 5: (b7) r4 =3D 4 ; R4=3DP4 6: (b7) r5 =3D 0 ; R5=3DP0 7: (85) call bpf_skb_load_bytes_relative#68 verifier bug: speculation barrier after jump instruction may not have the d= esired effect (BPF_CLASS(insn->code) =3D=3D BPF_JMP || BPF_CLASS(insn->code= ) =3D=3D BPF_JMP32) processed 9 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak= _states 0 mark_read 0 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D [...] The test is based on the PoC from the report. Signed-off-by: Luis Gerhorst Reported-by: Yinhao Hu Reported-by: Kaiyan Mei Reported-by: Dongliang Mu Link: https://lore.kernel.org/bpf/7678017d-b760-4053-a2d8-a6879b0dbeeb@hust= .edu.cn/ --- .../selftests/bpf/progs/verifier_unpriv.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/te= sting/selftests/bpf/progs/verifier_unpriv.c index 28b4f7035ceb..8ee1243e62a8 100644 --- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c +++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c @@ -950,4 +950,26 @@ l3_%=3D: r0 =3D 0; \ " ::: __clobber_all); } =20 +SEC("socket") +__description("unpriv: nospec after dead stack write in helper") +__success __success_unpriv +__retval(0) +/* Dead code sanitizer rewrites the call to `goto -1`. */ +__naked void unpriv_dead_helper_stack_write_nospec_result(void) +{ + asm volatile (" \ + r0 =3D 0; \ + if r0 !=3D 1 goto l0_%=3D; \ + r2 =3D 0; \ + r3 =3D r10; \ + r3 +=3D -16; \ + r4 =3D 4; \ + r5 =3D 0; \ + call %[bpf_skb_load_bytes_relative]; \ +l0_%=3D: exit; \ +" : + : __imm(bpf_skb_load_bytes_relative) + : __clobber_all); +} + char _license[] SEC("license") =3D "GPL"; --=20 2.52.0