From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tim Chen <tim.c.chen@linux.intel.com>
Date: Mon, 20 Nov 2017 13:47:54 -0800
Subject: [PATCH] x86/spec_ctrl: Add lock to serialize changes to ibrs and ibpb
 control
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

CVE-2017-5753
CVE-2017-5715

Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Andy Whitcroft <apw@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
(cherry picked from commit 85789933bc45a3e763823675bd0d80e3e617f234)
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
---
 arch/x86/kernel/cpu/intel.c          | 22 ++++++++++++----------
 arch/x86/kernel/cpu/microcode/core.c |  2 ++
 kernel/smp.c                         |  4 ++++
 kernel/sysctl.c                      | 14 +++++++++++++-
 4 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index c69ea2efbed1..8d558e24783c 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -628,16 +628,18 @@ static void init_intel(struct cpuinfo_x86 *c)
 
 	init_intel_misc_features(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 {
-                printk_once(KERN_INFO "FEATURE SPEC_CTRL Not Present\n");
+	if (!c->cpu_index) {
+		if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) {
+			printk(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 {
+			printk(KERN_INFO "FEATURE SPEC_CTRL Not Present\n");
+		}
 	}
 }
 
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 6450aeda72fc..55086921d29e 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -538,12 +538,14 @@ static ssize_t reload_store(struct device *dev,
 
 	if (boot_cpu_has(X86_FEATURE_SPEC_CTRL)) {
 		printk_once(KERN_INFO "FEATURE SPEC_CTRL Present\n");
+		mutex_lock(&spec_ctrl_mutex);
 		set_ibrs_supported();
 		set_ibpb_supported();
 		if (ibrs_inuse)
 			sysctl_ibrs_enabled = 1;
 		if (ibpb_inuse)
 			sysctl_ibpb_enabled = 1;
+		mutex_unlock(&spec_ctrl_mutex);
 	}
 
 	mutex_unlock(&microcode_mutex);
diff --git a/kernel/smp.c b/kernel/smp.c
index 3bece045f4a4..a224ec0c540c 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -519,6 +519,10 @@ int use_ibpb;
 EXPORT_SYMBOL(use_ibpb);
 #endif
 
+/* mutex to serialize IBRS & IBPB control changes */
+DEFINE_MUTEX(spec_ctrl_mutex);
+EXPORT_SYMBOL(spec_ctrl_mutex);
+
 /*
  * Setup routine for controlling SMP activation
  *
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 69c37bd6251a..47a37792109d 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -69,6 +69,7 @@
 #include <linux/mount.h>
 
 #include <linux/uaccess.h>
+#include <linux/mutex.h>
 #include <asm/processor.h>
 
 #ifdef CONFIG_X86
@@ -2634,12 +2635,17 @@ int proc_dointvec_minmax(struct ctl_table *table, int write,
 int proc_dointvec_ibrs_dump(struct ctl_table *table, int write,
 	void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-	int ret;
+	int ret, orig_inuse;
 	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);
+	mutex_lock(&spec_ctrl_mutex);
+	orig_inuse = use_ibrs;
+	/* temporary halt to ibrs usage to dump ibrs values */
+	clear_ibrs_inuse();
 	for_each_online_cpu(cpu) {
 	       u64 val;
 
@@ -2649,6 +2655,8 @@ int proc_dointvec_ibrs_dump(struct ctl_table *table, int write,
 		       val = 0;
 	       printk("read cpu %d ibrs val %lu\n", cpu, (unsigned long) val);
 	}
+	use_ibrs = orig_inuse;
+	mutex_unlock(&spec_ctrl_mutex);
 	return ret;
 }
 
@@ -2661,6 +2669,7 @@ int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
 	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);
+	mutex_lock(&spec_ctrl_mutex);
 	if (sysctl_ibrs_enabled == 0) {
 		/* always set IBRS off */
 		set_ibrs_disabled();
@@ -2684,6 +2693,7 @@ int proc_dointvec_ibrs_ctrl(struct ctl_table *table, int write,
 			/* platform don't support ibrs */
 			sysctl_ibrs_enabled = 0;
 	}
+	mutex_unlock(&spec_ctrl_mutex);
 	pr_debug("after:use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb);
 	return ret;
 }
@@ -2696,6 +2706,7 @@ int proc_dointvec_ibpb_ctrl(struct ctl_table *table, int write,
 	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);
+	mutex_lock(&spec_ctrl_mutex);
 	if (sysctl_ibpb_enabled == 0)
 		set_ibpb_disabled();
 	else if (sysctl_ibpb_enabled == 1) {
@@ -2704,6 +2715,7 @@ int proc_dointvec_ibpb_ctrl(struct ctl_table *table, int write,
 			/* platform don't support ibpb */
 			sysctl_ibpb_enabled = 0;
 	}
+	mutex_unlock(&spec_ctrl_mutex);
 	pr_debug("after:use_ibrs = %d, use_ibpb = %d\n", use_ibrs, use_ibpb);
 	return ret;
 }
-- 
2.14.2

