mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
devmem: check vmalloc address on kmem read/write
commit 325fda71d0
[ cebbert@redhat.com : backport to 2.6.32 ]
devmem: check vmalloc address on kmem read/write
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.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Signed-off-by: Wu Fengguang <fengguang.wu@intel.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Chuck Ebbert <cebbert@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
0e1104ada3
commit
6b344d83ca
@@ -421,6 +421,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
|
||||
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) {
|
||||
@@ -469,12 +470,16 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
|
||||
while (count > 0) {
|
||||
int len = size_inside_page(p, count);
|
||||
|
||||
if (!is_vmalloc_or_module_addr((void *)p)) {
|
||||
err = -ENXIO;
|
||||
break;
|
||||
}
|
||||
len = vread(kbuf, (char *)p, len);
|
||||
if (!len)
|
||||
break;
|
||||
if (copy_to_user(buf, kbuf, len)) {
|
||||
free_page((unsigned long)kbuf);
|
||||
return -EFAULT;
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
count -= len;
|
||||
buf += len;
|
||||
@@ -483,8 +488,8 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
|
||||
}
|
||||
free_page((unsigned long)kbuf);
|
||||
}
|
||||
*ppos = p;
|
||||
return read;
|
||||
*ppos = p;
|
||||
return read ? read : err;
|
||||
}
|
||||
|
||||
|
||||
@@ -553,6 +558,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
|
||||
ssize_t virtr = 0;
|
||||
ssize_t written;
|
||||
char * kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
|
||||
int err = 0;
|
||||
|
||||
if (p < (unsigned long) high_memory) {
|
||||
|
||||
@@ -576,13 +582,15 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
|
||||
while (count > 0) {
|
||||
int len = size_inside_page(p, count);
|
||||
|
||||
if (!is_vmalloc_or_module_addr((void *)p)) {
|
||||
err = -ENXIO;
|
||||
break;
|
||||
}
|
||||
if (len) {
|
||||
written = copy_from_user(kbuf, buf, len);
|
||||
if (written) {
|
||||
if (wrote + virtr)
|
||||
break;
|
||||
free_page((unsigned long)kbuf);
|
||||
return -EFAULT;
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
len = vwrite(kbuf, (char *)p, len);
|
||||
@@ -594,8 +602,8 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
|
||||
free_page((unsigned long)kbuf);
|
||||
}
|
||||
|
||||
*ppos = p;
|
||||
return virtr + wrote;
|
||||
*ppos = p;
|
||||
return virtr + wrote ? : err;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user