Return EIO when user space tries to read/write/mmap hwpoison pages via the /dev/mem interface. The approach: rename range_is_allowed() to devmem_check_pfn_range(), and add PageHWPoison() test into it. So as to fail the whole request if it contains any hwpoison page. Partial write into physical memory seems don't make much sense, and user space can avoid this limit by doing 1-page read/writes. CC: Greg KH CC: KAMEZAWA Hiroyuki Reviewed-by: Andi Kleen Signed-off-by: Wu Fengguang --- drivers/char/mem.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) --- linux-mm.orig/drivers/char/mem.c 2009-09-16 09:57:36.000000000 +0800 +++ linux-mm/drivers/char/mem.c 2009-09-16 09:59:37.000000000 +0800 @@ -90,31 +90,28 @@ static inline int valid_mmap_phys_addr_r } #endif -#ifdef CONFIG_STRICT_DEVMEM -static inline int range_is_allowed(unsigned long pfn, unsigned long size) +static int devmem_check_pfn_range(unsigned long pfn, unsigned long size) { u64 from = ((u64)pfn) << PAGE_SHIFT; u64 to = from + size; u64 cursor = from; while (cursor < to) { +#ifdef CONFIG_STRICT_DEVMEM if (!devmem_is_allowed(pfn)) { printk(KERN_INFO "Program %s tried to access /dev/mem between %Lx->%Lx.\n", current->comm, from, to); - return 0; + return -EPERM; } +#endif + if (pfn_valid(pfn) && PageHWPoison(pfn_to_page(pfn))) + return -EIO; cursor += PAGE_SIZE; pfn++; } - return 1; -} -#else -static inline int range_is_allowed(unsigned long pfn, unsigned long size) -{ - return 1; + return 0; } -#endif void __attribute__((weak)) unxlate_dev_mem_ptr(unsigned long phys, void *addr) { @@ -151,11 +148,13 @@ static ssize_t read_mem(struct file * fi while (count > 0) { unsigned long remaining; + int err; sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, count)) - return -EPERM; + err = devmem_check_pfn_range(p >> PAGE_SHIFT, count); + if (err) + return err; /* * On ia64 if a page has been mapped somewhere as @@ -185,9 +184,10 @@ static ssize_t write_mem(struct file * f size_t count, loff_t *ppos) { unsigned long p = *ppos; - ssize_t written, sz; unsigned long copied; + ssize_t written, sz; void *ptr; + int err; if (!valid_phys_addr_range(p, count)) return -EFAULT; @@ -209,8 +209,9 @@ static ssize_t write_mem(struct file * f while (count > 0) { sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, sz)) - return -EPERM; + err = devmem_check_pfn_range(p >> PAGE_SHIFT, sz); + if (err) + return err; /* * On ia64 if a page has been mapped somewhere as @@ -298,6 +299,7 @@ static struct vm_operations_struct mmap_ static int mmap_mem(struct file * file, struct vm_area_struct * vma) { size_t size = vma->vm_end - vma->vm_start; + int err; if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; @@ -305,8 +307,9 @@ static int mmap_mem(struct file * file, if (!private_mapping_ok(vma)) return -ENOSYS; - if (!range_is_allowed(vma->vm_pgoff, size)) - return -EPERM; + err = devmem_check_pfn_range(vma->vm_pgoff, size); + if (err) + return err; if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size, &vma->vm_page_prot)) -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/