aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/fbmem.c
diff options
context:
space:
mode:
authorAndrea Righi2009-02-04 15:12:03 -0800
committerLinus Torvalds2009-02-05 12:56:46 -0800
commit1f5e31d7e55ac7fbd4ec5e5b20c8868b0e4564c9 (patch)
tree713d0ace63c95da9b989aafce8ec84ebb1d1cbc3 /drivers/video/fbmem.c
parentafd8d0f940ba5078f38e435440089117ac7d9eb4 (diff)
fbmem: don't call copy_from/to_user() with mutex held
Avoid calling copy_from/to_user() with fb_info->lock mutex held in fbmem ioctl(). fb_mmap() is called under mm->mmap_sem (A) held, that also acquires fb_info->lock (B); fb_ioctl() takes fb_info->lock (B) and does copy_from/to_user() that might acquire mm->mmap_sem (A), causing a deadlock. NOTE: it doesn't push down the fb_info->lock in each own driver's fb_ioctl(), so there are still potential deadlocks elsewhere. Signed-off-by: Andrea Righi <righi.andrea@gmail.com> Cc: Dave Jones <davej@redhat.com> Cc: "Rafael J. Wysocki" <rjw@sisk.pl> Cc: Johannes Weiner <hannes@saeurebad.de> Cc: Krzysztof Helt <krzysztof.h1@wp.pl> Cc: Harvey Harrison <harvey.harrison@gmail.com> Cc: Stefan Richter <stefanr@s5r6.in-berlin.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/fbmem.c')
-rw-r--r--drivers/video/fbmem.c135
1 files changed, 69 insertions, 66 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 756efeb91abc..cfd9dce1ce0b 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1013,132 +1013,139 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
struct fb_con2fbmap con2fb;
+ struct fb_cmap cmap_from;
struct fb_cmap_user cmap;
struct fb_event event;
void __user *argp = (void __user *)arg;
long ret = 0;
- fb = info->fbops;
- if (!fb)
- return -ENODEV;
-
switch (cmd) {
case FBIOGET_VSCREENINFO:
- ret = copy_to_user(argp, &info->var,
- sizeof(var)) ? -EFAULT : 0;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ var = info->var;
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
break;
case FBIOPUT_VSCREENINFO:
- if (copy_from_user(&var, argp, sizeof(var))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&var, argp, sizeof(var)))
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
ret = fb_set_var(info, &var);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
- if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
+ unlock_fb_info(info);
+ if (!ret && copy_to_user(argp, &var, sizeof(var)))
ret = -EFAULT;
break;
case FBIOGET_FSCREENINFO:
- ret = copy_to_user(argp, &info->fix,
- sizeof(fix)) ? -EFAULT : 0;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ fix = info->fix;
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
break;
case FBIOPUTCMAP:
if (copy_from_user(&cmap, argp, sizeof(cmap)))
- ret = -EFAULT;
- else
- ret = fb_set_user_cmap(&cmap, info);
+ return -EFAULT;
+ ret = fb_set_user_cmap(&cmap, info);
break;
case FBIOGETCMAP:
if (copy_from_user(&cmap, argp, sizeof(cmap)))
- ret = -EFAULT;
- else
- ret = fb_cmap_to_user(&info->cmap, &cmap);
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ cmap_from = info->cmap;
+ unlock_fb_info(info);
+ ret = fb_cmap_to_user(&cmap_from, &cmap);
break;
case FBIOPAN_DISPLAY:
- if (copy_from_user(&var, argp, sizeof(var))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&var, argp, sizeof(var)))
+ return -EFAULT;
+ if (!lock_fb_info(info))
+ return -ENODEV;
acquire_console_sem();
ret = fb_pan_display(info, &var);
release_console_sem();
+ unlock_fb_info(info);
if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
- ret = -EFAULT;
+ return -EFAULT;
break;
case FBIO_CURSOR:
ret = -EINVAL;
break;
case FBIOGET_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
- ret = -EFAULT;
- else if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
- ret = -EINVAL;
- else {
- con2fb.framebuffer = -1;
- event.info = info;
- event.data = &con2fb;
- fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP,
- &event);
- ret = copy_to_user(argp, &con2fb,
- sizeof(con2fb)) ? -EFAULT : 0;
- }
+ return -EFAULT;
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+ return -EINVAL;
+ con2fb.framebuffer = -1;
+ event.data = &con2fb;
+
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ event.info = info;
+ fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);
+ unlock_fb_info(info);
+
+ ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
break;
case FBIOPUT_CON2FBMAP:
- if (copy_from_user(&con2fb, argp, sizeof(con2fb))) {
- ret = -EFAULT;
- break;
- }
- if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) {
- ret = -EINVAL;
- break;
- }
- if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) {
- ret = -EINVAL;
- break;
- }
+ if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
+ return -EFAULT;
+ if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
+ return -EINVAL;
+ if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
+ return -EINVAL;
if (!registered_fb[con2fb.framebuffer])
request_module("fb%d", con2fb.framebuffer);
if (!registered_fb[con2fb.framebuffer]) {
ret = -EINVAL;
break;
}
- event.info = info;
event.data = &con2fb;
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ event.info = info;
ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP,
&event);
+ unlock_fb_info(info);
break;
case FBIOBLANK:
+ if (!lock_fb_info(info))
+ return -ENODEV;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
ret = fb_blank(info, arg);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
- break;;
+ unlock_fb_info(info);
+ break;
default:
- if (fb->fb_ioctl == NULL)
- ret = -ENOTTY;
- else
+ if (!lock_fb_info(info))
+ return -ENODEV;
+ fb = info->fbops;
+ if (fb->fb_ioctl)
ret = fb->fb_ioctl(info, cmd, arg);
+ else
+ ret = -ENOTTY;
+ unlock_fb_info(info);
}
return ret;
}
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-__acquires(&info->lock)
-__releases(&info->lock)
{
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
- struct fb_info *info;
- long ret;
+ struct fb_info *info = registered_fb[fbidx];
- info = registered_fb[fbidx];
- mutex_lock(&info->lock);
- ret = do_fb_ioctl(info, cmd, arg);
- mutex_unlock(&info->lock);
- return ret;
+ return do_fb_ioctl(info, cmd, arg);
}
#ifdef CONFIG_COMPAT
@@ -1257,8 +1264,6 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
static long fb_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
-__acquires(&info->lock)
-__releases(&info->lock)
{
struct inode *inode = file->f_path.dentry->d_inode;
int fbidx = iminor(inode);
@@ -1266,7 +1271,6 @@ __releases(&info->lock)
struct fb_ops *fb = info->fbops;
long ret = -ENOIOCTLCMD;
- mutex_lock(&info->lock);
switch(cmd) {
case FBIOGET_VSCREENINFO:
case FBIOPUT_VSCREENINFO:
@@ -1292,7 +1296,6 @@ __releases(&info->lock)
ret = fb->fb_compat_ioctl(info, cmd, arg);
break;
}
- mutex_unlock(&info->lock);
return ret;
}
#endif