From cfa86e49c0eba93958cc4bf24e70f3194d30855b Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 4 Jan 2018 11:20:00 -0500 Subject: [PATCH] x86/spec_ctrl: Add sysctl knobs to enable/disable SPEC_CTRL feature There are 2 ways to control IBPB and IBRS 1. At boot time noibrs kernel boot parameter will disable IBRS usage noibpb kernel boot parameter will disable IBPB usage Otherwise if the above parameters are not specified, the system will enable ibrs and ibpb usage if the cpu supports it. 2. At run time echo 0 > /proc/sys/kernel/ibrs_enabled will turn off IBRS echo 1 > /proc/sys/kernel/ibrs_enabled will turn on IBRS in kernel echo 2 > /proc/sys/kernel/ibrs_enabled will turn on IBRS in both userspace and kernel Orabug: 27344012 CVE: CVE-2017-5715 Signed-off-by: Tim Chen Signed-off-by: Konrad Rzeszutek Wilk [Backport: This completes the scaffolding work done in the earlier patch which had the same title] Reviewed-by: John Haxby Signed-off-by: Kirtikar Kashyap --- arch/x86/include/asm/mwait.h | 4 +- arch/x86/include/asm/spec_ctrl.h | 6 +- arch/x86/kernel/cpu/intel.c | 8 +- arch/x86/kernel/smpboot.c | 4 +- arch/x86/kvm/vmx.c | 2 +- kernel/sysctl.c | 123 +++++++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/mwait.h b/arch/x86/include/asm/mwait.h index c4b6a15065d2..72f8fcd65ec1 100644 --- a/arch/x86/include/asm/mwait.h +++ b/arch/x86/include/asm/mwait.h @@ -59,14 +59,14 @@ static inline void mwait_idle_with_hints(unsigned long eax, unsigned long ecx) mb(); } - if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) + if (ibrs_inuse) native_wrmsrl(MSR_IA32_SPEC_CTRL, 0); __monitor((void *)¤t_thread_info()->flags, 0, 0); if (!need_resched()) __mwait(eax, ecx); - if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) + if (ibrs_inuse) native_wrmsrl(MSR_IA32_SPEC_CTRL, FEATURE_ENABLE_IBRS); } current_clr_polling(); diff --git a/arch/x86/include/asm/spec_ctrl.h b/arch/x86/include/asm/spec_ctrl.h index 9bf0e8e99642..ba1c51058b62 100644 --- a/arch/x86/include/asm/spec_ctrl.h +++ b/arch/x86/include/asm/spec_ctrl.h @@ -9,6 +9,7 @@ #ifdef __ASSEMBLY__ .extern use_ibrs +.extern use_ibpb #define __ASM_ENABLE_IBRS \ pushq %rax; \ @@ -168,7 +169,10 @@ ALTERNATIVE "", __stringify(__ASM_ENABLE_IBRS), X86_FEATURE_SPEC_CTRL .endm .macro DISABLE_IBRS -ALTERNATIVE "", __stringify(__ASM_DISABLE_IBRS), X86_FEATURE_SPEC_CTRL + testl $1, use_ibrs + jz 9f + __ASM_DISABLE_IBRS +9: .endm .macro SET_IBPB diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index bcb5303de902..3d8b1170b3ab 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -497,9 +497,15 @@ static void init_intel(struct cpuinfo_x86 *c) if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) { printk_once(KERN_INFO "FEATURE SPEC_CTRL Present\n"); set_ibrs_supported(); + set_ibpb_supported(); + if (ibrs_inuse) + sysctl_ibrs_enabled = 1; + if (ibpb_inuse) + sysctl_ibpb_enabled = 1; } - else + else { printk_once(KERN_INFO "FEATURE SPEC_CTRL Not Present\n"); + } } #ifdef CONFIG_X86_32 diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 9495f5efdf87..c530af21ccfc 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1505,14 +1505,14 @@ void native_play_dead(void) play_dead_common(); tboot_shutdown(TB_SHUTDOWN_WFS); - if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) + if (ibrs_inuse) native_wrmsrl(MSR_IA32_SPEC_CTRL, 0); mwait_play_dead(); /* Only returns on failure */ if (cpuidle_play_dead()) hlt_play_dead(); - if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) + if (ibrs_inuse) native_wrmsrl(MSR_IA32_SPEC_CTRL, FEATURE_ENABLE_IBRS); } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index d21ca86058b3..68c6ad376acb 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -1922,7 +1922,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (per_cpu(current_vmcs, cpu) != vmx->loaded_vmcs->vmcs) { per_cpu(current_vmcs, cpu) = vmx->loaded_vmcs->vmcs; vmcs_load(vmx->loaded_vmcs->vmcs); - if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) + if (ibpb_inuse) native_wrmsrl(MSR_IA32_PRED_CMD, FEATURE_SET_IBPB); } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8cdc4d55abd7..60726ece3bf2 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -69,6 +69,7 @@ #include #include +#include #ifdef CONFIG_X86 #include @@ -195,6 +196,15 @@ static int proc_dostring_coredump(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +#ifdef CONFIG_X86 +int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +int proc_dointvec_ibpb_ctrl(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +int proc_dointvec_ibrs_dump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +#endif + #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ static int __sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; @@ -231,6 +241,12 @@ extern struct ctl_table epoll_table[]; int sysctl_legacy_va_layout; #endif +u32 sysctl_ibrs_dump = 0; +u32 sysctl_ibrs_enabled = 0; +EXPORT_SYMBOL(sysctl_ibrs_enabled); +u32 sysctl_ibpb_enabled = 0; +EXPORT_SYMBOL(sysctl_ibpb_enabled); + /* The default sysctl tables: */ static struct ctl_table sysctl_base_table[] = { @@ -1171,6 +1187,35 @@ static struct ctl_table kern_table[] = { .extra1 = &zero, .extra2 = &one, }, +#endif +#ifdef CONFIG_X86 + { + .procname = "ibrs_enabled", + .data = &sysctl_ibrs_enabled, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_ibrs_ctrl, + .extra1 = &zero, + .extra2 = &two, + }, + { + .procname = "ibpb_enabled", + .data = &sysctl_ibpb_enabled, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_ibpb_ctrl, + .extra1 = &zero, + .extra2 = &one, + }, + { + .procname = "ibrs_dump", + .data = &sysctl_ibrs_dump, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_ibrs_dump, + .extra1 = &zero, + .extra2 = &one, + }, #endif { } }; @@ -2795,6 +2840,84 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, } } +#ifdef CONFIG_X86 +int proc_dointvec_ibrs_dump(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + unsigned int cpu; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + printk("sysctl_ibrs_enabled = %u, sysctl_ibpb_enabled = %u\n", sysctl_ibrs_enabled, sysctl_ibpb_enabled); + printk("use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb); + for_each_online_cpu(cpu) { + u64 val; + + if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) + rdmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, &val); + else + val = 0; + printk("read cpu %d ibrs val %lu\n", cpu, (unsigned long) val); + } + return ret; +} + +int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + unsigned int cpu; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + pr_debug("sysctl_ibrs_enabled = %u, sysctl_ibpb_enabled = %u\n", sysctl_ibrs_enabled, sysctl_ibpb_enabled); + pr_debug("before:use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb); + if (sysctl_ibrs_enabled == 0) { + /* always set IBRS off */ + set_ibrs_disabled(); + if (ibrs_supported) { + for_each_online_cpu(cpu) + wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, 0x0); + } + } else if (sysctl_ibrs_enabled == 2) { + /* always set IBRS on, even in user space */ + clear_ibrs_disabled(); + if (ibrs_supported) { + for_each_online_cpu(cpu) + wrmsrl_on_cpu(cpu, MSR_IA32_SPEC_CTRL, FEATURE_ENABLE_IBRS); + } else { + sysctl_ibrs_enabled = 0; + } + } else if (sysctl_ibrs_enabled == 1) { + /* use IBRS in kernel */ + clear_ibrs_disabled(); + if (!ibrs_inuse) + /* platform don't support ibrs */ + sysctl_ibrs_enabled = 0; + } + pr_debug("after:use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb); + return ret; +} + +int proc_dointvec_ibpb_ctrl(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + pr_debug("sysctl_ibrs_enabled = %u, sysctl_ibpb_enabled = %u\n", sysctl_ibrs_enabled, sysctl_ibpb_enabled); + pr_debug("before:use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb); + if (sysctl_ibpb_enabled == 0) + set_ibpb_disabled(); + else if (sysctl_ibpb_enabled == 1) { + clear_ibpb_disabled(); + if (!ibpb_inuse) + /* platform don't support ibpb */ + sysctl_ibpb_enabled = 0; + } + pr_debug("after:use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb); + return ret; +} +#endif #else /* CONFIG_PROC_SYSCTL */ int proc_dostring(struct ctl_table *table, int write, -- 2.50.1