From: Chuck Anderson Date: Mon, 12 Feb 2018 07:49:30 +0000 (-0800) Subject: retpoline/module: fall back to another spectre mitigation when disabling retpoline X-Git-Tag: v4.1.12-124.31.3~1112 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=bc434425cfbba9c961dbe1e102852e5f361ba1a9;p=users%2Fjedix%2Flinux-maple.git retpoline/module: fall back to another spectre mitigation when disabling retpoline 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 Signed-off-by: Chuck Anderson Reviewed-by: Todd Vierling Signed-off-by: Brian Maly Conflicts: Documentation/kernel-parameters.txt arch/x86/include/asm/spec_ctrl.h arch/x86/kernel/cpu/spec_ctrl.c --- diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index dfc2bfba21038..a3d5b9dadb00f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -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= diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h index f0e75ffab225b..17bd0916164a9 100644 --- a/arch/x86/include/asm/spec_ctrl.h +++ b/arch/x86/include/asm/spec_ctrl.h @@ -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 */ diff --git a/arch/x86/kernel/cpu/bugs_64.c b/arch/x86/kernel/cpu/bugs_64.c index 3eeff1bcd364d..2a4f6c1234db1 100644 --- a/arch/x86/kernel/cpu/bugs_64.c +++ b/arch/x86/kernel/cpu/bugs_64.c @@ -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) diff --git a/arch/x86/kernel/cpu/spec_ctrl.c b/arch/x86/kernel/cpu/spec_ctrl.c index a880b70656204..8f664e6387ff4 100644 --- a/arch/x86/kernel/cpu/spec_ctrl.c +++ b/arch/x86/kernel/cpu/spec_ctrl.c @@ -10,8 +10,10 @@ 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);