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>

authored by Dan Carpenter and committed by Paul Mundt 1e7c7804 c353103d

+21 -8
+20 -8
drivers/video/fbcmap.c
··· 88 * 89 */ 90 91 - int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) 92 { 93 int size = len * sizeof(u16); 94 95 if (cmap->len != len) { 96 fb_dealloc_cmap(cmap); 97 if (!len) 98 return 0; 99 100 - cmap->red = kmalloc(size, GFP_ATOMIC); 101 if (!cmap->red) 102 goto fail; 103 - cmap->green = kmalloc(size, GFP_ATOMIC); 104 if (!cmap->green) 105 goto fail; 106 - cmap->blue = kmalloc(size, GFP_ATOMIC); 107 if (!cmap->blue) 108 goto fail; 109 if (transp) { 110 - cmap->transp = kmalloc(size, GFP_ATOMIC); 111 if (!cmap->transp) 112 goto fail; 113 } else { ··· 117 } 118 cmap->start = 0; 119 cmap->len = len; 120 - fb_copy_cmap(fb_default_cmap(len), cmap); 121 return 0; 122 123 fail: 124 fb_dealloc_cmap(cmap); 125 - return -ENOMEM; 126 } 127 128 /** ··· 264 int rc, size = cmap->len * sizeof(u16); 265 struct fb_cmap umap; 266 267 memset(&umap, 0, sizeof(struct fb_cmap)); 268 - rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL); 269 if (rc) 270 return rc; 271 if (copy_from_user(umap.red, cmap->red, size) ||
··· 88 * 89 */ 90 91 + int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags) 92 { 93 int size = len * sizeof(u16); 94 + int ret = -ENOMEM; 95 96 if (cmap->len != len) { 97 fb_dealloc_cmap(cmap); 98 if (!len) 99 return 0; 100 101 + cmap->red = kmalloc(size, flags); 102 if (!cmap->red) 103 goto fail; 104 + cmap->green = kmalloc(size, flags); 105 if (!cmap->green) 106 goto fail; 107 + cmap->blue = kmalloc(size, flags); 108 if (!cmap->blue) 109 goto fail; 110 if (transp) { 111 + cmap->transp = kmalloc(size, flags); 112 if (!cmap->transp) 113 goto fail; 114 } else { ··· 116 } 117 cmap->start = 0; 118 cmap->len = len; 119 + ret = fb_copy_cmap(fb_default_cmap(len), cmap); 120 + if (ret) 121 + goto fail; 122 return 0; 123 124 fail: 125 fb_dealloc_cmap(cmap); 126 + return ret; 127 + } 128 + 129 + int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp) 130 + { 131 + return fb_alloc_cmap_gfp(cmap, len, transp, GFP_ATOMIC); 132 } 133 134 /** ··· 256 int rc, size = cmap->len * sizeof(u16); 257 struct fb_cmap umap; 258 259 + if (size < 0 || size < cmap->len) 260 + return -E2BIG; 261 + 262 memset(&umap, 0, sizeof(struct fb_cmap)); 263 + rc = fb_alloc_cmap_gfp(&umap, cmap->len, cmap->transp != NULL, 264 + GFP_KERNEL); 265 if (rc) 266 return rc; 267 if (copy_from_user(umap.red, cmap->red, size) ||
+1
include/linux/fb.h
··· 1122 1123 /* drivers/video/fbcmap.c */ 1124 extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); 1125 extern void fb_dealloc_cmap(struct fb_cmap *cmap); 1126 extern int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to); 1127 extern int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to);
··· 1122 1123 /* drivers/video/fbcmap.c */ 1124 extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); 1125 + extern int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags); 1126 extern void fb_dealloc_cmap(struct fb_cmap *cmap); 1127 extern int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to); 1128 extern int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to);