[PATCH 4/4] HWPOISON: stop /dev/mem users from accessing hwpoison pages

From: Wu Fengguang
Date: Tue Sep 15 2009 - 23:08:12 EST


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 <greg@xxxxxxxxx>
CC: KAMEZAWA Hiroyuki <kamezawa.hiroyu@xxxxxxxxxxxxxx>
Reviewed-by: Andi Kleen <andi@xxxxxxxxxxxxxx>
Signed-off-by: Wu Fengguang <fengguang.wu@xxxxxxxxx>
---
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@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/