/proc/kmem fixes and hwpoison bits v2
Subsystems (6)
cover /proc/kmem fixes and hwpoison bits v2
Hi Andrew and Kame,
Here are the 3 updated bug fix/cleanup patches for kmem. Comments are welcome.
Changes since v1:
- change vread()/vwrite() prototype (proposed by Kame)
- include Kame's is_vmalloc_or_module_addr() check and use Hugh's -ENXIO.
- I decided not to use __GFP_ZERO, since the buf will be reused in the loop, so it would be better for vread to zero-fill it each time.
- removed the hwpoison checks for vmalloc pages. It seems that vread/vwrite could be simplified to handle one single page, and the hwpoison bits can be considered after that.
Thanks,
Fengguang
--
PATCH 1/3 devmem: change vread()/vwrite() prototype to return success or error code
Silently ignore all vmalloc area holes in vread()/vwrite(), and report success (or error code in future) to the caller.
The original intention is to fix a vwrite() related bug, where it could return 0 which cannot be handled correctly by its caller write_kmem(). Then KAMEZAWA recommends to change the prototype to make the semantics clear.
--- drivers/char/mem.c | 24 ++++++++-------- fs/proc/kcore.c | 5 ++- include/linux/vmalloc.h | 6 ++-- mm/nommu.c | 8 ++--- mm/vmalloc.c | 55 +++++++++++--------------------------- 5 files changed, 40 insertions(+), 58 deletions(-)
mm/vmalloc.c +16 -39
@@ -1655,7 +1655,6 @@ EXPORT_SYMBOL(vmalloc_32_user);static int aligned_vread (char *buf,char *addr,unsigned long count) {struct page *p; -int copied =0 ;while (count) {unsigned long offset, length;
@@ -1685,16 +1684,14 @@ static int aligned_vread(char *buf, char addr += length; buf += length; - copied += length; count -= length; } -return copied; +return 0 ; }static int aligned_vwrite (char *buf,char *addr,unsigned long count) {struct page *p; -int copied =0 ;while (count) {unsigned long offset, length;
@@ -1722,10 +1719,9 @@ static int aligned_vwrite(char *buf, cha } addr += length; buf += length; - copied += length; count -= length; } -return copied; +return 0 ; }/**
@@ -1734,9 +1730,7 @@ static int aligned_vwrite(char *buf, cha* @addr: vm address. * @count: number of bytes to be read. * -* Returns # of bytes which addr and buf should be increased. -* (same number to @count). Returns 0 if [addr...addr+count) doesn't -* includes any intersect with alive vmalloc area. +* Returns 0 on success. * * This function checks that addr is a valid vmalloc'ed area, and * copy data from that area to a given buffer. If the given memory range
@@ -1744,23 +1738,21 @@ static int aligned_vwrite(char *buf, cha* proper area of @buf. If there are memory holes, they'll be zero-filled. * IOREMAP area is treated as memory hole and no copy is done. * -* If [addr...addr+count) doesn't includes any intersects with alive -* vm_struct area, returns 0. * @buf should be kernel's buffer. Because this function uses KM_USER0, * the caller should guarantee KM_USER0 is not used. * * Note: In usual ops, vread() is never necessary because the caller * should know vmalloc() area is valid and can use memcpy(). * This is for routines which have to access vmalloc area without -* any informaion, as /dev/kmem. +* any information, as /dev/kmem and /dev/kcore. * */ -long vread (char *buf,char *addr,unsigned long count) +int vread (char *buf,char *addr,unsigned long count) {struct vm_struct *tmp; -char *vaddr, *buf_start = buf; -unsigned long buflen = count; +char *vaddr; +char *buf_end = buf + count;unsigned long n;/* Don't allow overflow */
@@ -1794,13 +1786,11 @@ long vread(char *buf, char *addr, unsign finished:read_unlock (&vmlist_lock); -if (buf == buf_start) -return 0 ;/* zero-fill memory holes */ -if (buf != buf_start + buflen) -memset (buf,0 , buflen - (buf - buf_start)); +if (buf != buf_end) +memset (buf,0 , buf_end - buf); -return buflen; +return 0 ; }/**
@@ -1809,10 +1799,7 @@ finished:* @addr: vm address. * @count: number of bytes to be read. * -* Returns # of bytes which addr and buf should be incresed. -* (same number to @count). -* If [addr...addr+count) doesn't includes any intersect with valid -* vmalloc area, returns 0. +* Returns 0 on success. * * This function checks that addr is a valid vmalloc'ed area, and * copy data from a buffer to the given addr. If specified range of
@@ -1820,30 +1807,24 @@ finished:* proper area of @buf. If there are memory holes, no copy to hole. * IOREMAP area is treated as memory hole and no copy is done. * -* If [addr...addr+count) doesn't includes any intersects with alive -* vm_struct area, returns 0. * @buf should be kernel's buffer. Because this function uses KM_USER0, * the caller should guarantee KM_USER0 is not used. * * Note: In usual ops, vwrite() is never necessary because the caller * should know vmalloc() area is valid and can use memcpy(). * This is for routines which have to access vmalloc area without -* any informaion, as /dev/kmem. -* -* The caller should guarantee KM_USER1 is not used. +* any information, as /dev/kmem. */ -long vwrite (char *buf,char *addr,unsigned long count) +int vwrite (char *buf,char *addr,unsigned long count) {struct vm_struct *tmp;char *vaddr; -unsigned long n, buflen; -int copied =0 ; +unsigned long n;/* Don't allow overflow */ if ((unsigned long ) addr + count < count) count = -(unsigned long ) addr; - buflen = count;read_lock (&vmlist_lock);for (tmp = vmlist; count && tmp; tmp = tmp->next) {
@@ -1860,19 +1841,15 @@ long vwrite(char *buf, char *addr, unsig n = vaddr + tmp->size -PAGE_SIZE - addr;if (n > count) n = count; -if (!(tmp->flags &VM_IOREMAP )) { +if (!(tmp->flags &VM_IOREMAP ))aligned_vwrite (buf, addr, n); - copied++; - } buf += n; addr += n; count -= n; } finished:read_unlock (&vmlist_lock); -if (!copied) -return 0 ; -return buflen; +return 0 ; }/**
include/linux/vmalloc.h +3 -3
@@ -104,9 +104,9 @@ extern void unmap_kernel_range(unsignedextern struct vm_struct *alloc_vm_area (size_t size);extern void free_vm_area (struct vm_struct *area); -/* for /dev/kmem */ -extern long vread (char *buf,char *addr,unsigned long count); -extern long vwrite (char *buf,char *addr,unsigned long count); +/* for /dev/kmem and /dev/kcore */ +extern int vread (char *buf,char *addr,unsigned long count); +extern int vwrite (char *buf,char *addr,unsigned long count);/* * Internals. Dont't use..
drivers/char/mem.c +13 -11
@@ -396,6 +396,7 @@ static ssize_t read_kmem(struct file *fiunsigned long p = *ppos;ssize_t low_count, read, sz;char * kbuf;/* k-addr because vread() takes vmlist_lock rwlock */ +int err =0 ; read =0 ;if (p < (unsigned long ) high_memory) {
@@ -442,12 +443,12 @@ static ssize_t read_kmem(struct file *fireturn -ENOMEM ;while (count >0 ) { sz =size_inside_page (p, count); - sz =vread (kbuf, (char *)p, sz); -if (!sz) + err =vread (kbuf, (char *)p, sz); +if (err)break ;if (copy_to_user (buf, kbuf, sz)) { -free_page ((unsigned long )kbuf); -return -EFAULT ; + err = -EFAULT ; +break ; } count -= sz; buf += sz;
@@ -457,7 +458,7 @@ static ssize_t read_kmem(struct file *fifree_page ((unsigned long )kbuf); } *ppos = p; -return read; +return read ? read : err; }
@@ -521,6 +522,7 @@ static ssize_t write_kmem(struct file *ssize_t wrote =0 ;ssize_t virtr =0 ;char * kbuf;/* k-addr because vwrite() takes vmlist_lock rwlock */ +int err =0 ;if (p < (unsigned long ) high_memory) { unsigned long to_write = min_t(unsigned long, count,
@@ -543,12 +545,12 @@ static ssize_t write_kmem(struct file * n =copy_from_user (kbuf, buf, sz);if (n) { -if (wrote + virtr) -break ; -free_page ((unsigned long )kbuf); -return -EFAULT ; + err = -EFAULT ; +break ; } - sz =vwrite (kbuf, (char *)p, sz); + err =vwrite (kbuf, (char *)p, sz); +if (err) +break ; count -= sz; buf += sz; virtr += sz;
@@ -558,7 +560,7 @@ static ssize_t write_kmem(struct file * } *ppos = p; -return virtr + wrote; +return virtr + wrote ? : err; }#endif
fs/proc/kcore.c +4 -1
@@ -492,11 +492,14 @@ read_kcore(struct file *file, char __usereturn -EFAULT ; }else if (is_vmalloc_or_module_addr ((void *)start)) {char * elf_buf; +int err; elf_buf =kzalloc (tsz,GFP_KERNEL );if (!elf_buf)return -ENOMEM ; -vread (elf_buf, (char *)start, tsz); + err =vread (elf_buf, (char *)start, tsz); +if (err) +return err;/* we have to zero-fill user buffer even if no read */ if (copy_to_user (buffer, elf_buf, tsz)) {kfree (elf_buf);
mm/nommu.c +4 -4
@@ -263,20 +263,20 @@ unsigned long vmalloc_to_pfn(const void }EXPORT_SYMBOL (vmalloc_to_pfn); -long vread (char *buf,char *addr,unsigned long count) +int vread (char *buf,char *addr,unsigned long count) {memcpy (buf, addr, count); -return count; +return 0 ; } -long vwrite (char *buf,char *addr,unsigned long count) +int vwrite (char *buf,char *addr,unsigned long count) {/* Don't allow overflow */ if ((unsigned long ) addr + count < count) count = -(unsigned long ) addr;memcpy (addr, buf, count); -return (count); +return 0 ; }/*
Re: [PATCH 1/3] devmem: change vread()/vwrite() prototype to return success or error code
Quoted 65 lines · On Wed, 16 Sep 2009 09:39:40 +0800 Wu Fengguang <[email protected]> wrote:
Silently ignore all vmalloc area holes in vread()/vwrite(), and report success (or error code in future) to the caller.
The original intention is to fix a
vwrite()related bug, where it could return 0 which cannot be handled correctly by its callerwrite_kmem(). Then KAMEZAWA recommends to change the prototype to make the semantics clear.CC Andi Kleen <[email protected]>CC Benjamin Herrenschmidt <[email protected]>CC Christoph Lameter <[email protected]>CC Ingo Molnar <[email protected]>CC Tejun Heo <[email protected]>CC Nick Piggin <[email protected]>CC KAMEZAWA Hiroyuki <[email protected]>Signed-off-by Wu Fengguang <[email protected]>--- drivers/char/mem.c | 24 ++++++++-------- fs/proc/kcore.c | 5 ++- include/linux/vmalloc.h | 6 ++-- mm/nommu.c | 8 ++--- mm/vmalloc.c | 55 +++++++++++--------------------------- 5 files changed, 40 insertions(+), 58 deletions(-)mm/vmalloc.c +2
-6@@ -1655,7 +1655,6 @@ EXPORT_SYMBOL(vmalloc_32_user);static int aligned_vread (char *buf,char *addr,unsigned long count) {struct page *p; -int copied =0 ;while (count) {unsigned long offset, length;@@ -1685,16 +1684,14 @@ static int aligned_vread(char *buf, char addr += length; buf += length; - copied += length; count -= length; } -return copied; +return 0 ; }static int aligned_vwrite (char *buf,char *addr,unsigned long count) {struct page *p; -int copied =0 ;while (count) {unsigned long offset, length;@@ -1722,10 +1719,9 @@ static int aligned_vwrite(char *buf, cha } addr += length; buf += length; - copied += length; count -= length; } -return copied; +return 0 ; }/**
mm/vmalloc.c static int aligned_vwrite(char *buf, cha · line 1733
Quoted 7 lines
mm/vmalloc.c +1
-3@@ -1734,9 +1730,7 @@ static int aligned_vwrite(char *buf, cha* @addr: vm address. * @count: number of bytes to be read. * -* Returns # of bytes which addr and buf should be increased. -* (same number to @count). Returns 0 if [addr...addr+count) doesn't -* includes any intersect with alive vmalloc area. +* Returns 0 on success.
Seems to return 0 _always_. My version retunrs false if it's out-of-range. plz return false if out-of-vmalloc-range.
Now, use vmalloc area is exported via /proc/vmallocinfo. Then, it's easy to know "valid" vmalloc area. But users cannot know memory holes in "valid" vmalloc area. So, vread() returns success even if it founds holes.
Another thought:
Should we purge vread/vwrite and just use copy_to/from_user ? To do that, /proc/kcore should be revisited...
It has to copy pages one by one in aligned manner.
Anyway, it's merge window and not good season for this semantic changes of a function.
Quoted 207 lines
mm/vmalloc.c +13
-30* * This function checks that addr is a valid vmalloc'ed area, and * copy data from that area to a given buffer. If the given memory range @@ -1744,23 +1738,21 @@ static int aligned_vwrite(char *buf, cha* proper area of @buf. If there are memory holes, they'll be zero-filled. * IOREMAP area is treated as memory hole and no copy is done. * -* If [addr...addr+count) doesn't includes any intersects with alive -* vm_struct area, returns 0. * @buf should be kernel's buffer. Because this function uses KM_USER0, * the caller should guarantee KM_USER0 is not used. * * Note: In usual ops, vread() is never necessary because the caller * should know vmalloc() area is valid and can use memcpy(). * This is for routines which have to access vmalloc area without -* any informaion, as /dev/kmem. +* any information, as /dev/kmem and /dev/kcore. * */ -long vread (char *buf,char *addr,unsigned long count) +int vread (char *buf,char *addr,unsigned long count) {struct vm_struct *tmp; -char *vaddr, *buf_start = buf; -unsigned long buflen = count; +char *vaddr; +char *buf_end = buf + count;unsigned long n;/* Don't allow overflow */ @@ -1794,13 +1786,11 @@ long vread(char *buf, char *addr, unsign finished:read_unlock (&vmlist_lock); -if (buf == buf_start) -return 0 ;/* zero-fill memory holes */ -if (buf != buf_start + buflen) -memset (buf,0 , buflen - (buf - buf_start)); +if (buf != buf_end) +memset (buf,0 , buf_end - buf); -return buflen; +return 0 ; }/** @@ -1809,10 +1799,7 @@ finished:* @addr: vm address. * @count: number of bytes to be read. * -* Returns # of bytes which addr and buf should be incresed. -* (same number to @count). -* If [addr...addr+count) doesn't includes any intersect with valid -* vmalloc area, returns 0. +* Returns 0 on success. * * This function checks that addr is a valid vmalloc'ed area, and * copy data from a buffer to the given addr. If specified range of @@ -1820,30 +1807,24 @@ finished:* proper area of @buf. If there are memory holes, no copy to hole. * IOREMAP area is treated as memory hole and no copy is done. * -* If [addr...addr+count) doesn't includes any intersects with alive -* vm_struct area, returns 0. * @buf should be kernel's buffer. Because this function uses KM_USER0, * the caller should guarantee KM_USER0 is not used. * * Note: In usual ops, vwrite() is never necessary because the caller * should know vmalloc() area is valid and can use memcpy(). * This is for routines which have to access vmalloc area without -* any informaion, as /dev/kmem. -* -* The caller should guarantee KM_USER1 is not used. +* any information, as /dev/kmem. */ -long vwrite (char *buf,char *addr,unsigned long count) +int vwrite (char *buf,char *addr,unsigned long count) {struct vm_struct *tmp;char *vaddr; -unsigned long n, buflen; -int copied =0 ; +unsigned long n;/* Don't allow overflow */ if ((unsigned long ) addr + count < count) count = -(unsigned long ) addr; - buflen = count;read_lock (&vmlist_lock);for (tmp = vmlist; count && tmp; tmp = tmp->next) {@@ -1860,19 +1841,15 @@ long vwrite(char *buf, char *addr, unsig n = vaddr + tmp->size -PAGE_SIZE - addr;if (n > count) n = count; -if (!(tmp->flags &VM_IOREMAP )) { +if (!(tmp->flags &VM_IOREMAP ))aligned_vwrite (buf, addr, n); - copied++; - } buf += n; addr += n; count -= n; } finished:read_unlock (&vmlist_lock); -if (!copied) -return 0 ; -return buflen; +return 0 ; }/** include/linux/vmalloc.h +3
-3@@ -104,9 +104,9 @@ extern void unmap_kernel_range(unsignedextern struct vm_struct *alloc_vm_area (size_t size);extern void free_vm_area (struct vm_struct *area); -/* for /dev/kmem */ -extern long vread (char *buf,char *addr,unsigned long count); -extern long vwrite (char *buf,char *addr,unsigned long count); +/* for /dev/kmem and /dev/kcore */ +extern int vread (char *buf,char *addr,unsigned long count); +extern int vwrite (char *buf,char *addr,unsigned long count);/* * Internals. Dont't use.. drivers/char/mem.c +13
-11@@ -396,6 +396,7 @@ static ssize_t read_kmem(struct file *fiunsigned long p = *ppos;ssize_t low_count, read, sz;char * kbuf;/* k-addr because vread() takes vmlist_lock rwlock */ +int err =0 ; read =0 ;if (p < (unsigned long ) high_memory) {@@ -442,12 +443,12 @@ static ssize_t read_kmem(struct file *fireturn -ENOMEM ;while (count >0 ) { sz =size_inside_page (p, count); - sz =vread (kbuf, (char *)p, sz); -if (!sz) + err =vread (kbuf, (char *)p, sz); +if (err)break ;if (copy_to_user (buf, kbuf, sz)) { -free_page ((unsigned long )kbuf); -return -EFAULT ; + err = -EFAULT ; +break ; } count -= sz; buf += sz;@@ -457,7 +458,7 @@ static ssize_t read_kmem(struct file *fifree_page ((unsigned long )kbuf); } *ppos = p; -return read; +return read ? read : err; }@@ -521,6 +522,7 @@ static ssize_t write_kmem(struct file *ssize_t wrote =0 ;ssize_t virtr =0 ;char * kbuf;/* k-addr because vwrite() takes vmlist_lock rwlock */ +int err =0 ;if (p < (unsigned long ) high_memory) { unsigned long to_write = min_t(unsigned long, count,@@ -543,12 +545,12 @@ static ssize_t write_kmem(struct file * n =copy_from_user (kbuf, buf, sz);if (n) { -if (wrote + virtr) -break ; -free_page ((unsigned long )kbuf); -return -EFAULT ; + err = -EFAULT ; +break ; } - sz =vwrite (kbuf, (char *)p, sz); + err =vwrite (kbuf, (char *)p, sz); +if (err) +break ; count -= sz; buf += sz; virtr += sz;@@ -558,7 +560,7 @@ static ssize_t write_kmem(struct file * } *ppos = p; -return virtr + wrote; +return virtr + wrote ? : err; }#endif fs/proc/kcore.c +0
-0@@ -492,11 +492,14 @@ read_kcore(struct file *file, char __usereturn -EFAULT ; }else if (is_vmalloc_or_module_addr ((void *)start)) {
fs/proc/kcore.c read_kcore(struct file *file, char __use · line 502
Quoted 10 lines
fs/proc/kcore.c +4
-1char * elf_buf; +int err; elf_buf =kzalloc (tsz,GFP_KERNEL );if (!elf_buf)return -ENOMEM ; -vread (elf_buf, (char *)start, tsz); + err =vread (elf_buf, (char *)start, tsz); +if (err) +return err;
This is wrong.
/proc/kcore provides "coredump" image of kernel and it never faults. Then, it doesn't check error code intentionally and just return zero-filled buffer of reuqested length.
Thanks,
-Kame
Quoted 34 lines
fs/proc/kcore.c +0
-0/* we have to zero-fill user buffer even if no read */ if (copy_to_user (buffer, elf_buf, tsz)) {kfree (elf_buf);mm/nommu.c +4
-4@@ -263,20 +263,20 @@ unsigned long vmalloc_to_pfn(const void }EXPORT_SYMBOL (vmalloc_to_pfn); -long vread (char *buf,char *addr,unsigned long count) +int vread (char *buf,char *addr,unsigned long count) {memcpy (buf, addr, count); -return count; +return 0 ; } -long vwrite (char *buf,char *addr,unsigned long count) +int vwrite (char *buf,char *addr,unsigned long count) {/* Don't allow overflow */ if ((unsigned long ) addr + count < count) count = -(unsigned long ) addr;memcpy (addr, buf, count); -return (count); +return 0 ; }/* Signature · 1 line
--
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/3] devmem: change vread()/vwrite() prototype to return success or error code
Quoted 66 lines · KAMEZAWA Hiroyuki
Quoted 63 lines · On Wed, 16 Sep 2009 09:39:40 +0800 Wu Fengguang <[email protected]> wrote:
Silently ignore all vmalloc area holes in vread()/vwrite(), and report success (or error code in future) to the caller.
The original intention is to fix a
vwrite()related bug, where it could return 0 which cannot be handled correctly by its callerwrite_kmem(). Then KAMEZAWA recommends to change the prototype to make the semantics clear.CC Andi Kleen <[email protected]>CC Benjamin Herrenschmidt <[email protected]>CC Christoph Lameter <[email protected]>CC Ingo Molnar <[email protected]>CC Tejun Heo <[email protected]>CC Nick Piggin <[email protected]>CC KAMEZAWA Hiroyuki <[email protected]>Signed-off-by Wu Fengguang <[email protected]>--- drivers/char/mem.c | 24 ++++++++-------- fs/proc/kcore.c | 5 ++- include/linux/vmalloc.h | 6 ++-- mm/nommu.c | 8 ++--- mm/vmalloc.c | 55 +++++++++++--------------------------- 5 files changed, 40 insertions(+), 58 deletions(-)mm/vmalloc.c 2009-09-16 09:24:11.000000000 +0800 renamed from mm/vmalloc.c 2009-09-16 08:52:12.000000000 +0800 +2
-6@@ -1655,7 +1655,6 @@ EXPORT_SYMBOL(vmalloc_32_user); static int aligned_vread(char *buf, char *addr, unsigned long count) { struct page *p; - int copied = 0; while (count) { unsigned long offset, length;@@ -1685,16 +1684,14 @@ static int aligned_vread(char *buf, char addr += length; buf += length; - copied += length; count -= length; } - return copied; + return 0; } static int aligned_vwrite(char *buf, char *addr, unsigned long count) { struct page *p; - int copied = 0; while (count) { unsigned long offset, length;@@ -1722,10 +1719,9 @@ static int aligned_vwrite(char *buf, cha } addr += length; buf += length; - copied += length; count -= length; } - return copied; + return 0; }
Quoted 15 lines
mm/vmalloc.c 2009-09-16 09:24:11.000000000 +0800 static int aligned_vwrite(char *buf, cha · line 1733
Quoted 9 lines
mm/vmalloc.c 2009-09-16 09:24:11.000000000 +0800 renamed from mm/vmalloc.c 2009-09-16 08:52:12.000000000 +0800 +1
-3/**@@ -1734,9 +1730,7 @@ static int aligned_vwrite(char *buf, cha * @addr: vm address. * @count: number of bytes to be read. * - * Returns # of bytes which addr and buf should be increased. - * (same number to @count). Returns 0 if [addr...addr+count) doesn't - * includes any intersect with alive vmalloc area. + * Returns 0 on success.Seems to return 0 _always_. My version retunrs false if it's out-of-range. plz return false if out-of-vmalloc-range.
Now, use vmalloc area is exported via/proc/vmallocinfo. Then, it's easy to know "valid" vmalloc area. But users cannot know memory holes in "valid" vmalloc area. So,vread()returns success even if it founds holes.
OK, that's fair enough.
Quoted 2 lines
Another thought:
Should we purge vread/vwrite and just use copy_to/from_user ?
I like its simplicity. Though I'm afraid I don't know enough to make useful comments.
Quoted 2 lines
To do that,
/proc/kcoreshould be revisited... It has to copy pages one by one in aligned manner.
You know it used to not rely on vread() ;)
Quoted 2 lines
Anyway, it's merge window and not good season for this semantic changes of a function.
Agreed. I'll do a bug fix only patch, and leave other decisions to you.
Quoted 220 lines
Quoted 207 lines
mm/vmalloc.c 2009-09-16 09:24:11.000000000 +0800 renamed from mm/vmalloc.c 2009-09-16 08:52:12.000000000 +0800 +13
-30* * This function checks that addr is a valid vmalloc'ed area, and * copy data from that area to a given buffer. If the given memory range@@ -1744,23 +1738,21 @@ static int aligned_vwrite(char *buf, cha * proper area of @buf. If there are memory holes, they'll be zero-filled. * IOREMAP area is treated as memory hole and no copy is done. * - * If [addr...addr+count) doesn't includes any intersects with alive - * vm_struct area, returns 0. * @buf should be kernel's buffer. Because this function uses KM_USER0, * the caller should guarantee KM_USER0 is not used. * * Note: In usual ops, vread() is never necessary because the caller * should know vmalloc() area is valid and can use memcpy(). * This is for routines which have to access vmalloc area without - * any informaion, as /dev/kmem. + * any information, as /dev/kmem and /dev/kcore. * */ -long vread(char *buf, char *addr, unsigned long count) +int vread(char *buf, char *addr, unsigned long count) { struct vm_struct *tmp; - char *vaddr, *buf_start = buf; - unsigned long buflen = count; + char *vaddr; + char *buf_end = buf + count; unsigned long n; /* Don't allow overflow */@@ -1794,13 +1786,11 @@ long vread(char *buf, char *addr, unsign finished: read_unlock(&vmlist_lock); - if (buf == buf_start) - return 0; /* zero-fill memory holes */ - if (buf != buf_start + buflen) - memset(buf, 0, buflen - (buf - buf_start)); + if (buf != buf_end) + memset(buf, 0, buf_end - buf); - return buflen; + return 0; } /**@@ -1809,10 +1799,7 @@ finished: * @addr: vm address. * @count: number of bytes to be read. * - * Returns # of bytes which addr and buf should be incresed. - * (same number to @count). - * If [addr...addr+count) doesn't includes any intersect with valid - * vmalloc area, returns 0. + * Returns 0 on success. * * This function checks that addr is a valid vmalloc'ed area, and * copy data from a buffer to the given addr. If specified range of@@ -1820,30 +1807,24 @@ finished: * proper area of @buf. If there are memory holes, no copy to hole. * IOREMAP area is treated as memory hole and no copy is done. * - * If [addr...addr+count) doesn't includes any intersects with alive - * vm_struct area, returns 0. * @buf should be kernel's buffer. Because this function uses KM_USER0, * the caller should guarantee KM_USER0 is not used. * * Note: In usual ops, vwrite() is never necessary because the caller * should know vmalloc() area is valid and can use memcpy(). * This is for routines which have to access vmalloc area without - * any informaion, as /dev/kmem. - * - * The caller should guarantee KM_USER1 is not used. + * any information, as /dev/kmem. */ -long vwrite(char *buf, char *addr, unsigned long count) +int vwrite(char *buf, char *addr, unsigned long count) { struct vm_struct *tmp; char *vaddr; - unsigned long n, buflen; - int copied = 0; + unsigned long n; /* Don't allow overflow */ if ((unsigned long) addr + count < count) count = -(unsigned long) addr; - buflen = count; read_lock(&vmlist_lock); for (tmp = vmlist; count && tmp; tmp = tmp->next) {@@ -1860,19 +1841,15 @@ long vwrite(char *buf, char *addr, unsig n = vaddr + tmp->size - PAGE_SIZE - addr; if (n > count) n = count; - if (!(tmp->flags & VM_IOREMAP)) { + if (!(tmp->flags & VM_IOREMAP)) aligned_vwrite(buf, addr, n); - copied++; - } buf += n; addr += n; count -= n; } finished: read_unlock(&vmlist_lock); - if (!copied) - return 0; - return buflen; + return 0; } /**include/linux/vmalloc.h 2009-09-16 08:52:17.000000000 +0800 renamed from include/linux/vmalloc.h 2009-09-16 08:52:12.000000000 +0800 +3
-3@@ -104,9 +104,9 @@ extern void unmap_kernel_range(unsigned extern struct vm_struct *alloc_vm_area(size_t size); extern void free_vm_area(struct vm_struct *area); -/* for /dev/kmem */ -extern long vread(char *buf, char *addr, unsigned long count); -extern long vwrite(char *buf, char *addr, unsigned long count); +/* for /dev/kmem and /dev/kcore */ +extern int vread(char *buf, char *addr, unsigned long count); +extern int vwrite(char *buf, char *addr, unsigned long count); /* * Internals. Dont't use..drivers/char/mem.c 2009-09-16 09:23:00.000000000 +0800 renamed from drivers/char/mem.c 2009-09-16 08:52:12.000000000 +0800 +13
-11@@ -396,6 +396,7 @@ static ssize_t read_kmem(struct file *fi unsigned long p = *ppos; ssize_t low_count, read, sz; char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */ + int err = 0; read = 0; if (p < (unsigned long) high_memory) {@@ -442,12 +443,12 @@ static ssize_t read_kmem(struct file *fi return -ENOMEM; while (count > 0) { sz = size_inside_page(p, count); - sz = vread(kbuf, (char *)p, sz); - if (!sz) + err = vread(kbuf, (char *)p, sz); + if (err) break; if (copy_to_user(buf, kbuf, sz)) { - free_page((unsigned long)kbuf); - return -EFAULT; + err = -EFAULT; + break; } count -= sz; buf += sz;@@ -457,7 +458,7 @@ static ssize_t read_kmem(struct file *fi free_page((unsigned long)kbuf); } *ppos = p; - return read; + return read ? read : err; }@@ -521,6 +522,7 @@ static ssize_t write_kmem(struct file * ssize_t wrote = 0; ssize_t virtr = 0; char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ + int err = 0; if (p < (unsigned long) high_memory) { unsigned long to_write = min_t(unsigned long, count,@@ -543,12 +545,12 @@ static ssize_t write_kmem(struct file * n = copy_from_user(kbuf, buf, sz); if (n) { - if (wrote + virtr) - break; - free_page((unsigned long)kbuf); - return -EFAULT; + err = -EFAULT; + break; } - sz = vwrite(kbuf, (char *)p, sz); + err = vwrite(kbuf, (char *)p, sz); + if (err) + break; count -= sz; buf += sz; virtr += sz;@@ -558,7 +560,7 @@ static ssize_t write_kmem(struct file * } *ppos = p; - return virtr + wrote; + return virtr + wrote ? : err; } #endiffs/proc/kcore.c 2009-09-16 08:52:17.000000000 +0800 renamed from fs/proc/kcore.c 2009-09-16 08:52:12.000000000 +0800 +0
-0@@ -492,11 +492,14 @@ read_kcore(struct file *file, char __use return -EFAULT; } else if (is_vmalloc_or_module_addr((void *)start)) {fs/proc/kcore.c 2009-09-16 08:52:17.000000000 +0800 read_kcore(struct file *file, char __use · line 502
Quoted 10 lines
fs/proc/kcore.c 2009-09-16 08:52:17.000000000 +0800 renamed from fs/proc/kcore.c 2009-09-16 08:52:12.000000000 +0800 +4
-1char * elf_buf; + int err; elf_buf = kzalloc(tsz, GFP_KERNEL); if (!elf_buf) return -ENOMEM; - vread(elf_buf, (char *)start, tsz); + err = vread(elf_buf, (char *)start, tsz); + if (err) + return err;This is wrong.
Quoted 3 lines
/proc/kcoreprovides "coredump" image of kernel and it never faults. Then, it doesn't check error code intentionally and just return zero-filled buffer of reuqested length.
Ah OK. Will revert this change.
Thanks,
Fengguang
Quoted 29 lines
Quoted 29 lines
fs/proc/kcore.c 2009-09-16 08:52:17.000000000 +0800 renamed from fs/proc/kcore.c 2009-09-16 08:52:12.000000000 +0800 +0
-0/* we have to zero-fill user buffer even if no read */ if (copy_to_user(buffer, elf_buf, tsz)) { kfree(elf_buf);mm/nommu.c 2009-09-16 09:02:02.000000000 +0800 renamed from mm/nommu.c 2009-09-16 09:01:36.000000000 +0800 +4
-4@@ -263,20 +263,20 @@ unsigned long vmalloc_to_pfn(const void } EXPORT_SYMBOL(vmalloc_to_pfn); -long vread(char *buf, char *addr, unsigned long count) +int vread(char *buf, char *addr, unsigned long count) { memcpy(buf, addr, count); - return count; + return 0; } -long vwrite(char *buf, char *addr, unsigned long count) +int vwrite(char *buf, char *addr, unsigned long count) { /* Don't allow overflow */ if ((unsigned long) addr + count < count) count = -(unsigned long) addr; memcpy(addr, buf, count); - return(count); + return 0; }
Quoted 5 lines
mm/nommu.c 2009-09-16 09:02:02.000000000 +0800 unsigned long vmalloc_to_pfn(const void
Quoted 5 lines
mm/nommu.c 2009-09-16 09:02:02.000000000 +0800 renamed from mm/nommu.c 2009-09-16 09:01:36.000000000 +0800 +0
-1/* --
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH 1/3] devmem: change vread()/vwrite() prototype to return success or error code
In reply to Wu FengguangQuoted 8 lines · On Wed, 16 Sep 2009 09:39:40 +0800 Wu Fengguang <[email protected]> wrote:
Silently ignore all vmalloc area holes in vread()/vwrite(), and report success (or error code in future) to the caller.
The original intention is to fix a
vwrite()related bug, where it could return 0 which cannot be handled correctly by its callerwrite_kmem(). Then KAMEZAWA recommends to change the prototype to make the semantics clear.
Quoted 8 lines
CC Andi Kleen <[email protected]>CC Benjamin Herrenschmidt <[email protected]>CC Christoph Lameter <[email protected]>CC Ingo Molnar <[email protected]>CC Tejun Heo <[email protected]>CC Nick Piggin <[email protected]>CC KAMEZAWA Hiroyuki <[email protected]>Signed-off-by Wu Fengguang <[email protected]>
Okay, maybe it's good to change for now.
I sometimes wonder we should use copy_to/from_user to access vmalloc area... but we have IO_REMAP. So, maybe vread/vwrite is required one.
Thanks,
-Kame
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 2/3 devmem: check vmalloc address on kmem read/write
In reply to Wu FengguangFrom: KAMEZAWA Hiroyuki <[email protected]>
Otherwise vmalloc_to_page() will BUG().
This also makes the kmem read/write implementation aligned with mem(4):
"References to nonexistent locations cause errors to be returned." Here we return -ENXIO (inspired by Hugh) if no bytes have been transfered to/from user space, otherwise return partial read/write results.
--- drivers/char/mem.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/char/mem.c +8 -0
@@ -443,6 +443,10 @@ static ssize_t read_kmem(struct file *fireturn -ENOMEM ;while (count >0 ) { sz =size_inside_page (p, count); +if (!is_vmalloc_or_module_addr ((void *)p)) { + err = -ENXIO ; +break ; + } err =vread (kbuf, (char *)p, sz);if (err)break ;
@@ -543,6 +547,10 @@ static ssize_t write_kmem(struct file *unsigned long sz =size_inside_page (p, count);unsigned long n; +if (!is_vmalloc_or_module_addr ((void *)p)) { + err = -ENXIO ; +break ; + } n =copy_from_user (kbuf, buf, sz);if (n) { err = -EFAULT ;
Re: [PATCH 2/3] devmem: check vmalloc address on kmem read/write
Quoted 9 lines · On Wed, 16 Sep 2009 09:39:41 +0800 Wu Fengguang <[email protected]> wrote:
From: KAMEZAWA Hiroyuki <[email protected]>
Otherwise
vmalloc_to_page()willBUG().This also makes the kmem read/write implementation aligned with mem(4):
"References to nonexistent locations cause errors to be returned." Here we return-ENXIO(inspired by Hugh) if no bytes have been transfered to/from user space, otherwise return partial read/write results.
seems reasonable.
Quoted 41 lines
CC Greg Kroah-Hartman <[email protected]>CC Hugh Dickins <[email protected]>Signed-off-by KAMEZAWA Hiroyuki <[email protected]>Signed-off-by Wu Fengguang <[email protected]>--- drivers/char/mem.c | 8 ++++++++ 1 file changed, 8 insertions(+)drivers/char/mem.c +8
-0@@ -443,6 +443,10 @@ static ssize_t read_kmem(struct file *fireturn -ENOMEM ;while (count >0 ) { sz =size_inside_page (p, count); +if (!is_vmalloc_or_module_addr ((void *)p)) { + err = -ENXIO ; +break ; + } err =vread (kbuf, (char *)p, sz);if (err)break ;@@ -543,6 +547,10 @@ static ssize_t write_kmem(struct file *unsigned long sz =size_inside_page (p, count);unsigned long n; +if (!is_vmalloc_or_module_addr ((void *)p)) { + err = -ENXIO ; +break ; + } n =copy_from_user (kbuf, buf, sz);if (n) { err = -EFAULT ;Signature · 7 lines
-- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 3/3 HWPOISON: prevent /dev/kmem users from accessing hwpoison pages
In reply to Wu FengguangWhen /dev/kmem read()/write() encounters hwpoison page, stop it and return the amount of work done till now.
Vmalloc pages are not checked for now, to avoid conflicts with ongoing vread/vwrite works.
--- drivers/char/mem.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
drivers/char/mem.c +14 -4
@@ -427,6 +427,9 @@ static ssize_t read_kmem(struct file *fi*/ kbuf =xlate_dev_kmem_ptr ((char *)p); +if (unlikely (virt_addr_valid (kbuf) && +PageHWPoison (virt_to_page (kbuf)))) +return -EIO ;if (copy_to_user (buf, kbuf, sz))return -EFAULT ; buf += sz;
@@ -472,6 +475,7 @@ do_write_kmem(unsigned long p, const cha {ssize_t written, sz;unsigned long copied; +int err =0 ; written =0 ;#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
@@ -498,13 +502,19 @@ do_write_kmem(unsigned long p, const cha*/ ptr =xlate_dev_kmem_ptr ((char *)p); +if (unlikely (virt_addr_valid (ptr) && +PageHWPoison (virt_to_page (ptr)))) { + err = -EIO ; +break ; + } + copied =copy_from_user (ptr, buf, sz);if (copied) { written += sz - copied; -if (written) -break ; -return -EFAULT ; + err = -EFAULT ; +break ; } + buf += sz; p += sz; count -= sz;
@@ -512,7 +522,7 @@ do_write_kmem(unsigned long p, const cha } *ppos += written; -return written; +return written ? written : err; }