The diff below improves our resiliency against "variant 2".  Like on
x86 the defence is based on flushing the branch predictor cache at the
appropriate points.

It turns out we are already in pretty good shape as we are already
flushing on context switches.  I believe we're forced to do that
because we don't use ASIDs.  The other place that matters seems to be
when trapping because userland tried to access the kernel part of the
address space in an attempt to "train" the branch predictor.  I don't
pretend to fully understand the details but it makes some sense to me.

Now flushing the branch predictor cache is slightly tricky.  In
principle the architecture defines an instruction to do the flush.
But it is "ineffective" on Cortex-A15 (and Cortex-A57 and Cortex-A72
which are based on the same design and could in principle be run in
32-bit mode).  The following link gives a good summary:

https://github.com/ARM-software/arm-trusted-firmware/wiki/ARM-Trusted-Firmware-Security-Advisory-TFV-6

and seems to be consistent with what they did in the Linux kernel:

https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/log/?h=kpti

After some thinking I decided that the default should probably be to
use BPIALL as that is the architected instruction and most likely to
work correctly on future processors as the Cortex-A15/A57/A72 design
seems to have been phased out in favour of a newer design.

I deliberately moved the hook into "struct cpuinfo" because big.LITTLE
designs often pair vulnerable and non-vulnerable cores.

ok?


Index: arch/arm/arm/cpu.c
===================================================================
RCS file: /cvs/src/sys/arch/arm/arm/cpu.c,v
retrieving revision 1.43
diff -u -p -r1.43 cpu.c
--- arch/arm/arm/cpu.c  29 Dec 2017 14:45:15 -0000      1.43
+++ arch/arm/arm/cpu.c  13 Jan 2018 22:24:08 -0000
@@ -257,6 +257,42 @@ identify_arm_cpu(struct device *dv, stru
 
        printf("\n");
 
+       /*
+        * Some ARM processors are vulnerable to branch target
+        * injection attacks.
+        */
+       switch (cpuid & CPU_ID_CORTEX_MASK) {
+       case CPU_ID_CORTEX_A5:
+       case CPU_ID_CORTEX_A7:
+       case CPU_ID_CORTEX_A32:
+       case CPU_ID_CORTEX_A35:
+       case CPU_ID_CORTEX_A53:
+       case CPU_ID_CORTEX_A55:
+               /* Not vulnerable; no need to flush. */
+               ci->ci_flush_bp = cpufunc_nullop;
+               break;
+       case CPU_ID_CORTEX_A8:
+       case CPU_ID_CORTEX_A9:
+       case CPU_ID_CORTEX_A12:
+       case CPU_ID_CORTEX_A17:
+       case CPU_ID_CORTEX_A73:
+       case CPU_ID_CORTEX_A75:
+       default:
+               /* Vulnerable; flush BP cache. */
+               ci->ci_flush_bp = armv7_flush_bp;
+               break;
+       case CPU_ID_CORTEX_A15:
+       case CPU_ID_CORTEX_A57:
+       case CPU_ID_CORTEX_A72:
+               /*
+                * Vulnerable; BPIALL is "not effective" so must use
+                * ICIALLU and hope the firmware set the magic bit in
+                * the ACTLR that actually forces a BTB flush.
+                */
+               ci->ci_flush_bp = cortex_a15_flush_bp;
+               break;
+       }
+
        /* Print cache info. */
        if (arm_picache_line_size == 0 && arm_pdcache_line_size == 0)
                goto skip_pcache;
Index: arch/arm/arm/cpufunc_asm_armv7.S
===================================================================
RCS file: /cvs/src/sys/arch/arm/arm/cpufunc_asm_armv7.S,v
retrieving revision 1.14
diff -u -p -r1.14 cpufunc_asm_armv7.S
--- arch/arm/arm/cpufunc_asm_armv7.S    15 Aug 2016 21:08:56 -0000      1.14
+++ arch/arm/arm/cpufunc_asm_armv7.S    13 Jan 2018 22:24:08 -0000
@@ -209,6 +209,17 @@ ENTRY(armv7_dcache_inv_range)
 
 
 /*
+ * BTB functions.
+ */
+ENTRY(armv7_flush_bp)
+       mcr     CP15_BPIALL
+       mov     pc, lr
+
+ENTRY(cortex_a15_flush_bp)
+       mcr     CP15_ICIALLU            /* Heavy hammer; BPIALL is a no-op */
+       mov     pc, lr
+
+/*
  * Context switch.
  *
  * These is the CPU-specific parts of the context switcher cpu_switch()
Index: arch/arm/arm/fault.c
===================================================================
RCS file: /cvs/src/sys/arch/arm/arm/fault.c,v
retrieving revision 1.30
diff -u -p -r1.30 fault.c
--- arch/arm/arm/fault.c        8 Sep 2017 05:36:51 -0000       1.30
+++ arch/arm/arm/fault.c        13 Jan 2018 22:24:08 -0000
@@ -209,6 +209,15 @@ data_abort_handler(trapframe_t *tf)
                goto out;
        }
 
+       va = trunc_page((vaddr_t)far);
+
+       /*
+        * Flush BP cache on processors that are vulnerable to branch
+        * target injection attacks if access is outside user space.
+        */
+       if (va < VM_MIN_ADDRESS || va >= VM_MAX_ADDRESS)
+               curcpu()->ci_flush_bp();
+
        /*
         * At this point, we're dealing with one of the following data aborts:
         *
@@ -255,8 +264,6 @@ data_abort_handler(trapframe_t *tf)
                    "Program Counter\n");
                dab_fatal(tf, fsr, far, p, NULL);
        }
-
-       va = trunc_page((vaddr_t)far);
 
        /*
         * It is only a kernel address space fault iff:
Index: arch/arm/include/cpu.h
===================================================================
RCS file: /cvs/src/sys/arch/arm/include/cpu.h,v
retrieving revision 1.48
diff -u -p -r1.48 cpu.h
--- arch/arm/include/cpu.h      12 Aug 2017 13:18:48 -0000      1.48
+++ arch/arm/include/cpu.h      13 Jan 2018 22:24:08 -0000
@@ -206,6 +206,8 @@ struct cpu_info {
 #ifdef GPROF
        struct gmonparam *ci_gmon;
 #endif
+
+       void (*ci_flush_bp)(void);
 };
 
 extern struct cpu_info cpu_info_primary;
Index: arch/arm/include/cpufunc.h
===================================================================
RCS file: /cvs/src/sys/arch/arm/include/cpufunc.h,v
retrieving revision 1.29
diff -u -p -r1.29 cpufunc.h
--- arch/arm/include/cpufunc.h  6 Jan 2017 00:06:02 -0000       1.29
+++ arch/arm/include/cpufunc.h  13 Jan 2018 22:24:08 -0000
@@ -231,6 +231,9 @@ void        armv7_tlb_flushI        (void);
 void   armv7_tlb_flushD        (void);
 void   armv7_tlb_flushD_SE     (u_int va);
 
+void   armv7_flush_bp(void);
+void   cortex_a15_flush_bp(void);
+
 void   armv7_drain_writebuf    (void);
 void   armv7_cpu_sleep         (int mode);
 

Reply via email to