]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 26 Oct 2012 22:47:38 +0000 (15:47 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 26 Oct 2012 22:47:38 +0000 (15:47 -0700)
added patches:
usb-keyspan-fix-null-pointer-dereferences-and-memory-leaks.patch
usb-omninet-fix-port-data-memory-leak.patch
usb-opticon-fix-dma-from-stack.patch
usb-opticon-fix-memory-leak-in-error-path.patch
usb-qcserial-fix-interface-data-memory-leak-in-error-path.patch
usb-whiteheat-fix-memory-leak-in-error-path.patch
usb-whiteheat-fix-port-data-memory-leak.patch

queue-3.6/series
queue-3.6/usb-keyspan-fix-null-pointer-dereferences-and-memory-leaks.patch [new file with mode: 0644]
queue-3.6/usb-omninet-fix-port-data-memory-leak.patch [new file with mode: 0644]
queue-3.6/usb-opticon-fix-dma-from-stack.patch [new file with mode: 0644]
queue-3.6/usb-opticon-fix-memory-leak-in-error-path.patch [new file with mode: 0644]
queue-3.6/usb-qcserial-fix-interface-data-memory-leak-in-error-path.patch [new file with mode: 0644]
queue-3.6/usb-whiteheat-fix-memory-leak-in-error-path.patch [new file with mode: 0644]
queue-3.6/usb-whiteheat-fix-port-data-memory-leak.patch [new file with mode: 0644]

index 8b87fa3b1a648268ebf2c4a8cf39db17c2bfba80..b2f3018ab35807fd4b4d97e833b7ad0d6d338d1d 100644 (file)
@@ -46,3 +46,10 @@ usb-ipw-fix-interface-data-memory-leak-in-error-path.patch
 usb-mct_u232-fix-port-data-memory-leak.patch
 usb-mct_u232-fix-broken-close.patch
 usb-option-fix-interface-data-memory-leak-in-error-path.patch
+usb-keyspan-fix-null-pointer-dereferences-and-memory-leaks.patch
+usb-omninet-fix-port-data-memory-leak.patch
+usb-qcserial-fix-interface-data-memory-leak-in-error-path.patch
+usb-whiteheat-fix-memory-leak-in-error-path.patch
+usb-whiteheat-fix-port-data-memory-leak.patch
+usb-opticon-fix-dma-from-stack.patch
+usb-opticon-fix-memory-leak-in-error-path.patch
diff --git a/queue-3.6/usb-keyspan-fix-null-pointer-dereferences-and-memory-leaks.patch b/queue-3.6/usb-keyspan-fix-null-pointer-dereferences-and-memory-leaks.patch
new file mode 100644 (file)
index 0000000..54f4672
--- /dev/null
@@ -0,0 +1,312 @@
+From f79b2d0fe81eecb412dc48e87a119afc690da8e9 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:15 +0200
+Subject: USB: keyspan: fix NULL-pointer dereferences and memory leaks
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit f79b2d0fe81eecb412dc48e87a119afc690da8e9 upstream.
+
+Fix NULL-pointer dereference at release by moving port data allocation
+and deallocation to port_probe and port_remove.
+
+Fix NULL-pointer dereference at disconnect by stopping port urbs at
+port_remove.
+
+Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
+driver is bound) the port private data is no longer accessible at
+disconnect or release.
+
+Note that this patch also fixes port and interface-data memory leaks in
+the error path of attach should port initialisation fail for any port.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/keyspan.c |  183 ++++++++++++++++++++-----------------------
+ drivers/usb/serial/keyspan.h |    8 +
+ 2 files changed, 96 insertions(+), 95 deletions(-)
+
+--- a/drivers/usb/serial/keyspan.c
++++ b/drivers/usb/serial/keyspan.c
+@@ -1392,13 +1392,9 @@ static struct callbacks {
+          data in device_details */
+ static void keyspan_setup_urbs(struct usb_serial *serial)
+ {
+-      int                             i, j;
+       struct keyspan_serial_private   *s_priv;
+       const struct keyspan_device_details     *d_details;
+-      struct usb_serial_port          *port;
+-      struct keyspan_port_private     *p_priv;
+       struct callbacks                *cback;
+-      int                             endp;
+       s_priv = usb_get_serial_data(serial);
+       d_details = s_priv->device_details;
+@@ -1422,45 +1418,6 @@ static void keyspan_setup_urbs(struct us
+               (serial, d_details->glocont_endpoint, USB_DIR_OUT,
+                serial, s_priv->glocont_buf, GLOCONT_BUFLEN,
+                cback->glocont_callback);
+-
+-      /* Setup endpoints for each port specific thing */
+-      for (i = 0; i < d_details->num_ports; i++) {
+-              port = serial->port[i];
+-              p_priv = usb_get_serial_port_data(port);
+-
+-              /* Do indat endpoints first, once for each flip */
+-              endp = d_details->indat_endpoints[i];
+-              for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) {
+-                      p_priv->in_urbs[j] = keyspan_setup_urb
+-                              (serial, endp, USB_DIR_IN, port,
+-                               p_priv->in_buffer[j], 64,
+-                               cback->indat_callback);
+-              }
+-              for (; j < 2; ++j)
+-                      p_priv->in_urbs[j] = NULL;
+-
+-              /* outdat endpoints also have flip */
+-              endp = d_details->outdat_endpoints[i];
+-              for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) {
+-                      p_priv->out_urbs[j] = keyspan_setup_urb
+-                              (serial, endp, USB_DIR_OUT, port,
+-                               p_priv->out_buffer[j], 64,
+-                               cback->outdat_callback);
+-              }
+-              for (; j < 2; ++j)
+-                      p_priv->out_urbs[j] = NULL;
+-
+-              /* inack endpoint */
+-              p_priv->inack_urb = keyspan_setup_urb
+-                      (serial, d_details->inack_endpoints[i], USB_DIR_IN,
+-                       port, p_priv->inack_buffer, 1, cback->inack_callback);
+-
+-              /* outcont endpoint */
+-              p_priv->outcont_urb = keyspan_setup_urb
+-                      (serial, d_details->outcont_endpoints[i], USB_DIR_OUT,
+-                       port, p_priv->outcont_buffer, 64,
+-                       cback->outcont_callback);
+-      }
+ }
+ /* usa19 function doesn't require prescaler */
+@@ -2422,9 +2379,7 @@ static void keyspan_send_setup(struct us
+ static int keyspan_startup(struct usb_serial *serial)
+ {
+       int                             i, err;
+-      struct usb_serial_port          *port;
+       struct keyspan_serial_private   *s_priv;
+-      struct keyspan_port_private     *p_priv;
+       const struct keyspan_device_details     *d_details;
+       for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i)
+@@ -2448,19 +2403,6 @@ static int keyspan_startup(struct usb_se
+       s_priv->device_details = d_details;
+       usb_set_serial_data(serial, s_priv);
+-      /* Now setup per port private data */
+-      for (i = 0; i < serial->num_ports; i++) {
+-              port = serial->port[i];
+-              p_priv = kzalloc(sizeof(struct keyspan_port_private),
+-                                                              GFP_KERNEL);
+-              if (!p_priv) {
+-                      dbg("%s - kmalloc for keyspan_port_private (%d) failed!.", __func__, i);
+-                      return 1;
+-              }
+-              p_priv->device_details = d_details;
+-              usb_set_serial_port_data(port, p_priv);
+-      }
+-
+       keyspan_setup_urbs(serial);
+       if (s_priv->instat_urb != NULL) {
+@@ -2481,61 +2423,112 @@ static int keyspan_startup(struct usb_se
+ static void keyspan_disconnect(struct usb_serial *serial)
+ {
+-      int                             i, j;
+-      struct usb_serial_port          *port;
+-      struct keyspan_serial_private   *s_priv;
+-      struct keyspan_port_private     *p_priv;
++      struct keyspan_serial_private *s_priv;
+       s_priv = usb_get_serial_data(serial);
+-      /* Stop reading/writing urbs */
+       stop_urb(s_priv->instat_urb);
+       stop_urb(s_priv->glocont_urb);
+       stop_urb(s_priv->indat_urb);
+-      for (i = 0; i < serial->num_ports; ++i) {
+-              port = serial->port[i];
+-              p_priv = usb_get_serial_port_data(port);
+-              stop_urb(p_priv->inack_urb);
+-              stop_urb(p_priv->outcont_urb);
+-              for (j = 0; j < 2; j++) {
+-                      stop_urb(p_priv->in_urbs[j]);
+-                      stop_urb(p_priv->out_urbs[j]);
+-              }
+-      }
++}
++
++static void keyspan_release(struct usb_serial *serial)
++{
++      struct keyspan_serial_private *s_priv;
++
++      s_priv = usb_get_serial_data(serial);
+-      /* Now free them */
+       usb_free_urb(s_priv->instat_urb);
+       usb_free_urb(s_priv->indat_urb);
+       usb_free_urb(s_priv->glocont_urb);
+-      for (i = 0; i < serial->num_ports; ++i) {
+-              port = serial->port[i];
+-              p_priv = usb_get_serial_port_data(port);
+-              usb_free_urb(p_priv->inack_urb);
+-              usb_free_urb(p_priv->outcont_urb);
+-              for (j = 0; j < 2; j++) {
+-                      usb_free_urb(p_priv->in_urbs[j]);
+-                      usb_free_urb(p_priv->out_urbs[j]);
+-              }
+-      }
++
++      kfree(s_priv);
+ }
+-static void keyspan_release(struct usb_serial *serial)
++static int keyspan_port_probe(struct usb_serial_port *port)
+ {
+-      int                             i;
+-      struct usb_serial_port          *port;
+-      struct keyspan_serial_private   *s_priv;
++      struct usb_serial *serial = port->serial;
++      struct keyspan_port_private *s_priv;
++      struct keyspan_port_private *p_priv;
++      const struct keyspan_device_details *d_details;
++      struct callbacks *cback;
++      int endp;
++      int port_num;
++      int i;
+       s_priv = usb_get_serial_data(serial);
++      d_details = s_priv->device_details;
+-      /*  dbg("Freeing serial->private."); */
+-      kfree(s_priv);
++      p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL);
++      if (!p_priv)
++              return -ENOMEM;
++
++      s_priv = usb_get_serial_data(port->serial);
++      p_priv->device_details = d_details;
++
++      /* Setup values for the various callback routines */
++      cback = &keyspan_callbacks[d_details->msg_format];
++
++      port_num = port->number - port->serial->minor;
++
++      /* Do indat endpoints first, once for each flip */
++      endp = d_details->indat_endpoints[port_num];
++      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,
++                                              cback->indat_callback);
++      }
++      /* outdat endpoints also have flip */
++      endp = d_details->outdat_endpoints[port_num];
++      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,
++                                              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,
++                                      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,
++                                       cback->outcont_callback);
++
++      usb_set_serial_port_data(port, p_priv);
+-      /*  dbg("Freeing port->private."); */
+-      /* Now free per port private data */
+-      for (i = 0; i < serial->num_ports; i++) {
+-              port = serial->port[i];
+-              kfree(usb_get_serial_port_data(port));
++      return 0;
++}
++
++static int keyspan_port_remove(struct usb_serial_port *port)
++{
++      struct keyspan_port_private *p_priv;
++      int i;
++
++      p_priv = usb_get_serial_port_data(port);
++
++      stop_urb(p_priv->inack_urb);
++      stop_urb(p_priv->outcont_urb);
++      for (i = 0; i < 2; i++) {
++              stop_urb(p_priv->in_urbs[i]);
++              stop_urb(p_priv->out_urbs[i]);
++      }
++
++      usb_free_urb(p_priv->inack_urb);
++      usb_free_urb(p_priv->outcont_urb);
++      for (i = 0; i < 2; i++) {
++              usb_free_urb(p_priv->in_urbs[i]);
++              usb_free_urb(p_priv->out_urbs[i]);
+       }
++
++      kfree(p_priv);
++
++      return 0;
+ }
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+--- a/drivers/usb/serial/keyspan.h
++++ b/drivers/usb/serial/keyspan.h
+@@ -42,6 +42,8 @@ static void keyspan_dtr_rts          (struct usb
+ static int  keyspan_startup           (struct usb_serial *serial);
+ static void keyspan_disconnect                (struct usb_serial *serial);
+ static void keyspan_release           (struct usb_serial *serial);
++static int keyspan_port_probe(struct usb_serial_port *port);
++static int keyspan_port_remove(struct usb_serial_port *port);
+ static int  keyspan_write_room                (struct tty_struct *tty);
+ static int  keyspan_write             (struct tty_struct *tty,
+@@ -562,6 +564,8 @@ static struct usb_serial_driver keyspan_
+       .attach                 = keyspan_startup,
+       .disconnect             = keyspan_disconnect,
+       .release                = keyspan_release,
++      .port_probe             = keyspan_port_probe,
++      .port_remove            = keyspan_port_remove,
+ };
+ static struct usb_serial_driver keyspan_2port_device = {
+@@ -584,6 +588,8 @@ static struct usb_serial_driver keyspan_
+       .attach                 = keyspan_startup,
+       .disconnect             = keyspan_disconnect,
+       .release                = keyspan_release,
++      .port_probe             = keyspan_port_probe,
++      .port_remove            = keyspan_port_remove,
+ };
+ static struct usb_serial_driver keyspan_4port_device = {
+@@ -606,6 +612,8 @@ static struct usb_serial_driver keyspan_
+       .attach                 = keyspan_startup,
+       .disconnect             = keyspan_disconnect,
+       .release                = keyspan_release,
++      .port_probe             = keyspan_port_probe,
++      .port_remove            = keyspan_port_remove,
+ };
+ static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/queue-3.6/usb-omninet-fix-port-data-memory-leak.patch b/queue-3.6/usb-omninet-fix-port-data-memory-leak.patch
new file mode 100644 (file)
index 0000000..ae74943
--- /dev/null
@@ -0,0 +1,104 @@
+From feffa7ca6008ab859dd7ab7448a5a899bf0aa98f Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:06 +0200
+Subject: USB: omninet: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit feffa7ca6008ab859dd7ab7448a5a899bf0aa98f upstream.
+
+Fix port-data memory leak by replacing attach and release with
+port_probe and port_remove.
+
+Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
+driver is bound) the port private data is no longer freed at release as
+it is no longer accessible.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/omninet.c |   36 ++++++++++++++++++------------------
+ 1 file changed, 18 insertions(+), 18 deletions(-)
+
+--- a/drivers/usb/serial/omninet.c
++++ b/drivers/usb/serial/omninet.c
+@@ -46,8 +46,8 @@ static int  omninet_write(struct tty_str
+                               const unsigned char *buf, int count);
+ static int  omninet_write_room(struct tty_struct *tty);
+ static void omninet_disconnect(struct usb_serial *serial);
+-static void omninet_release(struct usb_serial *serial);
+-static int omninet_attach(struct usb_serial *serial);
++static int omninet_port_probe(struct usb_serial_port *port);
++static int omninet_port_remove(struct usb_serial_port *port);
+ static const struct usb_device_id id_table[] = {
+       { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) },
+@@ -64,7 +64,8 @@ static struct usb_serial_driver zyxel_om
+       .description =          "ZyXEL - omni.net lcd plus usb",
+       .id_table =             id_table,
+       .num_ports =            1,
+-      .attach =               omninet_attach,
++      .port_probe =           omninet_port_probe,
++      .port_remove =          omninet_port_remove,
+       .open =                 omninet_open,
+       .close =                omninet_close,
+       .write =                omninet_write,
+@@ -72,7 +73,6 @@ static struct usb_serial_driver zyxel_om
+       .read_bulk_callback =   omninet_read_bulk_callback,
+       .write_bulk_callback =  omninet_write_bulk_callback,
+       .disconnect =           omninet_disconnect,
+-      .release =              omninet_release,
+ };
+ static struct usb_serial_driver * const serial_drivers[] = {
+@@ -114,18 +114,26 @@ struct omninet_data {
+       __u8    od_outseq;      /* Sequence number for bulk_out URBs */
+ };
+-static int omninet_attach(struct usb_serial *serial)
++static int omninet_port_probe(struct usb_serial_port *port)
+ {
+       struct omninet_data *od;
+-      struct usb_serial_port *port = serial->port[0];
+       od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);
+-      if (!od) {
+-              dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n",
+-                      __func__, sizeof(struct omninet_data));
++      if (!od)
+               return -ENOMEM;
+-      }
++
+       usb_set_serial_port_data(port, od);
++
++      return 0;
++}
++
++static int omninet_port_remove(struct usb_serial_port *port)
++{
++      struct omninet_data *od;
++
++      od = usb_get_serial_port_data(port);
++      kfree(od);
++
+       return 0;
+ }
+@@ -291,14 +299,6 @@ static void omninet_disconnect(struct us
+       usb_kill_urb(wport->write_urb);
+ }
+-
+-static void omninet_release(struct usb_serial *serial)
+-{
+-      struct usb_serial_port *port = serial->port[0];
+-
+-      kfree(usb_get_serial_port_data(port));
+-}
+-
+ module_usb_serial_driver(serial_drivers, id_table);
+ MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/queue-3.6/usb-opticon-fix-dma-from-stack.patch b/queue-3.6/usb-opticon-fix-dma-from-stack.patch
new file mode 100644 (file)
index 0000000..344619f
--- /dev/null
@@ -0,0 +1,44 @@
+From ea0dbebffe118724cd4df7d9b071ea8ee48d48f0 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:11 +0200
+Subject: USB: opticon: fix DMA from stack
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit ea0dbebffe118724cd4df7d9b071ea8ee48d48f0 upstream.
+
+Make sure to allocate the control-message buffer dynamically as some
+platforms cannot do DMA from stack.
+
+Note that only the first byte of the old buffer was used.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/opticon.c |    7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/opticon.c
++++ b/drivers/usb/serial/opticon.c
+@@ -158,7 +158,11 @@ static int send_control_msg(struct usb_s
+ {
+       struct usb_serial *serial = port->serial;
+       int retval;
+-      u8 buffer[2];
++      u8 *buffer;
++
++      buffer = kzalloc(1, GFP_KERNEL);
++      if (!buffer)
++              return -ENOMEM;
+       buffer[0] = val;
+       /* Send the message to the vendor control endpoint
+@@ -167,6 +171,7 @@ static int send_control_msg(struct usb_s
+                               requesttype,
+                               USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
+                               0, 0, buffer, 1, 0);
++      kfree(buffer);
+       return retval;
+ }
diff --git a/queue-3.6/usb-opticon-fix-memory-leak-in-error-path.patch b/queue-3.6/usb-opticon-fix-memory-leak-in-error-path.patch
new file mode 100644 (file)
index 0000000..dab5795
--- /dev/null
@@ -0,0 +1,38 @@
+From acbf0e5263de563e25f7c104868e4490b9e72b13 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:12 +0200
+Subject: USB: opticon: fix memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit acbf0e5263de563e25f7c104868e4490b9e72b13 upstream.
+
+Fix memory leak in write error path.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/opticon.c |    4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/opticon.c
++++ b/drivers/usb/serial/opticon.c
+@@ -289,7 +289,7 @@ static int opticon_write(struct tty_stru
+       if (!dr) {
+               dev_err(&port->dev, "out of memory\n");
+               count = -ENOMEM;
+-              goto error;
++              goto error_no_dr;
+       }
+       dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
+@@ -319,6 +319,8 @@ static int opticon_write(struct tty_stru
+       return count;
+ error:
++      kfree(dr);
++error_no_dr:
+       usb_free_urb(urb);
+ error_no_urb:
+       kfree(buffer);
diff --git a/queue-3.6/usb-qcserial-fix-interface-data-memory-leak-in-error-path.patch b/queue-3.6/usb-qcserial-fix-interface-data-memory-leak-in-error-path.patch
new file mode 100644 (file)
index 0000000..b233b47
--- /dev/null
@@ -0,0 +1,87 @@
+From 961be09e1ead58509ed4bed0d5819a15d8613d8d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 15:42:41 +0200
+Subject: USB: qcserial: fix interface-data memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit 961be09e1ead58509ed4bed0d5819a15d8613d8d upstream.
+
+Move interface data allocation to attach so that it is deallocated
+should usb-serial probe fail.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/qcserial.c |   31 ++++++++++++++++---------------
+ 1 file changed, 16 insertions(+), 15 deletions(-)
+
+--- a/drivers/usb/serial/qcserial.c
++++ b/drivers/usb/serial/qcserial.c
+@@ -140,7 +140,6 @@ MODULE_DEVICE_TABLE(usb, id_table);
+ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ {
+-      struct usb_wwan_intf_private *data;
+       struct usb_host_interface *intf = serial->interface->cur_altsetting;
+       struct device *dev = &serial->dev->dev;
+       int retval = -ENODEV;
+@@ -156,13 +155,6 @@ static int qcprobe(struct usb_serial *se
+       ifnum = intf->desc.bInterfaceNumber;
+       dev_dbg(dev, "This Interface = %d\n", ifnum);
+-      data = kzalloc(sizeof(struct usb_wwan_intf_private),
+-                                       GFP_KERNEL);
+-      if (!data)
+-              return -ENOMEM;
+-
+-      spin_lock_init(&data->susp_lock);
+-
+       if (nintf == 1) {
+               /* QDL mode */
+               /* Gobi 2000 has a single altsetting, older ones have two */
+@@ -255,20 +247,28 @@ done:
+               }
+       }
+-      /* Set serial->private if not returning error */
+-      if (retval == 0)
+-              usb_set_serial_data(serial, data);
+-      else
+-              kfree(data);
+-
+       return retval;
+ }
++static int qc_attach(struct usb_serial *serial)
++{
++      struct usb_wwan_intf_private *data;
++
++      data = kzalloc(sizeof(*data), GFP_KERNEL);
++      if (!data)
++              return -ENOMEM;
++
++      spin_lock_init(&data->susp_lock);
++
++      usb_set_serial_data(serial, data);
++
++      return 0;
++}
++
+ static void qc_release(struct usb_serial *serial)
+ {
+       struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
+-      /* Free the private data allocated in qcprobe */
+       usb_set_serial_data(serial, NULL);
+       kfree(priv);
+ }
+@@ -287,6 +287,7 @@ static struct usb_serial_driver qcdevice
+       .write               = usb_wwan_write,
+       .write_room          = usb_wwan_write_room,
+       .chars_in_buffer     = usb_wwan_chars_in_buffer,
++      .attach              = qc_attach,
+       .release             = qc_release,
+       .port_probe          = usb_wwan_port_probe,
+       .port_remove         = usb_wwan_port_remove,
diff --git a/queue-3.6/usb-whiteheat-fix-memory-leak-in-error-path.patch b/queue-3.6/usb-whiteheat-fix-memory-leak-in-error-path.patch
new file mode 100644 (file)
index 0000000..d014d4e
--- /dev/null
@@ -0,0 +1,29 @@
+From c129197c99550d356cf5f69b046994dd53cd1b9d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:01 +0200
+Subject: USB: whiteheat: fix memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit c129197c99550d356cf5f69b046994dd53cd1b9d upstream.
+
+Make sure command buffer is deallocated in case of errors during attach.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Cc: <support@connecttech.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/whiteheat.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/whiteheat.c
++++ b/drivers/usb/serial/whiteheat.c
+@@ -405,6 +405,7 @@ no_firmware:
+               "%s: please contact support@connecttech.com\n",
+               serial->type->description);
+       kfree(result);
++      kfree(command);
+       return -ENODEV;
+ no_command_private:
diff --git a/queue-3.6/usb-whiteheat-fix-port-data-memory-leak.patch b/queue-3.6/usb-whiteheat-fix-port-data-memory-leak.patch
new file mode 100644 (file)
index 0000000..4f1e9d5
--- /dev/null
@@ -0,0 +1,146 @@
+From c467206ed6bcce26c83d0435612cc4fee2527305 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:02 +0200
+Subject: USB: whiteheat: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit c467206ed6bcce26c83d0435612cc4fee2527305 upstream.
+
+Fix port-data memory leak by moving port data allocation and
+deallocation to port_probe and port_remove.
+
+Since commit 0998d0631001288 (device-core: Ensure drvdata = NULL when no
+driver is bound) the port private data is no longer freed at release as
+it is no longer accessible.
+
+Note that the fifth port (command port) is never registered as a
+port device and thus should be handled in attach and release.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Cc: <support@connecttech.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/whiteheat.c |   59 ++++++++++++++++++-----------------------
+ 1 file changed, 26 insertions(+), 33 deletions(-)
+
+--- a/drivers/usb/serial/whiteheat.c
++++ b/drivers/usb/serial/whiteheat.c
+@@ -86,6 +86,8 @@ static int  whiteheat_firmware_attach(st
+ /* function prototypes for the Connect Tech WhiteHEAT serial converter */
+ static int  whiteheat_attach(struct usb_serial *serial);
+ static void whiteheat_release(struct usb_serial *serial);
++static int  whiteheat_port_probe(struct usb_serial_port *port);
++static int  whiteheat_port_remove(struct usb_serial_port *port);
+ static int  whiteheat_open(struct tty_struct *tty,
+                       struct usb_serial_port *port);
+ static void whiteheat_close(struct usb_serial_port *port);
+@@ -120,6 +122,8 @@ static struct usb_serial_driver whitehea
+       .num_ports =            4,
+       .attach =               whiteheat_attach,
+       .release =              whiteheat_release,
++      .port_probe =           whiteheat_port_probe,
++      .port_remove =          whiteheat_port_remove,
+       .open =                 whiteheat_open,
+       .close =                whiteheat_close,
+       .ioctl =                whiteheat_ioctl,
+@@ -290,15 +294,12 @@ static int whiteheat_attach(struct usb_s
+ {
+       struct usb_serial_port *command_port;
+       struct whiteheat_command_private *command_info;
+-      struct usb_serial_port *port;
+-      struct whiteheat_private *info;
+       struct whiteheat_hw_info *hw_info;
+       int pipe;
+       int ret;
+       int alen;
+       __u8 *command;
+       __u8 *result;
+-      int i;
+       command_port = serial->port[COMMAND_PORT];
+@@ -357,22 +358,6 @@ static int whiteheat_attach(struct usb_s
+                serial->type->description,
+                hw_info->sw_major_rev, hw_info->sw_minor_rev);
+-      for (i = 0; i < serial->num_ports; i++) {
+-              port = serial->port[i];
+-
+-              info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);
+-              if (info == NULL) {
+-                      dev_err(&port->dev,
+-                              "%s: Out of memory for port structures\n",
+-                              serial->type->description);
+-                      goto no_private;
+-              }
+-
+-              info->mcr = 0;
+-
+-              usb_set_serial_port_data(port, info);
+-      }
+-
+       command_info = kmalloc(sizeof(struct whiteheat_command_private),
+                                                               GFP_KERNEL);
+       if (command_info == NULL) {
+@@ -409,13 +394,6 @@ no_firmware:
+       return -ENODEV;
+ no_command_private:
+-      for (i = serial->num_ports - 1; i >= 0; i--) {
+-              port = serial->port[i];
+-              info = usb_get_serial_port_data(port);
+-              kfree(info);
+-no_private:
+-              ;
+-      }
+       kfree(result);
+ no_result_buffer:
+       kfree(command);
+@@ -423,21 +401,36 @@ no_command_buffer:
+       return -ENOMEM;
+ }
+-
+ static void whiteheat_release(struct usb_serial *serial)
+ {
+       struct usb_serial_port *command_port;
+-      struct whiteheat_private *info;
+-      int i;
+       /* free up our private data for our command port */
+       command_port = serial->port[COMMAND_PORT];
+       kfree(usb_get_serial_port_data(command_port));
++}
+-      for (i = 0; i < serial->num_ports; i++) {
+-              info = usb_get_serial_port_data(serial->port[i]);
+-              kfree(info);
+-      }
++static int whiteheat_port_probe(struct usb_serial_port *port)
++{
++      struct whiteheat_private *info;
++
++      info = kzalloc(sizeof(*info), GFP_KERNEL);
++      if (!info)
++              return -ENOMEM;
++
++      usb_set_serial_port_data(port, info);
++
++      return 0;
++}
++
++static int whiteheat_port_remove(struct usb_serial_port *port)
++{
++      struct whiteheat_private *info;
++
++      info = usb_get_serial_port_data(port);
++      kfree(info);
++
++      return 0;
+ }
+ static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port)