]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
retpoline/module: fall back to another spectre mitigation when disabling retpoline
authorChuck Anderson <chuck.anderson@oracle.com>
Mon, 12 Feb 2018 07:49:30 +0000 (23:49 -0800)
committerChuck Anderson <chuck.anderson@oracle.com>
Sat, 3 Mar 2018 01:53:06 +0000 (17:53 -0800)
Commit ("retpoline/module: Taint kernel for missing retpoline in module")
calls the new function disable_retpoline() when check_modinfo() determines
that a LKM being loaded was not compiled with retpoline.

This commit adds code to disable_retpoline() that attempts to fall back to
the Spectre v2 mitigations IBRS/IBPB when disabling retpoline.

Pseudocode for disable_retpoline():

if retpoline is not enabled
No messages/changes
return

if we are allowed to fall back to another mitigation
if IBRS is not in use
if we enabled it
spectre_v2_enabled = SPECTRE_V2_IBRS
pr_err("Spectre v2 mitigation set to IBRS.\n")
if we enabled IBPB mitigation
pr_err("Spectre v2 mitigation IBPB enabled.\n")
else
pr_err("Could not enable IBRS.\n")
spectre_v2_enabled = SPECTRE_V2_NONE
pr_err("No Spectre v2 mitigation to fall back to.\n")
else
spectre_v2_enabled = SPECTRE_V2_IBRS;
pr_err("Spectre v2 mitigation IBRS is set.\n")
else
spectre_v2_enabled = SPECTRE_V2_NONE;
pr_err("Cannot choose another Spectre v2 mitigation because retpoline_fallback is off.\n")

if spectre_v2_enabled == SPECTRE_V2_NONE
pr_err("system may be vulnerable to spectre\n")

The attempt to fall back can be disabled with the new kernel boot parameter
spectre_v2_heuristics=[retpoline_fallback=off]
Disabling retpoline fallback can also be done through debugfs
"retpoline_fallback".

Orabug: 27457549

Suggested-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Chuck Anderson <chuck.anderson@oracle.com>
Reviewed-by: Todd Vierling <todd.vierling@oracle.com>
Signed-off-by: Brian Maly <brian.maly@oracle.com>
Conflicts:
Documentation/kernel-parameters.txt
arch/x86/include/asm/spec_ctrl.h
arch/x86/kernel/cpu/spec_ctrl.c

Documentation/kernel-parameters.txt
arch/x86/include/asm/spec_ctrl.h
arch/x86/kernel/cpu/bugs_64.c
arch/x86/kernel/cpu/spec_ctrl.c

index dfc2bfba2103828480e017d3e7ad2540e1bc5db3..a3d5b9dadb00fdde1cb6a4a95170629201841b55 100644 (file)
@@ -3516,6 +3516,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        skylake=off     - do not use IBRS if present on Skylake
                                          instead of retpoline (this is equivalant
                                          to spectre_v2=retpoline,generic).
+                       retpoline_fallback=off - don't fall back to another
+                                                mitigation if a module is
+                                                loaded that was not compiled
+                                                with retpoline.
 
        spia_io_base=   [HW,MTD]
        spia_fio_base=
index f0e75ffab225b3cd8e7b7327d0e176ad1cdb1a5a..17bd0916164a94e8a3e042fe665ca656d4d03322 100644 (file)
@@ -218,10 +218,14 @@ extern void unprotected_firmware_end(void);
 
 #define ibrs_inuse             (check_ibrs_inuse())
 
-static inline void set_ibrs_inuse(void)
+static inline bool set_ibrs_inuse(void)
 {
-       if (ibrs_supported)
+       if (ibrs_supported) {
                use_ibrs |= SPEC_CTRL_IBRS_INUSE;
+               return true;
+       } else {
+               return false;
+       }
 }
 
 static inline void clear_ibrs_inuse(void)
@@ -243,7 +247,7 @@ static inline void set_ibrs_supported(void)
 {
        use_ibrs |= SPEC_CTRL_IBRS_SUPPORTED;
        if (!ibrs_disabled)
-               set_ibrs_inuse();
+               (void)set_ibrs_inuse();
 }
 
 static inline void set_ibrs_disabled(void)
@@ -255,6 +259,14 @@ static inline void set_ibrs_disabled(void)
        sysctl_ibrs_enabled = ibrs_inuse ? 1 : 0;
 }
 
+static inline void clear_ibrs_disabled(void)
+{
+       use_ibrs &= ~SPEC_CTRL_IBRS_ADMIN_DISABLED;
+       (void)set_ibrs_inuse();
+       /* Update what sysfs shows. */
+       sysctl_ibrs_enabled = ibrs_inuse ? 1 : 0;
+}
+
 static inline void set_ibrs_firmware(void)
 {
        if (ibrs_supported)
@@ -266,14 +278,6 @@ static inline void disable_ibrs_firmware(void)
        use_ibrs &= ~SPEC_CTRL_IBRS_FIRMWARE;
 }
 
-static inline void clear_ibrs_disabled(void)
-{
-       use_ibrs &= ~SPEC_CTRL_IBRS_ADMIN_DISABLED;
-       set_ibrs_inuse();
-       /* Update what sysfs shows. */
-       sysctl_ibrs_enabled = ibrs_inuse ? 1 : 0;
-}
-
 /* indicate usage of IBPB to control execution speculation */
 extern int use_ibpb;
 extern u32 sysctl_ibpb_enabled;
@@ -287,10 +291,14 @@ extern u32 sysctl_ibpb_enabled;
 
 #define ibpb_inuse             (check_ibpb_inuse())
 
-static inline void set_ibpb_inuse(void)
+static inline bool set_ibpb_inuse(void)
 {
-       if (ibpb_supported)
+       if (ibpb_supported) {
                use_ibpb |= SPEC_CTRL_IBPB_INUSE;
+               return true;
+       } else {
+               return false;
+       }
 }
 
 static inline void clear_ibpb_inuse(void)
@@ -312,7 +320,7 @@ static inline void set_ibpb_supported(void)
 {
        use_ibpb |= SPEC_CTRL_IBPB_SUPPORTED;
        if (!ibpb_disabled)
-               set_ibpb_inuse();
+               (void)set_ibpb_inuse();
 }
 
 static inline void set_ibpb_disabled(void)
@@ -327,10 +335,41 @@ static inline void set_ibpb_disabled(void)
 static inline void clear_ibpb_disabled(void)
 {
        use_ibpb &= ~SPEC_CTRL_IBPB_ADMIN_DISABLED;
-       set_ibpb_inuse();
+       (void)set_ibpb_inuse();
        /* Update what sysfs shows. */
        sysctl_ibpb_enabled = ibpb_inuse ? 1 : 0;
 }
 
+/*
+ * Allow/disallow falling back to another Spectre v2 mitigation
+ * when disabling retpoline.
+ */
+
+extern int retpoline_fallback;
+extern u32 sysctl_retpoline_fallback;
+
+/* Flags for retpoline_fallback: */
+#define SPEC_CTRL_USE_RETPOLINE_FALLBACK (1<<0)        /* pick new mitigation */
+                                               /* when disabling retpoline */
+
+#define allow_retpoline_fallback (retpoline_fallback &                 \
+                                 SPEC_CTRL_USE_RETPOLINE_FALLBACK)
+
+static inline void set_retpoline_fallback(void)
+{
+       retpoline_fallback |= SPEC_CTRL_USE_RETPOLINE_FALLBACK;
+
+       /* Update what sysfs shows. */
+       sysctl_retpoline_fallback = 1;
+}
+
+static inline void clear_retpoline_fallback(void)
+{
+       retpoline_fallback &= ~SPEC_CTRL_USE_RETPOLINE_FALLBACK;
+
+       /* Update what sysfs shows. */
+       sysctl_retpoline_fallback = 0;
+}
+
 #endif /* __ASSEMBLY__ */
 #endif /* _ASM_X86_SPEC_CTRL_H */
index 3eeff1bcd364dae227ae6aed1416a7014297550c..2a4f6c1234db1251c739f40b9cb29bf5d9c7febc 100644 (file)
@@ -45,6 +45,13 @@ EXPORT_SYMBOL(spec_ctrl_mutex);
 bool use_ibrs_on_skylake = true;
 EXPORT_SYMBOL(use_ibrs_on_skylake);
 
+/*
+ * retpoline_fallback flags:
+ * SPEC_CTRL_USE_RETPOLINE_FALLBACK    pick retpoline fallback mitigation
+ */
+int retpoline_fallback = SPEC_CTRL_USE_RETPOLINE_FALLBACK;
+EXPORT_SYMBOL(retpoline_fallback);
+
 
 int __init spectre_v2_heuristics_setup(char *p)
 {
@@ -66,6 +73,16 @@ int __init spectre_v2_heuristics_setup(char *p)
                        if (!strncmp(p, "off", 3))
                                use_ibrs_on_skylake = false;
                }
+               len = strlen("retpoline_fallback");
+               if (!strncmp(p, "retpoline_fallback", len)) {
+                       p += len;
+                       if (*p == '=')
+                               ++p;
+                       if (*p == '\0')
+                               break;
+                       if (!strncmp(p, "off", 3))
+                               clear_retpoline_fallback();
+               }
 
                p = strpbrk(p, ",");
                if (!p)
@@ -146,11 +163,53 @@ static const char *spectre_v2_strings[] = {
 
 static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
 
-/* A module has been loaded. Disable reporting that we're good. */
+/*
+ * Disable retpoline and attempt to fall back to another Spectre v2 mitigation.
+ * If possible, fall back to IBRS and IBPB.
+ * Failing that, indicate that we have no Spectre v2 mitigation.
+ *
+ * Obtains spec_ctrl_mutex before checking/changing Spectre v2 mitigation
+ * state.
+ */
 void disable_retpoline(void)
 {
-       spectre_v2_enabled = SPECTRE_V2_NONE;
-       pr_err("system may be vulnerable to spectre\n");
+       mutex_lock(&spec_ctrl_mutex);
+
+       if (retpoline_enabled()) {
+               pr_err("Disabling Spectre v2 mitigation retpoline.\n");
+       } else {
+               /* retpoline is not enabled.  Return */
+               mutex_unlock(&spec_ctrl_mutex);
+               return;
+       }
+
+       if (allow_retpoline_fallback) {
+               if (!ibrs_inuse) {
+                       /* try to enable ibrs */
+                       if (set_ibrs_inuse()) {
+                               pr_err("Spectre v2 mitigation set to IBRS.\n");
+                               spectre_v2_enabled = SPECTRE_V2_IBRS;
+                               if (!ibpb_inuse && set_ibpb_inuse()) {
+                                       pr_err("Spectre v2 mitigation IBPB enabled.\n");
+                               }
+                       } else {
+                               pr_err("Could not enable IBRS.\n");
+                               pr_err("No Spectre v2 mitigation to fall back to.\n");
+                               spectre_v2_enabled = SPECTRE_V2_NONE;
+                       }
+               } else {
+                       pr_err("Spectre v2 mitigation IBRS is set.\n");
+                       spectre_v2_enabled = SPECTRE_V2_IBRS;
+               }
+       } else {
+               pr_err("Cannot choose another Spectre v2 mitigation because retpoline_fallback is off.\n");
+               spectre_v2_enabled = SPECTRE_V2_NONE;
+       }
+
+       if (spectre_v2_enabled == SPECTRE_V2_NONE)
+               pr_err("system may be vulnerable to spectre\n");
+
+       mutex_unlock(&spec_ctrl_mutex);
 }
 
 bool retpoline_enabled(void)
index a880b70656204891278560edc90e0df8150687c2..8f664e6387ff4c3d7c375fbab5a6235f11948c40 100644 (file)
 
 u32 sysctl_ibrs_enabled;
 u32 sysctl_ibpb_enabled;
+u32 sysctl_retpoline_fallback = 1;     /* enabled by default */
 EXPORT_SYMBOL(sysctl_ibrs_enabled);
 EXPORT_SYMBOL(sysctl_ibpb_enabled);
+EXPORT_SYMBOL(sysctl_retpoline_fallback);
 
 static ssize_t __enabled_read(struct file *file, char __user *user_buf,
                               size_t count, loff_t *ppos, unsigned int *field)
@@ -143,12 +145,64 @@ static const struct file_operations fops_ibpb_enabled = {
        .llseek = default_llseek,
 };
 
+static ssize_t retpoline_fallback_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       return __enabled_read(file, user_buf, count, ppos,
+                             &sysctl_retpoline_fallback);
+}
+
+static ssize_t retpoline_fallback_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       char buf[32];
+       ssize_t len;
+       unsigned int enable;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtouint(buf, 0, &enable))
+               return -EINVAL;
+
+       /* Only 0 and 1 are allowed */
+       if (enable > 1)
+               return -EINVAL;
+
+       mutex_lock(&spec_ctrl_mutex);
+
+       if (enable == allow_retpoline_fallback) {
+               /* No change to current state.  Return. */
+               mutex_unlock(&spec_ctrl_mutex);
+               return count;
+       }
+
+       if (enable)
+               set_retpoline_fallback();
+       else
+               clear_retpoline_fallback();
+
+       mutex_unlock(&spec_ctrl_mutex);
+       return count;
+}
+
+static const struct file_operations fops_retpoline_fallback = {
+       .read = retpoline_fallback_read,
+       .write = retpoline_fallback_write,
+       .llseek = default_llseek,
+};
+
 static int __init debugfs_spec_ctrl(void)
 {
         debugfs_create_file("ibrs_enabled", S_IRUSR | S_IWUSR,
                             arch_debugfs_dir, NULL, &fops_ibrs_enabled);
        debugfs_create_file("ibpb_enabled", S_IRUSR | S_IWUSR,
                            arch_debugfs_dir, NULL, &fops_ibpb_enabled);
+       debugfs_create_file("retpoline_fallback", S_IRUSR | S_IWUSR,
+                            arch_debugfs_dir, NULL, &fops_retpoline_fallback);
         return 0;
 }
 late_initcall(debugfs_spec_ctrl);