tty/vt: handle bad user buffer in {G,P}IO_CMAP ioctl
authorMichael Gehring <mg@ebfe.org>
Wed, 21 Mar 2012 00:26:45 +0000 (01:26 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 9 Apr 2012 19:10:23 +0000 (12:10 -0700)
set_get_cmap() ignored the result of {get,put}_user(), causing ioctl(vt,
{G,P}IO_CMAP, 0xdeadbeef) to silently fail.

Another side effect of this: calling the PIO_CMAP ioctl with an invalid
buffer would zero the default colormap and the palette for all vts (all
colors set to black).

Leave the default colormap intact and return -EFAULT when
reading/writing to the userspace buffer fails.

Signed-off-by: Michael Gehring <mg@ebfe.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/vt/vt.c

index 3bdd4b1..5836289 100644 (file)
@@ -3893,36 +3893,6 @@ static void set_palette(struct vc_data *vc)
                vc->vc_sw->con_set_palette(vc, color_table);
 }
 
-static int set_get_cmap(unsigned char __user *arg, int set)
-{
-    int i, j, k;
-
-    WARN_CONSOLE_UNLOCKED();
-
-    for (i = 0; i < 16; i++)
-       if (set) {
-           get_user(default_red[i], arg++);
-           get_user(default_grn[i], arg++);
-           get_user(default_blu[i], arg++);
-       } else {
-           put_user(default_red[i], arg++);
-           put_user(default_grn[i], arg++);
-           put_user(default_blu[i], arg++);
-       }
-    if (set) {
-       for (i = 0; i < MAX_NR_CONSOLES; i++)
-           if (vc_cons_allocated(i)) {
-               for (j = k = 0; j < 16; j++) {
-                   vc_cons[i].d->vc_palette[k++] = default_red[j];
-                   vc_cons[i].d->vc_palette[k++] = default_grn[j];
-                   vc_cons[i].d->vc_palette[k++] = default_blu[j];
-               }
-               set_palette(vc_cons[i].d);
-           }
-    }
-    return 0;
-}
-
 /*
  * Load palette into the DAC registers. arg points to a colour
  * map, 3 bytes per colour, 16 colours, range from 0 to 255.
@@ -3930,24 +3900,50 @@ static int set_get_cmap(unsigned char __user *arg, int set)
 
 int con_set_cmap(unsigned char __user *arg)
 {
-       int rc;
+       int i, j, k;
+       unsigned char colormap[3*16];
+
+       if (copy_from_user(colormap, arg, sizeof(colormap)))
+               return -EFAULT;
 
        console_lock();
-       rc = set_get_cmap (arg,1);
+       for (i = k = 0; i < 16; i++) {
+               default_red[i] = colormap[k++];
+               default_grn[i] = colormap[k++];
+               default_blu[i] = colormap[k++];
+       }
+       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+               if (!vc_cons_allocated(i))
+                       continue;
+               for (j = k = 0; j < 16; j++) {
+                       vc_cons[i].d->vc_palette[k++] = default_red[j];
+                       vc_cons[i].d->vc_palette[k++] = default_grn[j];
+                       vc_cons[i].d->vc_palette[k++] = default_blu[j];
+               }
+               set_palette(vc_cons[i].d);
+       }
        console_unlock();
 
-       return rc;
+       return 0;
 }
 
 int con_get_cmap(unsigned char __user *arg)
 {
-       int rc;
+       int i, k;
+       unsigned char colormap[3*16];
 
        console_lock();
-       rc = set_get_cmap (arg,0);
+       for (i = k = 0; i < 16; i++) {
+               colormap[k++] = default_red[i];
+               colormap[k++] = default_grn[i];
+               colormap[k++] = default_blu[i];
+       }
        console_unlock();
 
-       return rc;
+       if (copy_to_user(arg, colormap, sizeof(colormap)))
+               return -EFAULT;
+
+       return 0;
 }
 
 void reset_palette(struct vc_data *vc)