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
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=
#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)
{
use_ibrs |= SPEC_CTRL_IBRS_SUPPORTED;
if (!ibrs_disabled)
- set_ibrs_inuse();
+ (void)set_ibrs_inuse();
}
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)
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;
#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)
{
use_ibpb |= SPEC_CTRL_IBPB_SUPPORTED;
if (!ibpb_disabled)
- set_ibpb_inuse();
+ (void)set_ibpb_inuse();
}
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 */
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)
{
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)
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)
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)
.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);