[PATCH] riscv, bpf: Emit fence.i for BPF_NOSPEC
Bo Gan
ganboing at gmail.com
Thu Jan 8 19:41:15 PST 2026
Hi Lukas,
Stefan and I have some doubts on fence.i's effectiveness as speculation
barrier. Flushing entire local instruction cache and instruction pipeline
is not absolutely necessary on impl having coherent I/D caches. Quoting
from Unprivileged SPEC ver. 20250508:
"The FENCE.I instruction was designed to support a wide variety of
implementations. A simple implementation can flush the local instruction
cache and the instruction pipeline when the FENCE.I is executed. A more
complex implementation might snoop the instruction (data) cache on every
data (instruction) cache miss, or use an inclusive unified private L2
cache to invalidate lines from the primary instruction cache when they
are being written by a local store instruction. If instruction and data
caches are kept coherent in this way, or if the memory system consists of
only uncached RAMs, then just the fetch pipeline needs to be flushed at a
FENCE.I"
There's the question on overhead, too. Perhaps there's a more accurate and
lightweight insn available? I'm not an expert in u-arch. My gut feeling is
that we should not be dependent on specific impl's behavior and the riscv
SPEC should provide guidelines on speculation barrier instructions and how
to use them. Thus, I'm forwarding this to the Speculation Barriers Task-
Group, which I hope should be the perfect place to discuss such kind of
issues. @Speculation Barriers TG Please share your thoughts. Note that we
are dealing with existing HW, so we expect something to be working with
current SPEC and actual silicon. I'd be happy if I'm proven wrong, and
fence.i can actually be a speculation barrier. That's also a relief. Thank
you everyone.
BTW, per SPEC:
"The FENCE.I only synchronizes the local hart, and the OS can reschedule
the user hart to a different physical hart after the FENCE.I. This would
require the OS to execute an additional FENCE.I as part of every context
migration"
fence.i is local. I know some core does a broadcast and try to make it a
global fence.i, but this is not required by the SPEC.
Bo
On 12/28/25 09:37, Lukas Gerlach wrote:
> The BPF verifier inserts BPF_NOSPEC instructions to create speculation
> barriers. However, the RISC-V BPF JIT emits nothing for this
> instruction, leaving programs vulnerable to speculative execution
> attacks.
>
> Originally, BPF_NOSPEC was used only for Spectre v4 mitigation, programs
> containing potential Spectre v1 gadgets were rejected by the verifier.
> With the VeriFence changes, the verifier now accepts these
> programs and inserts BPF_NOSPEC barriers for Spectre v1 mitigation as
> well. On RISC-V, this means programs that were previously rejected are
> now accepted but left unprotected against both v1 and v4 attacks.
>
> RISC-V lacks a dedicated speculation barrier instruction.
> This patch uses the fence.i instruction as a stopgap solution.
> However an alternative and safer approach would be to reject vulnerable bpf
> programs again.
>
> Fixes: f5e81d111750 ("bpf: Introduce BPF nospec instruction for mitigating Spectre v4")
> Fixes: 5fcf896efe28 ("Merge branch 'bpf-mitigate-spectre-v1-using-barriers'")
> Signed-off-by: Lukas Gerlach <lukas.gerlach at cispa.de>
> ---
> arch/riscv/net/bpf_jit.h | 10 ++++++++++
> arch/riscv/net/bpf_jit_comp32.c | 6 +++++-
> arch/riscv/net/bpf_jit_comp64.c | 6 +++++-
> 3 files changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h
> index 632ced07bca4..e70b3bc19206 100644
> --- a/arch/riscv/net/bpf_jit.h
> +++ b/arch/riscv/net/bpf_jit.h
> @@ -619,6 +619,16 @@ static inline void emit_fence_rw_rw(struct rv_jit_context *ctx)
> emit(rv_fence(0x3, 0x3), ctx);
> }
>
> +static inline u32 rv_fence_i(void)
> +{
> + return rv_i_insn(0, 0, 1, 0, 0x0f);
> +}
> +
> +static inline void emit_fence_i(struct rv_jit_context *ctx)
> +{
> + emit(rv_fence_i(), ctx);
> +}
> +
> static inline u32 rv_nop(void)
> {
> return rv_i_insn(0, 0, 0, 0, 0x13);
> diff --git a/arch/riscv/net/bpf_jit_comp32.c b/arch/riscv/net/bpf_jit_comp32.c
> index 592dd86fbf81..d9a6f55a7e8e 100644
> --- a/arch/riscv/net/bpf_jit_comp32.c
> +++ b/arch/riscv/net/bpf_jit_comp32.c
> @@ -1248,8 +1248,12 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
> return -1;
> break;
>
> - /* speculation barrier */
> + /*
> + * Speculation barrier using fence.i for pipeline serialization.
> + * RISC-V lacks a dedicated speculation barrier instruction.
> + */
> case BPF_ST | BPF_NOSPEC:
> + emit_fence_i(ctx);
> break;
>
> case BPF_ST | BPF_MEM | BPF_B:
> diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
> index 45cbc7c6fe49..fabafbebde0c 100644
> --- a/arch/riscv/net/bpf_jit_comp64.c
> +++ b/arch/riscv/net/bpf_jit_comp64.c
> @@ -1864,8 +1864,12 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
> break;
> }
>
> - /* speculation barrier */
> + /*
> + * Speculation barrier using fence.i for pipeline serialization.
> + * RISC-V lacks a dedicated speculation barrier instruction.
> + */
> case BPF_ST | BPF_NOSPEC:
> + emit_fence_i(ctx);
> break;
>
> /* ST: *(size *)(dst + off) = imm */
More information about the linux-riscv
mailing list