Commit 1e7c7804 authored by Dan Carpenter's avatar Dan Carpenter Committed by Paul Mundt

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: default avatarDan Carpenter <error27@gmail.com>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent c353103d
...@@ -88,26 +88,27 @@ static const struct fb_cmap default_16_colors = { ...@@ -88,26 +88,27 @@ static const struct fb_cmap default_16_colors = {
* *
*/ */
int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags)
{ {
int size = len * sizeof(u16); int size = len * sizeof(u16);
int ret = -ENOMEM;
if (cmap->len != len) { if (cmap->len != len) {
fb_dealloc_cmap(cmap); fb_dealloc_cmap(cmap);
if (!len) if (!len)
return 0; return 0;
cmap->red = kmalloc(size, GFP_ATOMIC); cmap->red = kmalloc(size, flags);
if (!cmap->red) if (!cmap->red)
goto fail; goto fail;
cmap->green = kmalloc(size, GFP_ATOMIC); cmap->green = kmalloc(size, flags);
if (!cmap->green) if (!cmap->green)
goto fail; goto fail;
cmap->blue = kmalloc(size, GFP_ATOMIC); cmap->blue = kmalloc(size, flags);
if (!cmap->blue) if (!cmap->blue)
goto fail; goto fail;
if (transp) { if (transp) {
cmap->transp = kmalloc(size, GFP_ATOMIC); cmap->transp = kmalloc(size, flags);
if (!cmap->transp) if (!cmap->transp)
goto fail; goto fail;
} else { } else {
...@@ -116,12 +117,19 @@ int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) ...@@ -116,12 +117,19 @@ int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
} }
cmap->start = 0; cmap->start = 0;
cmap->len = len; cmap->len = len;
fb_copy_cmap(fb_default_cmap(len), cmap); ret = fb_copy_cmap(fb_default_cmap(len), cmap);
if (ret)
goto fail;
return 0; return 0;
fail: fail:
fb_dealloc_cmap(cmap); fb_dealloc_cmap(cmap);
return -ENOMEM; return ret;
}
int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
{
return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC);
} }
/** /**
...@@ -256,8 +264,12 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info) ...@@ -256,8 +264,12 @@ int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
int rc, size = cmap->len * sizeof(u16); int rc, size = cmap->len * sizeof(u16);
struct fb_cmap umap; struct fb_cmap umap;
if (size < 0 || size < cmap->len)
return -E2BIG;
memset(&umap, 0, sizeof(struct fb_cmap)); memset(&umap, 0, sizeof(struct fb_cmap));
rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL); rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL,
GFP_KERNEL);
if (rc) if (rc)
return rc; return rc;
if (copy_from_user(umap.red, cmap->red, size) || if (copy_from_user(umap.red, cmap->red, size) ||
......
...@@ -1122,6 +1122,7 @@ extern const struct fb_videomode *fb_find_best_display(const struct fb_monspecs ...@@ -1122,6 +1122,7 @@ extern const struct fb_videomode *fb_find_best_display(const struct fb_monspecs
/* drivers/video/fbcmap.c */ /* drivers/video/fbcmap.c */
extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
extern int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags);
extern void fb_dealloc_cmap(struct fb_cmap *cmap); extern void fb_dealloc_cmap(struct fb_cmap *cmap);
extern int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to); extern int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to);
extern int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to); extern int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to);
......
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