Skip to content

Commit dff883d

Browse files
luisgerhorstAlexei Starovoitov
authored andcommitted
bpf, arm64, powerpc: Change nospec to include v1 barrier
This changes the semantics of BPF_NOSPEC (previously a v4-only barrier) to always emit a speculation barrier that works against both Spectre v1 AND v4. If mitigation is not needed on an architecture, the backend should set bpf_jit_bypass_spec_v4/v1(). As of now, this commit only has the user-visible implication that unpriv BPF's performance on PowerPC is reduced. This is the case because we have to emit additional v1 barrier instructions for BPF_NOSPEC now. This commit is required for a future commit to allow us to rely on BPF_NOSPEC for Spectre v1 mitigation. As of this commit, the feature that nospec acts as a v1 barrier is unused. Commit f5e81d1 ("bpf: Introduce BPF nospec instruction for mitigating Spectre v4") noted that mitigation instructions for v1 and v4 might be different on some archs. While this would potentially offer improved performance on PowerPC, it was dismissed after the following considerations: * Only having one barrier simplifies the verifier and allows us to easily rely on v4-induced barriers for reducing the complexity of v1-induced speculative path verification. * For the architectures that implemented BPF_NOSPEC, only PowerPC has distinct instructions for v1 and v4. Even there, some insns may be shared between the barriers for v1 and v4 (e.g., 'ori 31,31,0' and 'sync'). If this is still found to impact performance in an unacceptable way, BPF_NOSPEC can be split into BPF_NOSPEC_V1 and BPF_NOSPEC_V4 later. As an optimization, we can already skip v1/v4 insns from being emitted for PowerPC with this setup if bypass_spec_v1/v4 is set. Vulnerability-status for BPF_NOSPEC-based Spectre mitigations (v4 as of this commit, v1 in the future) is therefore: * x86 (32-bit and 64-bit), ARM64, and PowerPC (64-bit): Mitigated - This patch implements BPF_NOSPEC for these architectures. The previous v4-only version was supported since commit f5e81d1 ("bpf: Introduce BPF nospec instruction for mitigating Spectre v4") and commit b7540d6 ("powerpc/bpf: Emit stf barrier instruction sequences for BPF_NOSPEC"). * LoongArch: Not Vulnerable - Commit a6f6a95 ("LoongArch, bpf: Fix jit to skip speculation barrier opcode") is the only other past commit related to BPF_NOSPEC and indicates that the insn is not required there. * MIPS: Vulnerable (if unprivileged BPF is enabled) - Commit a6f6a95f2580 ("LoongArch, bpf: Fix jit to skip speculation barrier opcode") indicates that it is not vulnerable, but this contradicts the kernel and Debian documentation. Therefore, I assume that there exist vulnerable MIPS CPUs (but maybe not from Loongson?). In the future, BPF_NOSPEC could be implemented for MIPS based on the GCC speculation_barrier [1]. For now, we rely on unprivileged BPF being disabled by default. * Other: Unknown - To the best of my knowledge there is no definitive information available that indicates that any other arch is vulnerable. They are therefore left untouched (BPF_NOSPEC is not implemented, but bypass_spec_v1/v4 is also not set). I did the following testing to ensure the insn encoding is correct: * ARM64: * 'dsb nsh; isb' was successfully tested with the BPF CI in [2] * 'sb' locally using QEMU v7.2.15 -cpu max (emitted sb insn is executed for example with './test_progs -t verifier_array_access') * PowerPC: The following configs were tested locally with ppc64le QEMU v8.2 '-machine pseries -cpu POWER9': * STF_BARRIER_EIEIO + CONFIG_PPC_BOOK32_64 * STF_BARRIER_SYNC_ORI (forced on) + CONFIG_PPC_BOOK32_64 * STF_BARRIER_FALLBACK (forced on) + CONFIG_PPC_BOOK32_64 * CONFIG_PPC_E500 (forced on) + STF_BARRIER_EIEIO * CONFIG_PPC_E500 (forced on) + STF_BARRIER_SYNC_ORI (forced on) * CONFIG_PPC_E500 (forced on) + STF_BARRIER_FALLBACK (forced on) * CONFIG_PPC_E500 (forced on) + STF_BARRIER_NONE (forced on) Most of those cobinations should not occur in practice, but I was not able to get an PPC e6500 rootfs (for testing PPC_E500 without forcing it on). In any case, this should ensure that there are no unexpected conflicts between the insns when combined like this. Individual v1/v4 barriers were already emitted elsewhere. Hari's ack is for the PowerPC changes only. [1] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=29b74545531f6afbee9fc38c267524326dbfbedf ("MIPS: Add speculation_barrier support") [2] #8576 Signed-off-by: Luis Gerhorst <[email protected]> Acked-by: Hari Bathini <[email protected]> Cc: Henriette Herzog <[email protected]> Cc: Maximilian Ott <[email protected]> Cc: Milan Stephan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 03c68a0 commit dff883d

File tree

5 files changed

+65
-27
lines changed

5 files changed

+65
-27
lines changed

arch/arm64/net/bpf_jit.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,9 @@
325325
#define A64_MRS_SP_EL0(Rt) \
326326
aarch64_insn_gen_mrs(Rt, AARCH64_INSN_SYSREG_SP_EL0)
327327

328+
/* Barriers */
329+
#define A64_SB aarch64_insn_get_sb_value()
330+
#define A64_DSB_NSH (aarch64_insn_get_dsb_base_value() | 0x7 << 8)
331+
#define A64_ISB aarch64_insn_get_isb_value()
332+
328333
#endif /* _BPF_JIT_H */

arch/arm64/net/bpf_jit_comp.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,9 +1630,14 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
16301630
return ret;
16311631
break;
16321632

1633-
/* speculation barrier */
1633+
/* speculation barrier against v1 and v4 */
16341634
case BPF_ST | BPF_NOSPEC:
1635-
/* See bpf_jit_bypass_spec_v4() */
1635+
if (alternative_has_cap_likely(ARM64_HAS_SB)) {
1636+
emit(A64_SB, ctx);
1637+
} else {
1638+
emit(A64_DSB_NSH, ctx);
1639+
emit(A64_ISB, ctx);
1640+
}
16361641
break;
16371642

16381643
/* ST: *(size *)(dst + off) = imm */

arch/powerpc/net/bpf_jit_comp64.c

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
414414
u32 *addrs, int pass, bool extra_pass)
415415
{
416416
enum stf_barrier_type stf_barrier = stf_barrier_type_get();
417+
bool sync_emitted, ori31_emitted;
417418
const struct bpf_insn *insn = fp->insnsi;
418419
int flen = fp->len;
419420
int i, ret;
@@ -806,26 +807,52 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
806807

807808
/*
808809
* BPF_ST NOSPEC (speculation barrier)
810+
*
811+
* The following must act as a barrier against both Spectre v1
812+
* and v4 if we requested both mitigations. Therefore, also emit
813+
* 'isync; sync' on E500 or 'ori31' on BOOK3S_64 in addition to
814+
* the insns needed for a Spectre v4 barrier.
815+
*
816+
* If we requested only !bypass_spec_v1 OR only !bypass_spec_v4,
817+
* we can skip the respective other barrier type as an
818+
* optimization.
809819
*/
810820
case BPF_ST | BPF_NOSPEC:
811-
switch (stf_barrier) {
812-
case STF_BARRIER_EIEIO:
813-
EMIT(PPC_RAW_EIEIO() | 0x02000000);
814-
break;
815-
case STF_BARRIER_SYNC_ORI:
821+
sync_emitted = false;
822+
ori31_emitted = false;
823+
#ifdef CONFIG_PPC_E500
824+
if (!bpf_jit_bypass_spec_v1()) {
825+
EMIT(PPC_RAW_ISYNC());
816826
EMIT(PPC_RAW_SYNC());
817-
EMIT(PPC_RAW_LD(tmp1_reg, _R13, 0));
818-
EMIT(PPC_RAW_ORI(_R31, _R31, 0));
819-
break;
820-
case STF_BARRIER_FALLBACK:
821-
ctx->seen |= SEEN_FUNC;
822-
PPC_LI64(_R12, dereference_kernel_function_descriptor(bpf_stf_barrier));
823-
EMIT(PPC_RAW_MTCTR(_R12));
824-
EMIT(PPC_RAW_BCTRL());
825-
break;
826-
case STF_BARRIER_NONE:
827-
break;
827+
sync_emitted = true;
828+
}
829+
#endif
830+
if (!bpf_jit_bypass_spec_v4()) {
831+
switch (stf_barrier) {
832+
case STF_BARRIER_EIEIO:
833+
EMIT(PPC_RAW_EIEIO() | 0x02000000);
834+
break;
835+
case STF_BARRIER_SYNC_ORI:
836+
if (!sync_emitted)
837+
EMIT(PPC_RAW_SYNC());
838+
EMIT(PPC_RAW_LD(tmp1_reg, _R13, 0));
839+
EMIT(PPC_RAW_ORI(_R31, _R31, 0));
840+
ori31_emitted = true;
841+
break;
842+
case STF_BARRIER_FALLBACK:
843+
ctx->seen |= SEEN_FUNC;
844+
PPC_LI64(_R12, dereference_kernel_function_descriptor(bpf_stf_barrier));
845+
EMIT(PPC_RAW_MTCTR(_R12));
846+
EMIT(PPC_RAW_BCTRL());
847+
break;
848+
case STF_BARRIER_NONE:
849+
break;
850+
}
828851
}
852+
#ifdef CONFIG_PPC_BOOK3S_64
853+
if (!bpf_jit_bypass_spec_v1() && !ori31_emitted)
854+
EMIT(PPC_RAW_ORI(_R31, _R31, 0));
855+
#endif
829856
break;
830857

831858
/*

include/linux/filter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ struct ctl_table_header;
8282
#define BPF_CALL_ARGS 0xe0
8383

8484
/* unused opcode to mark speculation barrier for mitigating
85-
* Speculative Store Bypass
85+
* Spectre v1 and v4
8686
*/
8787
#define BPF_NOSPEC 0xc0
8888

kernel/bpf/core.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,14 +2102,15 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
21022102
#undef COND_JMP
21032103
/* ST, STX and LDX*/
21042104
ST_NOSPEC:
2105-
/* Speculation barrier for mitigating Speculative Store Bypass.
2106-
* In case of arm64, we rely on the firmware mitigation as
2107-
* controlled via the ssbd kernel parameter. Whenever the
2108-
* mitigation is enabled, it works for all of the kernel code
2109-
* with no need to provide any additional instructions here.
2110-
* In case of x86, we use 'lfence' insn for mitigation. We
2111-
* reuse preexisting logic from Spectre v1 mitigation that
2112-
* happens to produce the required code on x86 for v4 as well.
2105+
/* Speculation barrier for mitigating Speculative Store Bypass,
2106+
* Bounds-Check Bypass and Type Confusion. In case of arm64, we
2107+
* rely on the firmware mitigation as controlled via the ssbd
2108+
* kernel parameter. Whenever the mitigation is enabled, it
2109+
* works for all of the kernel code with no need to provide any
2110+
* additional instructions here. In case of x86, we use 'lfence'
2111+
* insn for mitigation. We reuse preexisting logic from Spectre
2112+
* v1 mitigation that happens to produce the required code on
2113+
* x86 for v4 as well.
21132114
*/
21142115
barrier_nospec();
21152116
CONT;

0 commit comments

Comments
 (0)