From 505422517d3f126bb939439e9d15dece94e11d2c Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 11 Dec 2009 18:14:40 +0100 Subject: x86, msr: Add support for non-contiguous cpumasks The current rd/wrmsr_on_cpus helpers assume that the supplied cpumasks are contiguous. However, there are machines out there like some K8 multinode Opterons which have a non-contiguous core enumeration on each node (e.g. cores 0,2 on node 0 instead of 0,1), see http://www.gossamer-threads.com/lists/linux/kernel/1160268. This patch fixes out-of-bounds writes (see URL above) by adding per-CPU msr structs which are used on the respective cores. Additionally, two helpers, msrs_{alloc,free}, are provided for use by the callers of the MSR accessors. Cc: H. Peter Anvin Cc: Mauro Carvalho Chehab Cc: Aristeu Rozanski Cc: Randy Dunlap Cc: Doug Thompson Signed-off-by: Borislav Petkov LKML-Reference: <20091211171440.GD31998@aftab> Signed-off-by: H. Peter Anvin --- drivers/edac/amd64_edac.c | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5fdd6daa40ea..df5b68433f34 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -13,6 +13,8 @@ module_param(report_gart_errors, int, 0644); static int ecc_enable_override; module_param(ecc_enable_override, int, 0644); +static struct msr *msrs; + /* Lookup table for all possible MC control instances */ struct amd64_pvt; static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES]; @@ -2495,8 +2497,7 @@ static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid) static bool amd64_nb_mce_bank_enabled_on_node(int nid) { cpumask_var_t mask; - struct msr *msrs; - int cpu, nbe, idx = 0; + int cpu, nbe; bool ret = false; if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { @@ -2507,32 +2508,22 @@ static bool amd64_nb_mce_bank_enabled_on_node(int nid) get_cpus_on_this_dct_cpumask(mask, nid); - msrs = kzalloc(sizeof(struct msr) * cpumask_weight(mask), GFP_KERNEL); - if (!msrs) { - amd64_printk(KERN_WARNING, "%s: error allocating msrs\n", - __func__); - free_cpumask_var(mask); - return false; - } - rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs); for_each_cpu(cpu, mask) { - nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE; + struct msr *reg = per_cpu_ptr(msrs, cpu); + nbe = reg->l & K8_MSR_MCGCTL_NBE; debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n", - cpu, msrs[idx].q, + cpu, reg->q, (nbe ? "enabled" : "disabled")); if (!nbe) goto out; - - idx++; } ret = true; out: - kfree(msrs); free_cpumask_var(mask); return ret; } @@ -2540,8 +2531,7 @@ out: static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) { cpumask_var_t cmask; - struct msr *msrs = NULL; - int cpu, idx = 0; + int cpu; if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { amd64_printk(KERN_WARNING, "%s: error allocating mask\n", @@ -2551,34 +2541,27 @@ static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on) get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id); - msrs = kzalloc(sizeof(struct msr) * cpumask_weight(cmask), GFP_KERNEL); - if (!msrs) { - amd64_printk(KERN_WARNING, "%s: error allocating msrs\n", - __func__); - return -ENOMEM; - } - rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); for_each_cpu(cpu, cmask) { + struct msr *reg = per_cpu_ptr(msrs, cpu); + if (on) { - if (msrs[idx].l & K8_MSR_MCGCTL_NBE) + if (reg->l & K8_MSR_MCGCTL_NBE) pvt->flags.ecc_report = 1; - msrs[idx].l |= K8_MSR_MCGCTL_NBE; + reg->l |= K8_MSR_MCGCTL_NBE; } else { /* * Turn off ECC reporting only when it was off before */ if (!pvt->flags.ecc_report) - msrs[idx].l &= ~K8_MSR_MCGCTL_NBE; + reg->l &= ~K8_MSR_MCGCTL_NBE; } - idx++; } wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs); - kfree(msrs); free_cpumask_var(cmask); return 0; @@ -3036,6 +3019,8 @@ static int __init amd64_edac_init(void) if (cache_k8_northbridges() < 0) return err; + msrs = msrs_alloc(); + err = pci_register_driver(&amd64_pci_driver); if (err) return err; @@ -3071,6 +3056,9 @@ static void __exit amd64_edac_exit(void) edac_pci_release_generic_ctl(amd64_ctl_pci); pci_unregister_driver(&amd64_pci_driver); + + msrs_free(msrs); + msrs = NULL; } module_init(amd64_edac_init); -- cgit v1.2.3 From a01c7800420d2c294ca403988488a635d4087a6d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 11 Dec 2009 15:48:23 -0800 Subject: nvram: Fix write beyond end condition; prove to gcc copy is safe In nvram_write, first of all, correctly handle the case where the file pointer is already beyond the end; we should return EOF in that case. Second, make the logic a bit more explicit so that gcc can statically prove that the copy_from_user() is safe. Once the condition of the beyond-end filepointer is eliminated, the copy is safe but gcc can't prove it, causing build failures for i386 allyesconfig. Third, eliminate the entirely superfluous variable "len", and just use the passed-in variable "count" instead. Signed-off-by: H. Peter Anvin Cc: Arjan van de Ven Cc: Andrew Morton Cc: Wim Van Sebroeck Cc: Frederic Weisbecker LKML-Reference: --- drivers/char/nvram.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 4008e2ce73c1..fdbcc9fd6d31 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -264,10 +264,16 @@ static ssize_t nvram_write(struct file *file, const char __user *buf, unsigned char contents[NVRAM_BYTES]; unsigned i = *ppos; unsigned char *tmp; - int len; - len = (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count; - if (copy_from_user(contents, buf, len)) + if (i >= NVRAM_BYTES) + return 0; /* Past EOF */ + + if (count > NVRAM_BYTES - i) + count = NVRAM_BYTES - i; + if (count > NVRAM_BYTES) + return -EFAULT; /* Can't happen, but prove it to gcc */ + + if (copy_from_user(contents, buf, count)) return -EFAULT; spin_lock_irq(&rtc_lock); @@ -275,7 +281,7 @@ static ssize_t nvram_write(struct file *file, const char __user *buf, if (!__nvram_check_checksum()) goto checksum_err; - for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp) + for (tmp = contents; count--; ++i, ++tmp) __nvram_write_byte(*tmp, i); __nvram_set_checksum(); -- cgit v1.2.3