aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fbmem.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-11 17:49:36 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-12 10:37:51 -0400
commit698b368275c3fa98261159253cfc79653f9dffc6 (patch)
treeb92c921fe6522ece33fbbde33cc173c9dd32d9a2 /drivers/video/fbmem.c
parent9f381a61f58bb6487c93ce2233bb9992f8ea9211 (diff)
fbcon: add lifetime refcount to opened frame buffers
This just adds the refcount and the new registration lock logic. It does not (for example) actually change the read/write/ioctl routines to actually use the frame buffer that was opened: those function still end up alway susing whatever the current frame buffer is at the time of the call. Without this, if something holds the frame buffer open over a framebuffer switch, the close() operation after the switch will access a fb_info that has been free'd by the unregistering of the old frame buffer. (The read/write/ioctl operations will normally not cause problems, because they will - illogically - pick up the new fbcon instead. But a switch that happens just as one of those is going on might see problems too, the window is just much smaller: one individual op rather than the whole open-close sequence.) This use-after-free is apparently fairly easily triggered by the Ubuntu 11.04 boot sequence. Acked-by: Tim Gardner <tim.gardner@canonical.com> Tested-by: Daniel J Blueman <daniel.blueman@gmail.com> Tested-by: Anca Emanuel <anca.emanuel@gmail.com> Cc: Bruno Prémont <bonbons@linux-vserver.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: Paul Mundt <lethal@linux-sh.org> Cc: Dave Airlie <airlied@redhat.com> Cc: Andy Whitcroft <andy.whitcroft@canonical.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/fbmem.c')
-rw-r--r--drivers/video/fbmem.c56
1 files changed, 46 insertions, 10 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index e0c2284924b6..eec14d2ca1c7 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -42,9 +42,34 @@
42 42
43#define FBPIXMAPSIZE (1024 * 8) 43#define FBPIXMAPSIZE (1024 * 8)
44 44
45static DEFINE_MUTEX(registration_lock);
45struct fb_info *registered_fb[FB_MAX] __read_mostly; 46struct fb_info *registered_fb[FB_MAX] __read_mostly;
46int num_registered_fb __read_mostly; 47int num_registered_fb __read_mostly;
47 48
49static struct fb_info *get_fb_info(unsigned int idx)
50{
51 struct fb_info *fb_info;
52
53 if (idx >= FB_MAX)
54 return ERR_PTR(-ENODEV);
55
56 mutex_lock(&registration_lock);
57 fb_info = registered_fb[idx];
58 if (fb_info)
59 atomic_inc(&fb_info->count);
60 mutex_unlock(&registration_lock);
61
62 return fb_info;
63}
64
65static void put_fb_info(struct fb_info *fb_info)
66{
67 if (!atomic_dec_and_test(&fb_info->count))
68 return;
69 if (fb_info->fbops->fb_destroy)
70 fb_info->fbops->fb_destroy(fb_info);
71}
72
48int lock_fb_info(struct fb_info *info) 73int lock_fb_info(struct fb_info *info)
49{ 74{
50 mutex_lock(&info->lock); 75 mutex_lock(&info->lock);
@@ -647,6 +672,7 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; }
647 672
648static void *fb_seq_start(struct seq_file *m, loff_t *pos) 673static void *fb_seq_start(struct seq_file *m, loff_t *pos)
649{ 674{
675 mutex_lock(&registration_lock);
650 return (*pos < FB_MAX) ? pos : NULL; 676 return (*pos < FB_MAX) ? pos : NULL;
651} 677}
652 678
@@ -658,6 +684,7 @@ static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos)
658 684
659static void fb_seq_stop(struct seq_file *m, void *v) 685static void fb_seq_stop(struct seq_file *m, void *v)
660{ 686{
687 mutex_unlock(&registration_lock);
661} 688}
662 689
663static int fb_seq_show(struct seq_file *m, void *v) 690static int fb_seq_show(struct seq_file *m, void *v)
@@ -1361,14 +1388,16 @@ __releases(&info->lock)
1361 struct fb_info *info; 1388 struct fb_info *info;
1362 int res = 0; 1389 int res = 0;
1363 1390
1364 if (fbidx >= FB_MAX) 1391 info = get_fb_info(fbidx);
1365 return -ENODEV; 1392 if (!info) {
1366 info = registered_fb[fbidx];
1367 if (!info)
1368 request_module("fb%d", fbidx); 1393 request_module("fb%d", fbidx);
1369 info = registered_fb[fbidx]; 1394 info = get_fb_info(fbidx);
1370 if (!info) 1395 if (!info)
1371 return -ENODEV; 1396 return -ENODEV;
1397 }
1398 if (IS_ERR(info))
1399 return PTR_ERR(info);
1400
1372 mutex_lock(&info->lock); 1401 mutex_lock(&info->lock);
1373 if (!try_module_get(info->fbops->owner)) { 1402 if (!try_module_get(info->fbops->owner)) {
1374 res = -ENODEV; 1403 res = -ENODEV;
@@ -1386,6 +1415,8 @@ __releases(&info->lock)
1386#endif 1415#endif
1387out: 1416out:
1388 mutex_unlock(&info->lock); 1417 mutex_unlock(&info->lock);
1418 if (res)
1419 put_fb_info(info);
1389 return res; 1420 return res;
1390} 1421}
1391 1422
@@ -1401,6 +1432,7 @@ __releases(&info->lock)
1401 info->fbops->fb_release(info,1); 1432 info->fbops->fb_release(info,1);
1402 module_put(info->fbops->owner); 1433 module_put(info->fbops->owner);
1403 mutex_unlock(&info->lock); 1434 mutex_unlock(&info->lock);
1435 put_fb_info(info);
1404 return 0; 1436 return 0;
1405} 1437}
1406 1438
@@ -1542,11 +1574,13 @@ register_framebuffer(struct fb_info *fb_info)
1542 remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, 1574 remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
1543 fb_is_primary_device(fb_info)); 1575 fb_is_primary_device(fb_info));
1544 1576
1577 mutex_lock(&registration_lock);
1545 num_registered_fb++; 1578 num_registered_fb++;
1546 for (i = 0 ; i < FB_MAX; i++) 1579 for (i = 0 ; i < FB_MAX; i++)
1547 if (!registered_fb[i]) 1580 if (!registered_fb[i])
1548 break; 1581 break;
1549 fb_info->node = i; 1582 fb_info->node = i;
1583 atomic_set(&fb_info->count, 1);
1550 mutex_init(&fb_info->lock); 1584 mutex_init(&fb_info->lock);
1551 mutex_init(&fb_info->mm_lock); 1585 mutex_init(&fb_info->mm_lock);
1552 1586
@@ -1583,6 +1617,7 @@ register_framebuffer(struct fb_info *fb_info)
1583 fb_var_to_videomode(&mode, &fb_info->var); 1617 fb_var_to_videomode(&mode, &fb_info->var);
1584 fb_add_videomode(&mode, &fb_info->modelist); 1618 fb_add_videomode(&mode, &fb_info->modelist);
1585 registered_fb[i] = fb_info; 1619 registered_fb[i] = fb_info;
1620 mutex_unlock(&registration_lock);
1586 1621
1587 event.info = fb_info; 1622 event.info = fb_info;
1588 if (!lock_fb_info(fb_info)) 1623 if (!lock_fb_info(fb_info))
@@ -1616,6 +1651,7 @@ unregister_framebuffer(struct fb_info *fb_info)
1616 struct fb_event event; 1651 struct fb_event event;
1617 int i, ret = 0; 1652 int i, ret = 0;
1618 1653
1654 mutex_lock(&registration_lock);
1619 i = fb_info->node; 1655 i = fb_info->node;
1620 if (!registered_fb[i]) { 1656 if (!registered_fb[i]) {
1621 ret = -EINVAL; 1657 ret = -EINVAL;
@@ -1638,7 +1674,7 @@ unregister_framebuffer(struct fb_info *fb_info)
1638 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) 1674 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1639 kfree(fb_info->pixmap.addr); 1675 kfree(fb_info->pixmap.addr);
1640 fb_destroy_modelist(&fb_info->modelist); 1676 fb_destroy_modelist(&fb_info->modelist);
1641 registered_fb[i]=NULL; 1677 registered_fb[i] = NULL;
1642 num_registered_fb--; 1678 num_registered_fb--;
1643 fb_cleanup_device(fb_info); 1679 fb_cleanup_device(fb_info);
1644 device_destroy(fb_class, MKDEV(FB_MAJOR, i)); 1680 device_destroy(fb_class, MKDEV(FB_MAJOR, i));
@@ -1646,9 +1682,9 @@ unregister_framebuffer(struct fb_info *fb_info)
1646 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); 1682 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
1647 1683
1648 /* this may free fb info */ 1684 /* this may free fb info */
1649 if (fb_info->fbops->fb_destroy) 1685 put_fb_info(fb_info);
1650 fb_info->fbops->fb_destroy(fb_info);
1651done: 1686done:
1687 mutex_unlock(&registration_lock);
1652 return ret; 1688 return ret;
1653} 1689}
1654 1690