]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
x86/bpf: Call branch history clearing sequence on exit
authorDaniel Sneddon <daniel.sneddon@linux.intel.com>
Mon, 5 May 2025 21:35:12 +0000 (14:35 -0700)
committerDave Hansen <dave.hansen@linux.intel.com>
Tue, 6 May 2025 15:18:32 +0000 (08:18 -0700)
Classic BPF programs have been identified as potential vectors for
intra-mode Branch Target Injection (BTI) attacks. Classic BPF programs can
be run by unprivileged users. They allow unprivileged code to execute
inside the kernel. Attackers can use unprivileged cBPF to craft branch
history in kernel mode that can influence the target of indirect branches.

Introduce a branch history buffer (BHB) clearing sequence during the JIT
compilation of classic BPF programs. The clearing sequence is the same as
is used in previous mitigations to protect syscalls. Since eBPF programs
already have their own mitigations in place, only insert the call on
classic programs that aren't run by privileged users.

Signed-off-by: Daniel Sneddon <daniel.sneddon@linux.intel.com>
Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>
arch/x86/net/bpf_jit_comp.c

index 9e5fe2ba858f08408b427e395eb64be9ee15cf29..6fb786c8b2aa380952c6937c724a98af1b43e8b0 100644 (file)
@@ -1502,6 +1502,30 @@ static void emit_priv_frame_ptr(u8 **pprog, void __percpu *priv_frame_ptr)
 #define PRIV_STACK_GUARD_SZ    8
 #define PRIV_STACK_GUARD_VAL   0xEB9F12345678eb9fULL
 
+static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,
+                                   struct bpf_prog *bpf_prog)
+{
+       u8 *prog = *pprog;
+       u8 *func;
+
+       if (cpu_feature_enabled(X86_FEATURE_CLEAR_BHB_LOOP)) {
+               /* The clearing sequence clobbers eax and ecx. */
+               EMIT1(0x50); /* push rax */
+               EMIT1(0x51); /* push rcx */
+               ip += 2;
+
+               func = (u8 *)clear_bhb_loop;
+               ip += x86_call_depth_emit_accounting(&prog, func, ip);
+
+               if (emit_call(&prog, func, ip))
+                       return -EINVAL;
+               EMIT1(0x59); /* pop rcx */
+               EMIT1(0x58); /* pop rax */
+       }
+       *pprog = prog;
+       return 0;
+}
+
 static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image,
                  int oldproglen, struct jit_context *ctx, bool jmp_padding)
 {
@@ -2544,6 +2568,13 @@ emit_jmp:
                        seen_exit = true;
                        /* Update cleanup_addr */
                        ctx->cleanup_addr = proglen;
+                       if (bpf_prog_was_classic(bpf_prog) &&
+                           !capable(CAP_SYS_ADMIN)) {
+                               u8 *ip = image + addrs[i - 1];
+
+                               if (emit_spectre_bhb_barrier(&prog, ip, bpf_prog))
+                                       return -EINVAL;
+                       }
                        if (bpf_prog->aux->exception_boundary) {
                                pop_callee_regs(&prog, all_callee_regs_used);
                                pop_r12(&prog);