aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Carpenter <error27@gmail.com>2010-11-16 04:11:02 -0500
committerPaul Mundt <lethal@linux-sh.org>2010-11-17 00:55:45 -0500
commit1e7c7804884fc5751e3872f13498fd533325f8b2 (patch)
tree1d9a9ead0d2c7cb18e46cc35878a0d64daaeb545
parentc353103de8795358af1584088aa471571decb307 (diff)
fbcmap: integer overflow bug
There is an integer overflow in fb_set_user_cmap() because cmap->len * 2 can wrap. It's basically harmless. Your terminal will be messed up until you type reset. This patch does three things to fix the bug. First, it checks the return value of fb_copy_cmap() in fb_alloc_cmap(). That is enough to fix address the overflow. Second it checks for the integer overflow in fb_set_user_cmap(). Lastly I wanted to cap "cmap->len" in fb_set_user_cmap() much lower because it gets used to determine the size of allocation. Unfortunately no one knows what the limit should be. Instead what this patch does is makes the allocation happen with GFP_KERNEL instead of GFP_ATOMIC and lets the kmalloc() decide what values of cmap->len are reasonable. To do this, the patch introduces a function called fb_alloc_cmap_gfp() which is like fb_alloc_cmap() except that it takes a GFP flag. Signed-off-by: Dan Carpenter <error27@gmail.com> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--drivers/video/fbcmap.c28
-rw-r--r--include/linux/fb.h1
2 files changed, 21 insertions, 8 deletions
diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c
index a79b9762901..affdf3e32cf 100644
--- a/drivers/video/fbcmap.c
+++ b/drivers/video/fbcmap.c
@@ -88,26 +88,27 @@ static const struct fb_cmap default_16_colors = {
88 * 88 *
89 */ 89 */
90 90
91int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) 91int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags)
92{ 92{
93 int size = len * sizeof(u16); 93 int size = len * sizeof(u16);
94 int ret = -ENOMEM;
94 95
95 if (cmap->len != len) { 96 if (cmap->len != len) {
96 fb_dealloc_cmap(cmap); 97 fb_dealloc_cmap(cmap);
97 if (!len) 98 if (!len)
98 return 0; 99 return 0;
99 100
100 cmap->red = kmalloc(size, GFP_ATOMIC); 101 cmap->red = kmalloc(size, flags);
101 if (!cmap->red) 102 if (!cmap->red)
102 goto fail; 103 goto fail;
103 cmap->green = kmalloc(size, GFP_ATOMIC); 104 cmap->green = kmalloc(size, flags);
104 if (!cmap->green) 105 if (!cmap->green)
105 goto fail; 106 goto fail;
106 cmap->blue = kmalloc(size, GFP_ATOMIC); 107 cmap->blue = kmalloc(size, flags);
107 if (!cmap->blue) 108 if (!cmap->blue)
108 goto fail; 109 goto fail;
109 if (transp) { 110 if (transp) {
110 cmap->transp = kmalloc(size, GFP_ATOMIC); 111 cmap->transp = kmalloc(size, flags);
111 if (!cmap->transp) 112 if (!cmap->transp)
112 goto fail; 113 goto fail;
113 } else { 114 } else {
@@ -116,12 +117,19 @@ int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
116 } 117 }
117 cmap->start = 0; 118 cmap->start = 0;
118 cmap->len = len; 119 cmap->len = len;
119 fb_copy_cmap(fb_default_cmap(len), cmap); 120 ret = fb_copy_cmap(fb_default_cmap(len), cmap);
121 if (ret)
122 goto fail;
120 return 0; 123 return 0;
121 124
122fail: 125fail:
123 fb_dealloc_cmap(cmap); 126 fb_dealloc_cmap(cmap);
124 return -ENOMEM; 127 return ret;
128}
129
130int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
131{
132 return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC);
125} 133}
126 134
127/** 135/**
@@ -256,8 +264,12 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
256 int rc, size = cmap->len * sizeof(u16); 264 int rc, size = cmap->len * sizeof(u16);
257 struct fb_cmap umap; 265 struct fb_cmap umap;
258 266
267 if (size < 0 || size < cmap->len)
268 return -E2BIG;
269
259 memset(&umap, 0, sizeof(struct fb_cmap)); 270 memset(&umap, 0, sizeof(struct fb_cmap));
260 rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL); 271 rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL,
272 GFP_KERNEL);
261 if (rc) 273 if (rc)
262 return rc; 274 return rc;
263 if (copy_from_user(umap.red, cmap->red, size) || 275 if (copy_from_user(umap.red, cmap->red, size) ||
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 7fca3dc4e47..d1631d37e9e 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1122,6 +1122,7 @@ extern const struct fb_videomode *fb_find_best_display(const struct fb_monspecs
1122 1122
1123/* drivers/video/fbcmap.c */ 1123/* drivers/video/fbcmap.c */
1124extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); 1124extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
1125extern int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags);
1125extern void fb_dealloc_cmap(struct fb_cmap *cmap); 1126extern void fb_dealloc_cmap(struct fb_cmap *cmap);
1126extern int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to); 1127extern int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to);
1127extern int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to); 1128extern int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to);