| From aa7f67713382c74acfbc1783cf21af553426af1f Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 19 May 2020 06:40:42 -0700 |
| Subject: x86/speculation: Avoid force-disabling IBPB based on STIBP and |
| enhanced IBRS. |
| |
| From: Anthony Steinhauser <asteinhauser@google.com> |
| |
| [ Upstream commit 21998a351512eba4ed5969006f0c55882d995ada ] |
| |
| When STIBP is unavailable or enhanced IBRS is available, Linux |
| force-disables the IBPB mitigation of Spectre-BTB even when simultaneous |
| multithreading is disabled. While attempts to enable IBPB using |
| prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_INDIRECT_BRANCH, ...) fail with |
| EPERM, the seccomp syscall (or its prctl(PR_SET_SECCOMP, ...) equivalent) |
| which are used e.g. by Chromium or OpenSSH succeed with no errors but the |
| application remains silently vulnerable to cross-process Spectre v2 attacks |
| (classical BTB poisoning). At the same time the SYSFS reporting |
| (/sys/devices/system/cpu/vulnerabilities/spectre_v2) displays that IBPB is |
| conditionally enabled when in fact it is unconditionally disabled. |
| |
| STIBP is useful only when SMT is enabled. When SMT is disabled and STIBP is |
| unavailable, it makes no sense to force-disable also IBPB, because IBPB |
| protects against cross-process Spectre-BTB attacks regardless of the SMT |
| state. At the same time since missing STIBP was only observed on AMD CPUs, |
| AMD does not recommend using STIBP, but recommends using IBPB, so disabling |
| IBPB because of missing STIBP goes directly against AMD's advice: |
| https://developer.amd.com/wp-content/resources/Architecture_Guidelines_Update_Indirect_Branch_Control.pdf |
| |
| Similarly, enhanced IBRS is designed to protect cross-core BTB poisoning |
| and BTB-poisoning attacks from user space against kernel (and |
| BTB-poisoning attacks from guest against hypervisor), it is not designed |
| to prevent cross-process (or cross-VM) BTB poisoning between processes (or |
| VMs) running on the same core. Therefore, even with enhanced IBRS it is |
| necessary to flush the BTB during context-switches, so there is no reason |
| to force disable IBPB when enhanced IBRS is available. |
| |
| Enable the prctl control of IBPB even when STIBP is unavailable or enhanced |
| IBRS is available. |
| |
| Fixes: 7cc765a67d8e ("x86/speculation: Enable prctl mode for spectre_v2_user") |
| Signed-off-by: Anthony Steinhauser <asteinhauser@google.com> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kernel/cpu/bugs.c | 86 ++++++++++++++++++++++---------------- |
| 1 file changed, 49 insertions(+), 37 deletions(-) |
| |
| diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c |
| index 6ee7d81fe339..94e10898126a 100644 |
| --- a/arch/x86/kernel/cpu/bugs.c |
| +++ b/arch/x86/kernel/cpu/bugs.c |
| @@ -558,7 +558,8 @@ early_param("nospectre_v1", nospectre_v1_cmdline); |
| |
| static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE; |
| |
| -static enum spectre_v2_user_mitigation spectre_v2_user = SPECTRE_V2_USER_NONE; |
| +static enum spectre_v2_user_mitigation spectre_v2_user_stibp = SPECTRE_V2_USER_NONE; |
| +static enum spectre_v2_user_mitigation spectre_v2_user_ibpb = SPECTRE_V2_USER_NONE; |
| |
| #ifdef RETPOLINE |
| static bool spectre_v2_bad_module; |
| @@ -702,15 +703,6 @@ spectre_v2_user_select_mitigation(enum spectre_v2_mitigation_cmd v2_cmd) |
| break; |
| } |
| |
| - /* |
| - * At this point, an STIBP mode other than "off" has been set. |
| - * If STIBP support is not being forced, check if STIBP always-on |
| - * is preferred. |
| - */ |
| - if (mode != SPECTRE_V2_USER_STRICT && |
| - boot_cpu_has(X86_FEATURE_AMD_STIBP_ALWAYS_ON)) |
| - mode = SPECTRE_V2_USER_STRICT_PREFERRED; |
| - |
| /* Initialize Indirect Branch Prediction Barrier */ |
| if (boot_cpu_has(X86_FEATURE_IBPB)) { |
| setup_force_cpu_cap(X86_FEATURE_USE_IBPB); |
| @@ -733,23 +725,36 @@ spectre_v2_user_select_mitigation(enum spectre_v2_mitigation_cmd v2_cmd) |
| pr_info("mitigation: Enabling %s Indirect Branch Prediction Barrier\n", |
| static_key_enabled(&switch_mm_always_ibpb) ? |
| "always-on" : "conditional"); |
| + |
| + spectre_v2_user_ibpb = mode; |
| } |
| |
| - /* If enhanced IBRS is enabled no STIBP required */ |
| - if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED) |
| + /* |
| + * If enhanced IBRS is enabled or SMT impossible, STIBP is not |
| + * required. |
| + */ |
| + if (!smt_possible || spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED) |
| return; |
| |
| /* |
| - * If SMT is not possible or STIBP is not available clear the STIBP |
| - * mode. |
| + * At this point, an STIBP mode other than "off" has been set. |
| + * If STIBP support is not being forced, check if STIBP always-on |
| + * is preferred. |
| */ |
| - if (!smt_possible || !boot_cpu_has(X86_FEATURE_STIBP)) |
| + if (mode != SPECTRE_V2_USER_STRICT && |
| + boot_cpu_has(X86_FEATURE_AMD_STIBP_ALWAYS_ON)) |
| + mode = SPECTRE_V2_USER_STRICT_PREFERRED; |
| + |
| + /* |
| + * If STIBP is not available, clear the STIBP mode. |
| + */ |
| + if (!boot_cpu_has(X86_FEATURE_STIBP)) |
| mode = SPECTRE_V2_USER_NONE; |
| + |
| + spectre_v2_user_stibp = mode; |
| + |
| set_mode: |
| - spectre_v2_user = mode; |
| - /* Only print the STIBP mode when SMT possible */ |
| - if (smt_possible) |
| - pr_info("%s\n", spectre_v2_user_strings[mode]); |
| + pr_info("%s\n", spectre_v2_user_strings[mode]); |
| } |
| |
| static const char * const spectre_v2_strings[] = { |
| @@ -989,7 +994,7 @@ void arch_smt_update(void) |
| { |
| mutex_lock(&spec_ctrl_mutex); |
| |
| - switch (spectre_v2_user) { |
| + switch (spectre_v2_user_stibp) { |
| case SPECTRE_V2_USER_NONE: |
| break; |
| case SPECTRE_V2_USER_STRICT: |
| @@ -1222,14 +1227,16 @@ static int ib_prctl_set(struct task_struct *task, unsigned long ctrl) |
| { |
| switch (ctrl) { |
| case PR_SPEC_ENABLE: |
| - if (spectre_v2_user == SPECTRE_V2_USER_NONE) |
| + if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE && |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_NONE) |
| return 0; |
| /* |
| * Indirect branch speculation is always disabled in strict |
| * mode. |
| */ |
| - if (spectre_v2_user == SPECTRE_V2_USER_STRICT || |
| - spectre_v2_user == SPECTRE_V2_USER_STRICT_PREFERRED) |
| + if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED) |
| return -EPERM; |
| task_clear_spec_ib_disable(task); |
| task_update_spec_tif(task); |
| @@ -1240,10 +1247,12 @@ static int ib_prctl_set(struct task_struct *task, unsigned long ctrl) |
| * Indirect branch speculation is always allowed when |
| * mitigation is force disabled. |
| */ |
| - if (spectre_v2_user == SPECTRE_V2_USER_NONE) |
| + if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE && |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_NONE) |
| return -EPERM; |
| - if (spectre_v2_user == SPECTRE_V2_USER_STRICT || |
| - spectre_v2_user == SPECTRE_V2_USER_STRICT_PREFERRED) |
| + if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED) |
| return 0; |
| task_set_spec_ib_disable(task); |
| if (ctrl == PR_SPEC_FORCE_DISABLE) |
| @@ -1274,7 +1283,8 @@ void arch_seccomp_spec_mitigate(struct task_struct *task) |
| { |
| if (ssb_mode == SPEC_STORE_BYPASS_SECCOMP) |
| ssb_prctl_set(task, PR_SPEC_FORCE_DISABLE); |
| - if (spectre_v2_user == SPECTRE_V2_USER_SECCOMP) |
| + if (spectre_v2_user_ibpb == SPECTRE_V2_USER_SECCOMP || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_SECCOMP) |
| ib_prctl_set(task, PR_SPEC_FORCE_DISABLE); |
| } |
| #endif |
| @@ -1303,22 +1313,24 @@ static int ib_prctl_get(struct task_struct *task) |
| if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) |
| return PR_SPEC_NOT_AFFECTED; |
| |
| - switch (spectre_v2_user) { |
| - case SPECTRE_V2_USER_NONE: |
| + if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE && |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_NONE) |
| return PR_SPEC_ENABLE; |
| - case SPECTRE_V2_USER_PRCTL: |
| - case SPECTRE_V2_USER_SECCOMP: |
| + else if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED) |
| + return PR_SPEC_DISABLE; |
| + else if (spectre_v2_user_ibpb == SPECTRE_V2_USER_PRCTL || |
| + spectre_v2_user_ibpb == SPECTRE_V2_USER_SECCOMP || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_PRCTL || |
| + spectre_v2_user_stibp == SPECTRE_V2_USER_SECCOMP) { |
| if (task_spec_ib_force_disable(task)) |
| return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; |
| if (task_spec_ib_disable(task)) |
| return PR_SPEC_PRCTL | PR_SPEC_DISABLE; |
| return PR_SPEC_PRCTL | PR_SPEC_ENABLE; |
| - case SPECTRE_V2_USER_STRICT: |
| - case SPECTRE_V2_USER_STRICT_PREFERRED: |
| - return PR_SPEC_DISABLE; |
| - default: |
| + } else |
| return PR_SPEC_NOT_AFFECTED; |
| - } |
| } |
| |
| int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) |
| @@ -1459,7 +1471,7 @@ static char *stibp_state(void) |
| if (spectre_v2_enabled == SPECTRE_V2_IBRS_ENHANCED) |
| return ""; |
| |
| - switch (spectre_v2_user) { |
| + switch (spectre_v2_user_stibp) { |
| case SPECTRE_V2_USER_NONE: |
| return ", STIBP: disabled"; |
| case SPECTRE_V2_USER_STRICT: |
| -- |
| 2.25.1 |
| |