]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
x86/spectre: Add boot time option to select Spectre v2 mitigation
authorDavid Woodhouse <dwmw@amazon.co.uk>
Thu, 11 Jan 2018 21:46:26 +0000 (21:46 +0000)
committerJack Vogel <jack.vogel@oracle.com>
Wed, 7 Feb 2018 19:00:19 +0000 (11:00 -0800)
commit da285121560e769cc31797bba6422eea71d473e0 upstream.

Add a spectre_v2= option to select the mitigation used for the indirect
branch speculation vulnerability.

Currently, the only option available is retpoline, in its various forms.
This will be expanded to cover the new IBRS/IBPB microcode features.

The RETPOLINE_AMD feature relies on a serializing LFENCE for speculation
control. For AMD hardware, only set RETPOLINE_AMD if LFENCE is a
serializing instruction, which is indicated by the LFENCE_RDTSC feature.

[ tglx: Folded back the LFENCE/AMD fixes and reworked it so IBRS
   integration becomes simple ]

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: gnomes@lxorguk.ukuu.org.uk
Cc: Rik van Riel <riel@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: thomas.lendacky@amd.com
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Kees Cook <keescook@google.com>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@linux-foundation.org>
Cc: Paul Turner <pjt@google.com>
Link: https://lkml.kernel.org/r/1515707194-20531-5-git-send-email-dwmw@amazon.co.uk
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 9f789bc5711bcacb5df003594b992f0c1cc19df4)
Orabug: 27477743
CVE: CVE-2017-5715
Signed-off-by: Daniel Jordan <daniel.m.jordan@oracle.com>
Conflicts:
arch/x86/kernel/cpu/bugs_64.c
          (dmj:
            - patch used arch/x86/kernel/cpu/bugs.c
            - UEK4-specific IBPB and IBRS code merged with retpoline
              cmdline parsing
            - disabled IBRS in the case of full retpoline mitigation)
           (konrad:
            - disabled lfence on IBRS paths in case of full retpoline)
arch/x86/kernel/cpu/common.c
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Pavel Tatashin <pasha.tatashin@oracle.com>
Documentation/kernel-parameters.txt
arch/x86/include/asm/nospec-branch.h
arch/x86/kernel/cpu/bugs_64.c
arch/x86/kernel/cpu/common.c

index 1afb0d9106bdcda8ef79db930b2cb4a1acdd8fa4..0976a377bd0f27a5d9eac99f946a025a4e30e43d 100644 (file)
@@ -2393,6 +2393,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        nohugeiomap     [KNL,x86] Disable kernel huge I/O mappings.
 
+       nospectre_v2    [X86] Disable all mitigations for the Spectre variant 2
+                       (indirect branch prediction) vulnerability. System may
+                       allow data leaks with this option, which is equivalent
+                       to spectre_v2=off.
+
        noxsave         [BUGS=X86] Disables x86 extended register state save
                        and restore using xsave. The kernel will fallback to
                        enabling legacy floating-point and sse state.
@@ -3485,6 +3490,29 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        sonypi.*=       [HW] Sony Programmable I/O Control Device driver
                        See Documentation/laptops/sonypi.txt
 
+       spectre_v2=     [X86] Control mitigation of Spectre variant 2
+                       (indirect branch speculation) vulnerability.
+
+                       on   - unconditionally enable
+                       off  - unconditionally disable
+                       auto - kernel detects whether your CPU model is
+                              vulnerable
+
+                       Selecting 'on' will, and 'auto' may, choose a
+                       mitigation method at run time according to the
+                       CPU, the available microcode, the setting of the
+                       CONFIG_RETPOLINE configuration option, and the
+                       compiler with which the kernel was built.
+
+                       Specific mitigations can also be selected manually:
+
+                       retpoline         - replace indirect branches
+                       retpoline,generic - google's original retpoline
+                       retpoline,amd     - AMD-specific minimal thunk
+
+                       Not specifying this option is equivalent to
+                       spectre_v2=auto.
+
        spia_io_base=   [HW,MTD]
        spia_fio_base=
        spia_pedr=
index 5763548fb30b9f2f8cf76607a9d97e699ca2f488..fe48aeee79d1ebf5e370d3ea966c3f49cfe6f820 100644 (file)
 # define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
 #endif
 
+/* The Spectre V2 mitigation variants */
+enum spectre_v2_mitigation {
+       SPECTRE_V2_NONE,
+       SPECTRE_V2_RETPOLINE_MINIMAL,
+       SPECTRE_V2_RETPOLINE_MINIMAL_AMD,
+       SPECTRE_V2_RETPOLINE_GENERIC,
+       SPECTRE_V2_RETPOLINE_AMD,
+       SPECTRE_V2_IBRS,
+};
+
 #endif /* __ASSEMBLY__ */
 #endif /* __NOSPEC_BRANCH_H__ */
index aa1c948ca2a542fd387065fc629c6861114eee8a..09c8f0995c70835d3f8556418875624102895ee7 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/device.h>
 #endif
 #include <asm/alternative.h>
+#include <asm/nospec-branch.h>
+#include <asm/cmdline.h>
 #include <asm/bugs.h>
 #include <asm/processor.h>
 #include <asm/mtrr.h>
@@ -38,7 +40,7 @@ EXPORT_SYMBOL(use_ibpb);
 DEFINE_MUTEX(spec_ctrl_mutex);
 EXPORT_SYMBOL(spec_ctrl_mutex);
 
-static void __init spectre_v2_parse_cmdline(void);
+static void __init spectre_v2_select_mitigation(void);
 
 void __init check_bugs(void)
 {
@@ -47,6 +49,10 @@ void __init check_bugs(void)
        printk(KERN_INFO "CPU: ");
        print_cpu_info(&boot_cpu_data);
 #endif
+
+       /* Select the proper spectre mitigation before patching alternatives */
+       spectre_v2_select_mitigation();
+
        alternative_instructions();
 
        /*
@@ -59,24 +65,56 @@ void __init check_bugs(void)
         */
        if (!direct_gbpages)
                set_memory_4k((unsigned long)__va(0), 1);
+}
+
+/* The kernel command line selection */
+enum spectre_v2_mitigation_cmd {
+       SPECTRE_V2_CMD_NONE,
+       SPECTRE_V2_CMD_AUTO,
+       SPECTRE_V2_CMD_FORCE,
+       SPECTRE_V2_CMD_RETPOLINE,
+       SPECTRE_V2_CMD_RETPOLINE_GENERIC,
+       SPECTRE_V2_CMD_RETPOLINE_AMD,
+};
+
+static const char *spectre_v2_strings[] = {
+       [SPECTRE_V2_NONE]                       = "Vulnerable",
+       [SPECTRE_V2_RETPOLINE_MINIMAL]          = "Vulnerable: Minimal generic ASM retpoline",
+       [SPECTRE_V2_RETPOLINE_MINIMAL_AMD]      = "Vulnerable: Minimal AMD ASM retpoline",
+       [SPECTRE_V2_RETPOLINE_GENERIC]          = "Mitigation: Full generic retpoline",
+       [SPECTRE_V2_RETPOLINE_AMD]              = "Mitigation: Full AMD retpoline",
+};
 
-       spectre_v2_parse_cmdline();
+#undef pr_fmt
+#define pr_fmt(fmt)     "Spectre V2 mitigation: " fmt
+
+static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
+
+static void __init spec2_print_if_insecure(const char *reason)
+{
+       if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+               pr_info("%s\n", reason);
 }
 
-static inline bool match_option(const char *arg, int arglen, const char *opt)
+static void __init spec2_print_if_secure(const char *reason)
 {
-       int len = strlen(opt);
+       if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+               pr_info("%s\n", reason);
+}
 
-       return len == arglen && !strncmp(arg, opt, len);
+static inline bool retp_compiler(void)
+{
+       return __is_defined(RETPOLINE);
 }
 
-static void spectre_v2_usage_error(const char *str)
+static inline bool match_option(const char *arg, int arglen, const char *opt)
 {
-       pr_warn("%s arguments for option spectre_v2. "
-                   "Usage spectre_v2={on|off|auto}\n", str);
+       int len = strlen(opt);
+
+       return len == arglen && !strncmp(arg, opt, len);
 }
 
-static void __init spectre_v2_parse_cmdline(void)
+static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
 {
        char arg[20];
        int ret;
@@ -93,34 +131,115 @@ static void __init spectre_v2_parse_cmdline(void)
                set_lfence_disabled();
        }
 
+       ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
+                                 sizeof(arg));
+       if (ret > 0)  {
+               if (match_option(arg, ret, "off")) {
+                       goto disable;
+               } else if (match_option(arg, ret, "on")) {
+                       spec2_print_if_secure("force enabled on command line.");
+                       return SPECTRE_V2_CMD_FORCE;
+               } else if (match_option(arg, ret, "retpoline")) {
+                       spec2_print_if_insecure("retpoline selected on command line.");
+                       return SPECTRE_V2_CMD_RETPOLINE;
+               } else if (match_option(arg, ret, "retpoline,amd")) {
+                       if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) {
+                               pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
+                               return SPECTRE_V2_CMD_AUTO;
+                       }
+                       spec2_print_if_insecure("AMD retpoline selected on command line.");
+                       return SPECTRE_V2_CMD_RETPOLINE_AMD;
+               } else if (match_option(arg, ret, "retpoline,generic")) {
+                       spec2_print_if_insecure("generic retpoline selected on command line.");
+                       return SPECTRE_V2_CMD_RETPOLINE_GENERIC;
+               } else if (match_option(arg, ret, "auto")) {
+                       return SPECTRE_V2_CMD_AUTO;
+               }
+       }
+
+       if (!cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
+               return SPECTRE_V2_CMD_AUTO;
+disable:
+       spec2_print_if_insecure("disabled on command line.");
+       return SPECTRE_V2_CMD_NONE;
+}
 
-       if (cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
-               goto disable;
+static void __init spectre_v2_select_mitigation(void)
+{
+       enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
+       enum spectre_v2_mitigation mode = SPECTRE_V2_NONE;
 
-       ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
-           sizeof(arg));
+       /*
+        * If the CPU is not affected and the command line mode is NONE or AUTO
+        * then nothing to do.
+        */
+       if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2) &&
+           (cmd == SPECTRE_V2_CMD_NONE || cmd == SPECTRE_V2_CMD_AUTO)) {
+               set_ibrs_disabled();
+               set_ibpb_disabled();
+               set_lfence_disabled();
+               return;
+       }
+
+       switch (cmd) {
+       case SPECTRE_V2_CMD_NONE:
+               set_ibrs_disabled();
+               set_ibpb_disabled();
+               set_lfence_disabled();
+               return;
 
-       if (ret > 0) {
+       case SPECTRE_V2_CMD_FORCE:
+               /* FALLTRHU */
+       case SPECTRE_V2_CMD_AUTO:
+               goto retpoline_auto;
 
-               if (match_option(arg, ret, "off"))
-                       goto disable;
+       case SPECTRE_V2_CMD_RETPOLINE_AMD:
+               if (IS_ENABLED(CONFIG_RETPOLINE))
+                       goto retpoline_amd;
+               break;
+       case SPECTRE_V2_CMD_RETPOLINE_GENERIC:
+               if (IS_ENABLED(CONFIG_RETPOLINE))
+                       goto retpoline_generic;
+               break;
+       case SPECTRE_V2_CMD_RETPOLINE:
+               if (IS_ENABLED(CONFIG_RETPOLINE))
+                       goto retpoline_auto;
+               break;
+       }
+       pr_err("kernel not compiled with retpoline; retpoline mitigation not available");
+       return;
 
-               if (match_option(arg, ret, "on") ||
-                       match_option(arg, ret, "auto")) {
-                       if (!(ibrs_supported || ibpb_supported))
-                               pr_warn("Spectre_v2 mitigation unsupported\n");
-               } else {
-                       spectre_v2_usage_error("Invalid");
+retpoline_auto:
+       if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
+       retpoline_amd:
+               if (!boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
+                       pr_err("LFENCE not serializing. Switching to generic retpoline\n");
+                       goto retpoline_generic;
                }
+               mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_AMD :
+                                        SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
+               setup_force_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
+               setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
+       } else {
+       retpoline_generic:
+               mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC :
+                                        SPECTRE_V2_RETPOLINE_MINIMAL;
+               setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
        }
 
-       return;
+       spectre_v2_enabled = mode;
+       pr_info("%s\n", spectre_v2_strings[mode]);
 
-disable:
-       set_ibrs_disabled();
-       set_ibpb_disabled();
+       /* IBRS is unnecessary with retpoline mitigation. */
+       if (mode == SPECTRE_V2_RETPOLINE_GENERIC ||
+           mode == SPECTRE_V2_RETPOLINE_AMD) {
+               set_ibrs_disabled();
+               set_lfence_disabled();
+       }
 }
 
+#undef pr_fmt
+
 #ifdef CONFIG_SYSFS
 ssize_t cpu_show_meltdown(struct device *dev,
                          struct device_attribute *attr, char *buf)
@@ -146,12 +265,10 @@ ssize_t cpu_show_spectre_v2(struct device *dev,
 {
        if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
                return sprintf(buf, "Not affected\n");
-       if (ibrs_inuse || ibpb_inuse || lfence_inuse)
-               return sprintf(buf, "Mitigation: %s%s\n",
-                               ibrs_inuse ? "IBRS " :
-                                       lfence_inuse ? "lfence " : "",
-                               ibpb_inuse ? "IBPB" : "");
 
-       return sprintf(buf, "Vulnerable\n");
+       return sprintf(buf, "%s%s%s\n", spectre_v2_strings[spectre_v2_enabled],
+                                       ibrs_inuse ? ", IBRS" :
+                                               lfence_inuse ? " lfence " : "",
+                                       ibpb_inuse ? ", IBPB" : "");
 }
 #endif
index abf5f3cadf4362d967ba3e5bad2781442eaa44e8..b4b794f1102204c1a2da9de63e6b0b6dd207b798 100644 (file)
@@ -843,10 +843,6 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
 
        setup_force_cpu_cap(X86_FEATURE_ALWAYS);
 
-#ifdef CONFIG_RETPOLINE
-       setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
-#endif
-
 #ifdef CONFIG_X86_32
        /*
         * Regardless of whether PCID is enumerated, the SDM says