Commit 921b7383 authored by Thomas Zimmermann's avatar Thomas Zimmermann

fbdev: Return number of bytes read or written

Always return the number of bytes read or written within the
framebuffer. Only return an errno code if framebuffer memory
was not touched. This is the semantics required by POSIX and
makes fb_read() and fb_write() compatible with IGT tests. [1]

This bug has been fixed for fb_write() long ago by
commit 6a2a8866 ("[PATCH] fbdev: Fix return error of
fb_write"). The code in fb_read() and the corresponding fb_sys_()
helpers was forgotten.

It can happen that copy_{from, to}_user() only partially copies
the given buffer. Take this into account when calculating the
number of bytes.

v2:
	* consider return value from copy_{from,to}_user() (Geert)
Signed-off-by: default avatarThomas Zimmermann <tzimmermann@suse.de>
Tested-by: default avatarSui Jingfeng <suijingfeng@loongson.cn>
Reviewed-by: default avatarJavier Martinez Canillas <javierm@redhat.com>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Acked-by: default avatarHelge Deller <deller@gmx.de>
Link: https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/blob/master/tests/fbdev.c # 1
Link: https://patchwork.freedesktop.org/patch/msgid/20230428122452.4856-15-tzimmermann@suse.de
parent 254a4fda
...@@ -19,7 +19,8 @@ ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count, ...@@ -19,7 +19,8 @@ ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count,
unsigned long p = *ppos; unsigned long p = *ppos;
void *src; void *src;
int err = 0; int err = 0;
unsigned long total_size; unsigned long total_size, c;
ssize_t ret;
if (info->state != FBINFO_STATE_RUNNING) if (info->state != FBINFO_STATE_RUNNING)
return -EPERM; return -EPERM;
...@@ -43,13 +44,14 @@ ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count, ...@@ -43,13 +44,14 @@ ssize_t fb_sys_read(struct fb_info *info, char __user *buf, size_t count,
if (info->fbops->fb_sync) if (info->fbops->fb_sync)
info->fbops->fb_sync(info); info->fbops->fb_sync(info);
if (copy_to_user(buf, src, count)) c = copy_to_user(buf, src, count);
if (c)
err = -EFAULT; err = -EFAULT;
ret = count - c;
if (!err) *ppos += ret;
*ppos += count;
return (err) ? err : count; return ret ? ret : err;
} }
EXPORT_SYMBOL_GPL(fb_sys_read); EXPORT_SYMBOL_GPL(fb_sys_read);
...@@ -59,7 +61,8 @@ ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, ...@@ -59,7 +61,8 @@ ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
unsigned long p = *ppos; unsigned long p = *ppos;
void *dst; void *dst;
int err = 0; int err = 0;
unsigned long total_size; unsigned long total_size, c;
size_t ret;
if (info->state != FBINFO_STATE_RUNNING) if (info->state != FBINFO_STATE_RUNNING)
return -EPERM; return -EPERM;
...@@ -89,13 +92,14 @@ ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, ...@@ -89,13 +92,14 @@ ssize_t fb_sys_write(struct fb_info *info, const char __user *buf,
if (info->fbops->fb_sync) if (info->fbops->fb_sync)
info->fbops->fb_sync(info); info->fbops->fb_sync(info);
if (copy_from_user(dst, buf, count)) c = copy_from_user(dst, buf, count);
if (c)
err = -EFAULT; err = -EFAULT;
ret = count - c;
if (!err) *ppos += ret;
*ppos += count;
return (err) ? err : count; return ret ? ret : err;
} }
EXPORT_SYMBOL_GPL(fb_sys_write); EXPORT_SYMBOL_GPL(fb_sys_write);
......
...@@ -766,7 +766,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ...@@ -766,7 +766,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
u8 *buffer, *dst; u8 *buffer, *dst;
u8 __iomem *src; u8 __iomem *src;
int c, cnt = 0, err = 0; int c, cnt = 0, err = 0;
unsigned long total_size; unsigned long total_size, trailing;
if (!info || ! info->screen_base) if (!info || ! info->screen_base)
return -ENODEV; return -ENODEV;
...@@ -808,10 +808,13 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ...@@ -808,10 +808,13 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
dst += c; dst += c;
src += c; src += c;
if (copy_to_user(buf, buffer, c)) { trailing = copy_to_user(buf, buffer, c);
if (trailing == c) {
err = -EFAULT; err = -EFAULT;
break; break;
} }
c -= trailing;
*ppos += c; *ppos += c;
buf += c; buf += c;
cnt += c; cnt += c;
...@@ -820,7 +823,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ...@@ -820,7 +823,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
kfree(buffer); kfree(buffer);
return (err) ? err : cnt; return cnt ? cnt : err;
} }
static ssize_t static ssize_t
...@@ -831,7 +834,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) ...@@ -831,7 +834,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
u8 *buffer, *src; u8 *buffer, *src;
u8 __iomem *dst; u8 __iomem *dst;
int c, cnt = 0, err = 0; int c, cnt = 0, err = 0;
unsigned long total_size; unsigned long total_size, trailing;
if (!info || !info->screen_base) if (!info || !info->screen_base)
return -ENODEV; return -ENODEV;
...@@ -876,10 +879,12 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) ...@@ -876,10 +879,12 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
c = (count > PAGE_SIZE) ? PAGE_SIZE : count; c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
src = buffer; src = buffer;
if (copy_from_user(src, buf, c)) { trailing = copy_from_user(src, buf, c);
if (trailing == c) {
err = -EFAULT; err = -EFAULT;
break; break;
} }
c -= trailing;
fb_memcpy_tofb(dst, src, c); fb_memcpy_tofb(dst, src, c);
dst += c; dst += c;
......
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