USB: keyspan: fix port DMA-buffer allocations
authorJohan Hovold <jhovold@gmail.com>
Tue, 13 Aug 2013 11:27:37 +0000 (13:27 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 14 Aug 2013 20:51:02 +0000 (13:51 -0700)
Make sure port DMA-buffers are allocated separately from containing
structure to prevent potential memory corruption on non-cache-coherent
systems.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/serial/keyspan.c

index 3d2ce56e10098329e09970510d54d342ec92a524..e731bbc166a0438bce3ce448dfcfa12868b87a9e 100644 (file)
 #define INSTAT_BUFLEN  32
 #define GLOCONT_BUFLEN 64
 #define INDAT49W_BUFLEN        512
+#define IN_BUFLEN      64
+#define OUT_BUFLEN     64
+#define INACK_BUFLEN   1
+#define OUTCONT_BUFLEN 64
 
        /* Per device and per port private data */
 struct keyspan_serial_private {
@@ -81,18 +85,18 @@ struct keyspan_port_private {
 
        /* Input endpoints and buffer for this port */
        struct urb      *in_urbs[2];
-       char            in_buffer[2][64];
+       char            *in_buffer[2];
        /* Output endpoints and buffer for this port */
        struct urb      *out_urbs[2];
-       char            out_buffer[2][64];
+       char            *out_buffer[2];
 
        /* Input ack endpoint */
        struct urb      *inack_urb;
-       char            inack_buffer[1];
+       char            *inack_buffer;
 
        /* Output control endpoint */
        struct urb      *outcont_urb;
-       char            outcont_buffer[64];
+       char            *outcont_buffer;
 
        /* Settings for the port */
        int             baud;
@@ -2406,6 +2410,26 @@ static int keyspan_port_probe(struct usb_serial_port *port)
        if (!p_priv)
                return -ENOMEM;
 
+       for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) {
+               p_priv->in_buffer[i] = kzalloc(IN_BUFLEN, GFP_KERNEL);
+               if (!p_priv->in_buffer[i])
+                       goto err_in_buffer;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) {
+               p_priv->out_buffer[i] = kzalloc(OUT_BUFLEN, GFP_KERNEL);
+               if (!p_priv->out_buffer[i])
+                       goto err_out_buffer;
+       }
+
+       p_priv->inack_buffer = kzalloc(INACK_BUFLEN, GFP_KERNEL);
+       if (!p_priv->inack_buffer)
+               goto err_inack_buffer;
+
+       p_priv->outcont_buffer = kzalloc(OUTCONT_BUFLEN, GFP_KERNEL);
+       if (!p_priv->outcont_buffer)
+               goto err_outcont_buffer;
+
        p_priv->device_details = d_details;
 
        /* Setup values for the various callback routines */
@@ -2418,7 +2442,8 @@ static int keyspan_port_probe(struct usb_serial_port *port)
        for (i = 0; i <= d_details->indat_endp_flip; ++i, ++endp) {
                p_priv->in_urbs[i] = keyspan_setup_urb(serial, endp,
                                                USB_DIR_IN, port,
-                                               p_priv->in_buffer[i], 64,
+                                               p_priv->in_buffer[i],
+                                               IN_BUFLEN,
                                                cback->indat_callback);
        }
        /* outdat endpoints also have flip */
@@ -2426,25 +2451,41 @@ static int keyspan_port_probe(struct usb_serial_port *port)
        for (i = 0; i <= d_details->outdat_endp_flip; ++i, ++endp) {
                p_priv->out_urbs[i] = keyspan_setup_urb(serial, endp,
                                                USB_DIR_OUT, port,
-                                               p_priv->out_buffer[i], 64,
+                                               p_priv->out_buffer[i],
+                                               OUT_BUFLEN,
                                                cback->outdat_callback);
        }
        /* inack endpoint */
        p_priv->inack_urb = keyspan_setup_urb(serial,
                                        d_details->inack_endpoints[port_num],
                                        USB_DIR_IN, port,
-                                       p_priv->inack_buffer, 1,
+                                       p_priv->inack_buffer,
+                                       INACK_BUFLEN,
                                        cback->inack_callback);
        /* outcont endpoint */
        p_priv->outcont_urb = keyspan_setup_urb(serial,
                                        d_details->outcont_endpoints[port_num],
                                        USB_DIR_OUT, port,
-                                       p_priv->outcont_buffer, 64,
+                                       p_priv->outcont_buffer,
+                                       OUTCONT_BUFLEN,
                                         cback->outcont_callback);
 
        usb_set_serial_port_data(port, p_priv);
 
        return 0;
+
+err_outcont_buffer:
+       kfree(p_priv->inack_buffer);
+err_inack_buffer:
+       for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i)
+               kfree(p_priv->out_buffer[i]);
+err_out_buffer:
+       for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i)
+               kfree(p_priv->in_buffer[i]);
+err_in_buffer:
+       kfree(p_priv);
+
+       return -ENOMEM;
 }
 
 static int keyspan_port_remove(struct usb_serial_port *port)
@@ -2468,6 +2509,13 @@ static int keyspan_port_remove(struct usb_serial_port *port)
                usb_free_urb(p_priv->out_urbs[i]);
        }
 
+       kfree(p_priv->outcont_buffer);
+       kfree(p_priv->inack_buffer);
+       for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i)
+               kfree(p_priv->out_buffer[i]);
+       for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i)
+               kfree(p_priv->in_buffer[i]);
+
        kfree(p_priv);
 
        return 0;