Commit 39939350 authored by Antonino Daplas's avatar Antonino Daplas Committed by Linus Torvalds

[PATCH] fbdev: fix copy_to/from_user in fbmem.c:fb_read/write

This patch fixes a problem reported by David S. Miller <davem@redhat.com>

"I just noticed that fb_{read,write}() uses copy_*_user() with
 the kernel buffer being the frame buffer.  It needs to use
 the proper device address accessor functions."

The patch will do an intermediate copy of the contents to a page-sized,
kmalloc'ed buffer.
Signed-off-by: default avatarAntonino Daplas <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6254ea38
...@@ -878,6 +878,8 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ...@@ -878,6 +878,8 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
int fbidx = iminor(inode); int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx]; struct fb_info *info = registered_fb[fbidx];
u32 *buffer, *dst, *src;
int c, i, cnt = 0, err = 0;
if (!info || ! info->screen_base) if (!info || ! info->screen_base)
return -ENODEV; return -ENODEV;
...@@ -894,18 +896,45 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ...@@ -894,18 +896,45 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
count = info->fix.smem_len; count = info->fix.smem_len;
if (count + p > info->fix.smem_len) if (count + p > info->fix.smem_len)
count = info->fix.smem_len - p; count = info->fix.smem_len - p;
cnt = 0;
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
src = (u32 *) (info->screen_base + p);
if (info->fbops->fb_sync) if (info->fbops->fb_sync)
info->fbops->fb_sync(info); info->fbops->fb_sync(info);
if (count) {
char *base_addr;
base_addr = info->screen_base; while (count) {
count -= copy_to_user(buf, base_addr+p, count); c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
if (!count) dst = buffer;
return -EFAULT; for (i = c >> 2; i--; )
*ppos += count; *dst++ = fb_readl(src++);
if (c & 3) {
u8 *dst8 = (u8 *) dst;
u8 *src8 = (u8 *) src;
for (i = c & 3; i--;)
*dst8++ = fb_readb(src8++);
src = (u32 *) src8;
}
if (copy_to_user(buf, buffer, c)) {
err = -EFAULT;
break;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
} }
return count;
kfree(buffer);
return (err) ? err : cnt;
} }
static ssize_t static ssize_t
...@@ -915,7 +944,8 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) ...@@ -915,7 +944,8 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
int fbidx = iminor(inode); int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx]; struct fb_info *info = registered_fb[fbidx];
int err; u32 *buffer, *dst, *src;
int c, i, cnt = 0, err;
if (!info || !info->screen_base) if (!info || !info->screen_base)
return -ENODEV; return -ENODEV;
...@@ -935,19 +965,43 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) ...@@ -935,19 +965,43 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
count = info->fix.smem_len - p; count = info->fix.smem_len - p;
err = -ENOSPC; err = -ENOSPC;
} }
cnt = 0;
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
dst = (u32 *) (info->screen_base + p);
if (info->fbops->fb_sync) if (info->fbops->fb_sync)
info->fbops->fb_sync(info); info->fbops->fb_sync(info);
if (count) {
char *base_addr;
base_addr = info->screen_base; while (count) {
count -= copy_from_user(base_addr+p, buf, count); c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
*ppos += count; src = buffer;
err = -EFAULT; if (copy_from_user(src, buf, c)) {
err = -EFAULT;
break;
}
for (i = c >> 2; i--; )
fb_writel(*src++, dst++);
if (c & 3) {
u8 *src8 = (u8 *) src;
u8 *dst8 = (u8 *) dst;
for (i = c & 3; i--; )
fb_writeb(*src8++, dst8++);
dst = (u32 *) dst8;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
} }
if (count) kfree(buffer);
return count;
return err; return (err) ? err : cnt;
} }
#ifdef CONFIG_KMOD #ifdef CONFIG_KMOD
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment