x86/CPU/AMD: Do not leak quotient data after a division by 0
authorBorislav Petkov (AMD) <bp@alien8.de>
Fri, 4 Aug 2023 22:06:43 +0000 (00:06 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 9 Aug 2023 14:55:00 +0000 (07:55 -0700)
Under certain circumstances, an integer division by 0 which faults, can
leave stale quotient data from a previous division operation on Zen1
microarchitectures.

Do a dummy division 0/1 before returning from the #DE exception handler
in order to avoid any leaks of potentially sensitive data.

Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Cc: <stable@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/x86/include/asm/cpufeatures.h
arch/x86/include/asm/processor.h
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/traps.c

index 094f88f..b69b0d7 100644 (file)
 
 /* BUG word 2 */
 #define X86_BUG_SRSO                   X86_BUG(1*32 + 0) /* AMD SRSO bug */
+#define X86_BUG_DIV0                   X86_BUG(1*32 + 1) /* AMD DIV0 speculation bug */
 #endif /* _ASM_X86_CPUFEATURES_H */
index 7c67db7..973db04 100644 (file)
@@ -683,10 +683,12 @@ extern u16 get_llc_id(unsigned int cpu);
 extern u32 amd_get_nodes_per_socket(void);
 extern u32 amd_get_highest_perf(void);
 extern bool cpu_has_ibpb_brtype_microcode(void);
+extern void amd_clear_divider(void);
 #else
 static inline u32 amd_get_nodes_per_socket(void)       { return 0; }
 static inline u32 amd_get_highest_perf(void)           { return 0; }
 static inline bool cpu_has_ibpb_brtype_microcode(void) { return false; }
+static inline void amd_clear_divider(void)             { }
 #endif
 
 extern unsigned long arch_align_stack(unsigned long sp);
index 1e1e253..b55d8f8 100644 (file)
@@ -75,6 +75,10 @@ static const int amd_zenbleed[] =
                           AMD_MODEL_RANGE(0x17, 0x60, 0x0, 0x7f, 0xf),
                           AMD_MODEL_RANGE(0x17, 0xa0, 0x0, 0xaf, 0xf));
 
+static const int amd_div0[] =
+       AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x17, 0x00, 0x0, 0x2f, 0xf),
+                          AMD_MODEL_RANGE(0x17, 0x50, 0x0, 0x5f, 0xf));
+
 static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
 {
        int osvw_id = *erratum++;
@@ -1130,6 +1134,11 @@ static void init_amd(struct cpuinfo_x86 *c)
                WARN_ON_ONCE(msr_set_bit(MSR_EFER, _EFER_AUTOIBRS));
 
        zenbleed_check(c);
+
+       if (cpu_has_amd_erratum(c, amd_div0)) {
+               pr_notice_once("AMD Zen1 DIV0 bug detected. Disable SMT for full protection.\n");
+               setup_force_cpu_bug(X86_BUG_DIV0);
+       }
 }
 
 #ifdef CONFIG_X86_32
@@ -1309,3 +1318,13 @@ bool cpu_has_ibpb_brtype_microcode(void)
                return false;
        }
 }
+
+/*
+ * Issue a DIV 0/1 insn to clear any division data from previous DIV
+ * operations.
+ */
+void noinstr amd_clear_divider(void)
+{
+       asm volatile(ALTERNATIVE("", "div %2\n\t", X86_BUG_DIV0)
+                    :: "a" (0), "d" (0), "r" (1));
+}
index 4a817d2..1885326 100644 (file)
@@ -206,6 +206,8 @@ DEFINE_IDTENTRY(exc_divide_error)
 {
        do_error_trap(regs, 0, "divide error", X86_TRAP_DE, SIGFPE,
                      FPE_INTDIV, error_get_trap_addr(regs));
+
+       amd_clear_divider();
 }
 
 DEFINE_IDTENTRY(exc_overflow)