diff options
author | Andrea Righi <righi.andrea@gmail.com> | 2009-02-04 18:12:03 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-02-05 15:56:46 -0500 |
commit | 1f5e31d7e55ac7fbd4ec5e5b20c8868b0e4564c9 (patch) | |
tree | 713d0ace63c95da9b989aafce8ec84ebb1d1cbc3 /drivers/video/fbcmap.c | |
parent | afd8d0f940ba5078f38e435440089117ac7d9eb4 (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/fbcmap.c')
-rw-r--r-- | drivers/video/fbcmap.c | 20 |
1 files changed, 14 insertions, 6 deletions
diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index 91b78e691505..f53b9f1d6aba 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c | |||
@@ -250,10 +250,6 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info) | |||
250 | int rc, size = cmap->len * sizeof(u16); | 250 | int rc, size = cmap->len * sizeof(u16); |
251 | struct fb_cmap umap; | 251 | struct fb_cmap umap; |
252 | 252 | ||
253 | if (cmap->start < 0 || (!info->fbops->fb_setcolreg && | ||
254 | !info->fbops->fb_setcmap)) | ||
255 | return -EINVAL; | ||
256 | |||
257 | memset(&umap, 0, sizeof(struct fb_cmap)); | 253 | memset(&umap, 0, sizeof(struct fb_cmap)); |
258 | rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL); | 254 | rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL); |
259 | if (rc) | 255 | if (rc) |
@@ -262,11 +258,23 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info) | |||
262 | copy_from_user(umap.green, cmap->green, size) || | 258 | copy_from_user(umap.green, cmap->green, size) || |
263 | copy_from_user(umap.blue, cmap->blue, size) || | 259 | copy_from_user(umap.blue, cmap->blue, size) || |
264 | (cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) { | 260 | (cmap->transp && copy_from_user(umap.transp, cmap->transp, size))) { |
265 | fb_dealloc_cmap(&umap); | 261 | rc = -EFAULT; |
266 | return -EFAULT; | 262 | goto out; |
267 | } | 263 | } |
268 | umap.start = cmap->start; | 264 | umap.start = cmap->start; |
265 | if (!lock_fb_info(info)) { | ||
266 | rc = -ENODEV; | ||
267 | goto out; | ||
268 | } | ||
269 | if (cmap->start < 0 || (!info->fbops->fb_setcolreg && | ||
270 | !info->fbops->fb_setcmap)) { | ||
271 | rc = -EINVAL; | ||
272 | goto out1; | ||
273 | } | ||
269 | rc = fb_set_cmap(&umap, info); | 274 | rc = fb_set_cmap(&umap, info); |
275 | out1: | ||
276 | unlock_fb_info(info); | ||
277 | out: | ||
270 | fb_dealloc_cmap(&umap); | 278 | fb_dealloc_cmap(&umap); |
271 | return rc; | 279 | return rc; |
272 | } | 280 | } |