| From b3b7c4795ccab5be71f080774c45bbbcc75c2aaf Mon Sep 17 00:00:00 2001 |
| From: Seunghun Han <kkamagui@gmail.com> |
| Date: Tue, 6 Mar 2018 15:21:43 +0100 |
| Subject: x86/MCE: Serialize sysfs changes |
| |
| From: Seunghun Han <kkamagui@gmail.com> |
| |
| commit b3b7c4795ccab5be71f080774c45bbbcc75c2aaf upstream. |
| |
| The check_interval file in |
| |
| /sys/devices/system/machinecheck/machinecheck<cpu number> |
| |
| directory is a global timer value for MCE polling. If it is changed by one |
| CPU, mce_restart() broadcasts the event to other CPUs to delete and restart |
| the MCE polling timer and __mcheck_cpu_init_timer() reinitializes the |
| mce_timer variable. |
| |
| If more than one CPU writes a specific value to the check_interval file |
| concurrently, mce_timer is not protected from such concurrent accesses and |
| all kinds of explosions happen. Since only root can write to those sysfs |
| variables, the issue is not a big deal security-wise. |
| |
| However, concurrent writes to these configuration variables is void of |
| reason so the proper thing to do is to serialize the access with a mutex. |
| |
| Boris: |
| |
| - Make store_int_with_restart() use device_store_ulong() to filter out |
| negative intervals |
| - Limit min interval to 1 second |
| - Correct locking |
| - Massage commit message |
| |
| Signed-off-by: Seunghun Han <kkamagui@gmail.com> |
| Signed-off-by: Borislav Petkov <bp@suse.de> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Cc: Tony Luck <tony.luck@intel.com> |
| Cc: linux-edac <linux-edac@vger.kernel.org> |
| Cc: stable@vger.kernel.org |
| Link: http://lkml.kernel.org/r/20180302202706.9434-1-kkamagui@gmail.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kernel/cpu/mcheck/mce.c | 22 +++++++++++++++++++++- |
| 1 file changed, 21 insertions(+), 1 deletion(-) |
| |
| --- a/arch/x86/kernel/cpu/mcheck/mce.c |
| +++ b/arch/x86/kernel/cpu/mcheck/mce.c |
| @@ -57,6 +57,9 @@ |
| |
| static DEFINE_MUTEX(mce_log_mutex); |
| |
| +/* sysfs synchronization */ |
| +static DEFINE_MUTEX(mce_sysfs_mutex); |
| + |
| #define CREATE_TRACE_POINTS |
| #include <trace/events/mce.h> |
| |
| @@ -2083,6 +2086,7 @@ static ssize_t set_ignore_ce(struct devi |
| if (kstrtou64(buf, 0, &new) < 0) |
| return -EINVAL; |
| |
| + mutex_lock(&mce_sysfs_mutex); |
| if (mca_cfg.ignore_ce ^ !!new) { |
| if (new) { |
| /* disable ce features */ |
| @@ -2095,6 +2099,8 @@ static ssize_t set_ignore_ce(struct devi |
| on_each_cpu(mce_enable_ce, (void *)1, 1); |
| } |
| } |
| + mutex_unlock(&mce_sysfs_mutex); |
| + |
| return size; |
| } |
| |
| @@ -2107,6 +2113,7 @@ static ssize_t set_cmci_disabled(struct |
| if (kstrtou64(buf, 0, &new) < 0) |
| return -EINVAL; |
| |
| + mutex_lock(&mce_sysfs_mutex); |
| if (mca_cfg.cmci_disabled ^ !!new) { |
| if (new) { |
| /* disable cmci */ |
| @@ -2118,6 +2125,8 @@ static ssize_t set_cmci_disabled(struct |
| on_each_cpu(mce_enable_ce, NULL, 1); |
| } |
| } |
| + mutex_unlock(&mce_sysfs_mutex); |
| + |
| return size; |
| } |
| |
| @@ -2125,8 +2134,19 @@ static ssize_t store_int_with_restart(st |
| struct device_attribute *attr, |
| const char *buf, size_t size) |
| { |
| - ssize_t ret = device_store_int(s, attr, buf, size); |
| + unsigned long old_check_interval = check_interval; |
| + ssize_t ret = device_store_ulong(s, attr, buf, size); |
| + |
| + if (check_interval == old_check_interval) |
| + return ret; |
| + |
| + if (check_interval < 1) |
| + check_interval = 1; |
| + |
| + mutex_lock(&mce_sysfs_mutex); |
| mce_restart(); |
| + mutex_unlock(&mce_sysfs_mutex); |
| + |
| return ret; |
| } |
| |