]> 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:32:41 +0000 (15:32 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 26 Oct 2012 22:32:41 +0000 (15:32 -0700)
added patches:
usb-ch341-fix-port-data-memory-leak.patch
usb-digi_acceleport-fix-port-data-memory-leak.patch
usb-ipw-fix-interface-data-memory-leak-in-error-path.patch
usb-mct_u232-fix-broken-close.patch
usb-mct_u232-fix-port-data-memory-leak.patch
usb-option-fix-interface-data-memory-leak-in-error-path.patch
usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch

queue-3.6/series
queue-3.6/usb-ch341-fix-port-data-memory-leak.patch [new file with mode: 0644]
queue-3.6/usb-digi_acceleport-fix-port-data-memory-leak.patch [new file with mode: 0644]
queue-3.6/usb-ipw-fix-interface-data-memory-leak-in-error-path.patch [new file with mode: 0644]
queue-3.6/usb-mct_u232-fix-broken-close.patch [new file with mode: 0644]
queue-3.6/usb-mct_u232-fix-port-data-memory-leak.patch [new file with mode: 0644]
queue-3.6/usb-option-fix-interface-data-memory-leak-in-error-path.patch [new file with mode: 0644]
queue-3.6/usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch [new file with mode: 0644]

index 2dff7a94b56ae791e0419c1ec50e93c7526ca850..8b87fa3b1a648268ebf2c4a8cf39db17c2bfba80 100644 (file)
@@ -38,4 +38,11 @@ drivers-hv-cleanup-error-handling-in-vmbus_open.patch
 ehci-fix-lucid-nohandoff-pci-quirk-to-be-more-generic-with-bios-versions.patch
 ehci-add-yet-another-lucid-nohandoff-pci-quirk.patch
 usb-storage-add-unusual_devs-entry-for-casio-ex-n1-digital-camera.patch
+usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch
 usb-hub-send-clear_tt_buffer_complete-events-when-canceling-tt-clear-work.patch
+usb-ch341-fix-port-data-memory-leak.patch
+usb-digi_acceleport-fix-port-data-memory-leak.patch
+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
diff --git a/queue-3.6/usb-ch341-fix-port-data-memory-leak.patch b/queue-3.6/usb-ch341-fix-port-data-memory-leak.patch
new file mode 100644 (file)
index 0000000..aeebaa1
--- /dev/null
@@ -0,0 +1,90 @@
+From 456c5be56ed070a4d883c60b587bcc1c97a8cf3e Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:03 +0200
+Subject: USB: ch341: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit 456c5be56ed070a4d883c60b587bcc1c97a8cf3e upstream.
+
+Fix port-data memory leak by moving port data allocation to port_probe
+and actually implementing deallocation.
+
+Note that this driver has never even bothered to try to deallocate it's
+port data...
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ch341.c |   23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/serial/ch341.c
++++ b/drivers/usb/serial/ch341.c
+@@ -241,13 +241,11 @@ out:     kfree(buffer);
+       return r;
+ }
+-/* allocate private data */
+-static int ch341_attach(struct usb_serial *serial)
++static int ch341_port_probe(struct usb_serial_port *port)
+ {
+       struct ch341_private *priv;
+       int r;
+-      /* private data */
+       priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+@@ -257,17 +255,27 @@ static int ch341_attach(struct usb_seria
+       priv->baud_rate = DEFAULT_BAUD_RATE;
+       priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
+-      r = ch341_configure(serial->dev, priv);
++      r = ch341_configure(port->serial->dev, priv);
+       if (r < 0)
+               goto error;
+-      usb_set_serial_port_data(serial->port[0], priv);
++      usb_set_serial_port_data(port, priv);
+       return 0;
+ error:        kfree(priv);
+       return r;
+ }
++static int ch341_port_remove(struct usb_serial_port *port)
++{
++      struct ch341_private *priv;
++
++      priv = usb_get_serial_port_data(port);
++      kfree(priv);
++
++      return 0;
++}
++
+ static int ch341_carrier_raised(struct usb_serial_port *port)
+ {
+       struct ch341_private *priv = usb_get_serial_port_data(port);
+@@ -303,7 +311,7 @@ static void ch341_close(struct usb_seria
+ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
+ {
+       struct usb_serial *serial = port->serial;
+-      struct ch341_private *priv = usb_get_serial_port_data(serial->port[0]);
++      struct ch341_private *priv = usb_get_serial_port_data(port);
+       int r;
+       priv->baud_rate = DEFAULT_BAUD_RATE;
+@@ -606,7 +614,8 @@ static struct usb_serial_driver ch341_de
+       .tiocmget          = ch341_tiocmget,
+       .tiocmset          = ch341_tiocmset,
+       .read_int_callback = ch341_read_int_callback,
+-      .attach            = ch341_attach,
++      .port_probe        = ch341_port_probe,
++      .port_remove       = ch341_port_remove,
+       .reset_resume      = ch341_reset_resume,
+ };
diff --git a/queue-3.6/usb-digi_acceleport-fix-port-data-memory-leak.patch b/queue-3.6/usb-digi_acceleport-fix-port-data-memory-leak.patch
new file mode 100644 (file)
index 0000000..e401f8f
--- /dev/null
@@ -0,0 +1,196 @@
+From fb44ff854e148bc5c5982dad32da98b7a0989d2d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:04 +0200
+Subject: USB: digi_acceleport: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit fb44ff854e148bc5c5982dad32da98b7a0989d2d 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 oob port is never registered as a port device and should
+thus be handled in attach and release.
+
+Compile-only tested.
+
+Cc: Peter Berger <pberger@brimson.com>
+Cc: Al Borchers <alborchers@steinerpoint.com>
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/digi_acceleport.c |  117 ++++++++++++++++++++---------------
+ 1 file changed, 67 insertions(+), 50 deletions(-)
+
+--- a/drivers/usb/serial/digi_acceleport.c
++++ b/drivers/usb/serial/digi_acceleport.c
+@@ -244,6 +244,8 @@ static int digi_startup_device(struct us
+ static int digi_startup(struct usb_serial *serial);
+ static void digi_disconnect(struct usb_serial *serial);
+ static void digi_release(struct usb_serial *serial);
++static int digi_port_probe(struct usb_serial_port *port);
++static int digi_port_remove(struct usb_serial_port *port);
+ static void digi_read_bulk_callback(struct urb *urb);
+ static int digi_read_inb_callback(struct urb *urb);
+ static int digi_read_oob_callback(struct urb *urb);
+@@ -298,6 +300,8 @@ static struct usb_serial_driver digi_acc
+       .attach =                       digi_startup,
+       .disconnect =                   digi_disconnect,
+       .release =                      digi_release,
++      .port_probe =                   digi_port_probe,
++      .port_remove =                  digi_port_remove,
+ };
+ static struct usb_serial_driver digi_acceleport_4_device = {
+@@ -324,6 +328,8 @@ static struct usb_serial_driver digi_acc
+       .attach =                       digi_startup,
+       .disconnect =                   digi_disconnect,
+       .release =                      digi_release,
++      .port_probe =                   digi_port_probe,
++      .port_remove =                  digi_port_remove,
+ };
+ static struct usb_serial_driver * const serial_drivers[] = {
+@@ -1237,59 +1243,50 @@ static int digi_startup_device(struct us
+       return ret;
+ }
++static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
++{
++      struct digi_port *priv;
++
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      spin_lock_init(&priv->dp_port_lock);
++      priv->dp_port_num = port_num;
++      init_waitqueue_head(&priv->dp_modem_change_wait);
++      init_waitqueue_head(&priv->dp_transmit_idle_wait);
++      init_waitqueue_head(&priv->dp_flush_wait);
++      init_waitqueue_head(&priv->dp_close_wait);
++      INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
++      priv->dp_port = port;
++
++      init_waitqueue_head(&port->write_wait);
++
++      usb_set_serial_port_data(port, priv);
++
++      return 0;
++}
+ static int digi_startup(struct usb_serial *serial)
+ {
+-
+-      int i;
+-      struct digi_port *priv;
+       struct digi_serial *serial_priv;
++      int ret;
+-      /* allocate the private data structures for all ports */
+-      /* number of regular ports + 1 for the out-of-band port */
+-      for (i = 0; i < serial->type->num_ports + 1; i++) {
+-              /* allocate port private structure */
+-              priv = kmalloc(sizeof(struct digi_port), GFP_KERNEL);
+-              if (priv == NULL) {
+-                      while (--i >= 0)
+-                              kfree(usb_get_serial_port_data(serial->port[i]));
+-                      return 1;                       /* error */
+-              }
++      serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
++      if (!serial_priv)
++              return -ENOMEM;
+-              /* initialize port private structure */
+-              spin_lock_init(&priv->dp_port_lock);
+-              priv->dp_port_num = i;
+-              priv->dp_out_buf_len = 0;
+-              priv->dp_write_urb_in_use = 0;
+-              priv->dp_modem_signals = 0;
+-              init_waitqueue_head(&priv->dp_modem_change_wait);
+-              priv->dp_transmit_idle = 0;
+-              init_waitqueue_head(&priv->dp_transmit_idle_wait);
+-              priv->dp_throttled = 0;
+-              priv->dp_throttle_restart = 0;
+-              init_waitqueue_head(&priv->dp_flush_wait);
+-              init_waitqueue_head(&priv->dp_close_wait);
+-              INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
+-              priv->dp_port = serial->port[i];
+-              /* initialize write wait queue for this port */
+-              init_waitqueue_head(&serial->port[i]->write_wait);
+-
+-              usb_set_serial_port_data(serial->port[i], priv);
+-      }
+-
+-      /* allocate serial private structure */
+-      serial_priv = kmalloc(sizeof(struct digi_serial), GFP_KERNEL);
+-      if (serial_priv == NULL) {
+-              for (i = 0; i < serial->type->num_ports + 1; i++)
+-                      kfree(usb_get_serial_port_data(serial->port[i]));
+-              return 1;                       /* error */
+-      }
+-
+-      /* initialize serial private structure */
+       spin_lock_init(&serial_priv->ds_serial_lock);
+       serial_priv->ds_oob_port_num = serial->type->num_ports;
+       serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
+-      serial_priv->ds_device_started = 0;
++
++      ret = digi_port_init(serial_priv->ds_oob_port,
++                                              serial_priv->ds_oob_port_num);
++      if (ret) {
++              kfree(serial_priv);
++              return ret;
++      }
++
+       usb_set_serial_data(serial, serial_priv);
+       return 0;
+@@ -1310,15 +1307,35 @@ static void digi_disconnect(struct usb_s
+ static void digi_release(struct usb_serial *serial)
+ {
+-      int i;
++      struct digi_serial *serial_priv;
++      struct digi_port *priv;
++
++      serial_priv = usb_get_serial_data(serial);
++
++      priv = usb_get_serial_port_data(serial_priv->ds_oob_port);
++      kfree(priv);
+-      /* free the private data structures for all ports */
+-      /* number of regular ports + 1 for the out-of-band port */
+-      for (i = 0; i < serial->type->num_ports + 1; i++)
+-              kfree(usb_get_serial_port_data(serial->port[i]));
+-      kfree(usb_get_serial_data(serial));
++      kfree(serial_priv);
+ }
++static int digi_port_probe(struct usb_serial_port *port)
++{
++      unsigned port_num;
++
++      port_num = port->number - port->serial->minor;
++
++      return digi_port_init(port, port_num);
++}
++
++static int digi_port_remove(struct usb_serial_port *port)
++{
++      struct digi_port *priv;
++
++      priv = usb_get_serial_port_data(port);
++      kfree(priv);
++
++      return 0;
++}
+ static void digi_read_bulk_callback(struct urb *urb)
+ {
diff --git a/queue-3.6/usb-ipw-fix-interface-data-memory-leak-in-error-path.patch b/queue-3.6/usb-ipw-fix-interface-data-memory-leak-in-error-path.patch
new file mode 100644 (file)
index 0000000..6ad3be7
--- /dev/null
@@ -0,0 +1,40 @@
+From a997448c89905b80aa4022f734f03685e733d711 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 15:42:39 +0200
+Subject: USB: ipw: fix interface-data memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit a997448c89905b80aa4022f734f03685e733d711 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/ipw.c |    5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/serial/ipw.c
++++ b/drivers/usb/serial/ipw.c
+@@ -209,8 +209,7 @@ static int ipw_open(struct tty_struct *t
+       return 0;
+ }
+-/* fake probe - only to allocate data structures */
+-static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id)
++static int ipw_attach(struct usb_serial *serial)
+ {
+       struct usb_wwan_intf_private *data;
+@@ -310,7 +309,7 @@ static struct usb_serial_driver ipw_devi
+       .num_ports =            1,
+       .open =                 ipw_open,
+       .close =                ipw_close,
+-      .probe =                ipw_probe,
++      .attach =               ipw_attach,
+       .release =              ipw_release,
+       .port_probe =           usb_wwan_port_probe,
+       .port_remove =          usb_wwan_port_remove,
diff --git a/queue-3.6/usb-mct_u232-fix-broken-close.patch b/queue-3.6/usb-mct_u232-fix-broken-close.patch
new file mode 100644 (file)
index 0000000..e38f987
--- /dev/null
@@ -0,0 +1,50 @@
+From 5260e458f5eff269a43e4f1e9c47186c57b88ddb Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:14 +0200
+Subject: USB: mct_u232: fix broken close
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit 5260e458f5eff269a43e4f1e9c47186c57b88ddb upstream.
+
+Make sure generic close is called at close.
+
+The driver relies on the generic write implementation but did not call
+generic close.
+
+Note that the call to kill the read urb is not redundant, as mct_u232
+uses an interrupt urb from the second port as the read urb and that
+generic close therefore fails to kill it.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/mct_u232.c |   14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -524,12 +524,14 @@ static void mct_u232_dtr_rts(struct usb_
+ static void mct_u232_close(struct usb_serial_port *port)
+ {
+-      if (port->serial->dev) {
+-              /* shutdown our urbs */
+-              usb_kill_urb(port->write_urb);
+-              usb_kill_urb(port->read_urb);
+-              usb_kill_urb(port->interrupt_in_urb);
+-      }
++      /*
++       * Must kill the read urb as it is actually an interrupt urb, which
++       * generic close thus fails to kill.
++       */
++      usb_kill_urb(port->read_urb);
++      usb_kill_urb(port->interrupt_in_urb);
++
++      usb_serial_generic_close(port);
+ } /* mct_u232_close */
diff --git a/queue-3.6/usb-mct_u232-fix-port-data-memory-leak.patch b/queue-3.6/usb-mct_u232-fix-port-data-memory-leak.patch
new file mode 100644 (file)
index 0000000..d3acce0
--- /dev/null
@@ -0,0 +1,108 @@
+From a8f2ae7a3aa59079d7e7e1ddf5007f03532f458c Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:13 +0200
+Subject: USB: mct_u232: fix port-data memory leak
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit a8f2ae7a3aa59079d7e7e1ddf5007f03532f458c 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 write waitqueue was initialised but never used.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/mct_u232.c |   45 +++++++++++++++++++++++-------------------
+ 1 file changed, 25 insertions(+), 20 deletions(-)
+
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -51,7 +51,8 @@ static bool debug;
+  * Function prototypes
+  */
+ static int  mct_u232_startup(struct usb_serial *serial);
+-static void mct_u232_release(struct usb_serial *serial);
++static int  mct_u232_port_probe(struct usb_serial_port *port);
++static int  mct_u232_port_remove(struct usb_serial_port *remove);
+ static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port);
+ static void mct_u232_close(struct usb_serial_port *port);
+ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on);
+@@ -101,7 +102,8 @@ static struct usb_serial_driver mct_u232
+       .tiocmget =          mct_u232_tiocmget,
+       .tiocmset =          mct_u232_tiocmset,
+       .attach =            mct_u232_startup,
+-      .release =           mct_u232_release,
++      .port_probe =        mct_u232_port_probe,
++      .port_remove =       mct_u232_port_remove,
+       .ioctl =             mct_u232_ioctl,
+       .get_icount =        mct_u232_get_icount,
+ };
+@@ -392,18 +394,8 @@ static void mct_u232_msr_to_state(unsign
+ static int mct_u232_startup(struct usb_serial *serial)
+ {
+-      struct mct_u232_private *priv;
+       struct usb_serial_port *port, *rport;
+-      priv = kzalloc(sizeof(struct mct_u232_private), GFP_KERNEL);
+-      if (!priv)
+-              return -ENOMEM;
+-      spin_lock_init(&priv->lock);
+-      init_waitqueue_head(&priv->msr_wait);
+-      usb_set_serial_port_data(serial->port[0], priv);
+-
+-      init_waitqueue_head(&serial->port[0]->write_wait);
+-
+       /* Puh, that's dirty */
+       port = serial->port[0];
+       rport = serial->port[1];
+@@ -416,18 +408,31 @@ static int mct_u232_startup(struct usb_s
+       return 0;
+ } /* mct_u232_startup */
++static int mct_u232_port_probe(struct usb_serial_port *port)
++{
++      struct mct_u232_private *priv;
++
++      priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
+-static void mct_u232_release(struct usb_serial *serial)
++      spin_lock_init(&priv->lock);
++      init_waitqueue_head(&priv->msr_wait);
++
++      usb_set_serial_port_data(port, priv);
++
++      return 0;
++}
++
++static int mct_u232_port_remove(struct usb_serial_port *port)
+ {
+       struct mct_u232_private *priv;
+-      int i;
+-      for (i = 0; i < serial->num_ports; ++i) {
+-              /* My special items, the standard routines free my urbs */
+-              priv = usb_get_serial_port_data(serial->port[i]);
+-              kfree(priv);
+-      }
+-} /* mct_u232_release */
++      priv = usb_get_serial_port_data(port);
++      kfree(priv);
++
++      return 0;
++}
+ static int  mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
+ {
diff --git a/queue-3.6/usb-option-fix-interface-data-memory-leak-in-error-path.patch b/queue-3.6/usb-option-fix-interface-data-memory-leak-in-error-path.patch
new file mode 100644 (file)
index 0000000..9b94777
--- /dev/null
@@ -0,0 +1,80 @@
+From c2dd4a8eac7821fed2c2d19e4607d0986b53b0fe Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 15:42:40 +0200
+Subject: USB: option: fix interface-data memory leak in error path
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit c2dd4a8eac7821fed2c2d19e4607d0986b53b0fe upstream.
+
+Move interface data allocation to attach so that it is deallocated
+should usb-serial probe fail.
+
+Note that the usb device id is stored at probe so that it can be used
+in attach to determine send-setup blacklisting.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/option.c |   21 +++++++++++++++++++--
+ 1 file changed, 19 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -47,6 +47,7 @@
+ /* Function prototypes */
+ static int  option_probe(struct usb_serial *serial,
+                       const struct usb_device_id *id);
++static int option_attach(struct usb_serial *serial);
+ static void option_release(struct usb_serial *serial);
+ static int option_send_setup(struct usb_serial_port *port);
+ static void option_instat_callback(struct urb *urb);
+@@ -1244,6 +1245,7 @@ static struct usb_serial_driver option_1
+       .tiocmget          = usb_wwan_tiocmget,
+       .tiocmset          = usb_wwan_tiocmset,
+       .ioctl             = usb_wwan_ioctl,
++      .attach            = option_attach,
+       .release           = option_release,
+       .port_probe        = usb_wwan_port_probe,
+       .port_remove       = usb_wwan_port_remove,
+@@ -1293,8 +1295,6 @@ static bool is_blacklisted(const u8 ifnu
+ static int option_probe(struct usb_serial *serial,
+                       const struct usb_device_id *id)
+ {
+-      struct usb_wwan_intf_private *data;
+-      struct option_private *priv;
+       struct usb_interface_descriptor *iface_desc =
+                               &serial->interface->cur_altsetting->desc;
+       struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
+@@ -1332,6 +1332,19 @@ static int option_probe(struct usb_seria
+               iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
+               return -ENODEV;
++      /* Store device id so we can use it during attach. */
++      usb_set_serial_data(serial, (void *)id);
++
++      return 0;
++}
++
++static int option_attach(struct usb_serial *serial)
++{
++      struct usb_interface_descriptor *iface_desc;
++      const struct usb_device_id *id;
++      struct usb_wwan_intf_private *data;
++      struct option_private *priv;
++
+       data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+@@ -1342,6 +1355,10 @@ static int option_probe(struct usb_seria
+               return -ENOMEM;
+       }
++      /* Retrieve device id stored at probe. */
++      id = usb_get_serial_data(serial);
++      iface_desc = &serial->interface->cur_altsetting->desc;
++
+       priv->bInterfaceNumber = iface_desc->bInterfaceNumber;
+       data->private = priv;
diff --git a/queue-3.6/usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch b/queue-3.6/usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch
new file mode 100644 (file)
index 0000000..6b67f3e
--- /dev/null
@@ -0,0 +1,252 @@
+From b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc Mon Sep 17 00:00:00 2001
+From: Johan Hovold <jhovold@gmail.com>
+Date: Thu, 25 Oct 2012 10:29:16 +0200
+Subject: USB: usb-wwan: fix multiple memory leaks in error paths
+
+From: Johan Hovold <jhovold@gmail.com>
+
+commit b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc upstream.
+
+Fix port-data memory leak in usb-serial probe error path by moving port
+data allocation to port_probe.
+
+Since commit a1028f0abf ("usb: usb_wwan: replace release and disconnect
+with a port_remove hook") port data is deallocated in port_remove. This
+leaves a possibility for memory leaks if usb-serial probe fails after
+attach but before the port in question has been successfully registered.
+
+Note that this patch also fixes two additional memory leaks in the error
+path of attach should port initialisation fail for any port as the urbs
+were never freed and neither was the data of any of the successfully
+initialised ports.
+
+Compile-only tested.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/serial/ipw.c      |    2 
+ drivers/usb/serial/option.c   |    2 
+ drivers/usb/serial/qcserial.c |    2 
+ drivers/usb/serial/usb-wwan.h |    2 
+ drivers/usb/serial/usb_wwan.c |  136 +++++++++++++++++-------------------------
+ 5 files changed, 60 insertions(+), 84 deletions(-)
+
+--- a/drivers/usb/serial/ipw.c
++++ b/drivers/usb/serial/ipw.c
+@@ -311,8 +311,8 @@ static struct usb_serial_driver ipw_devi
+       .open =                 ipw_open,
+       .close =                ipw_close,
+       .probe =                ipw_probe,
+-      .attach =               usb_wwan_startup,
+       .release =              ipw_release,
++      .port_probe =           usb_wwan_port_probe,
+       .port_remove =          usb_wwan_port_remove,
+       .dtr_rts =              ipw_dtr_rts,
+       .write =                usb_wwan_write,
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -1244,8 +1244,8 @@ static struct usb_serial_driver option_1
+       .tiocmget          = usb_wwan_tiocmget,
+       .tiocmset          = usb_wwan_tiocmset,
+       .ioctl             = usb_wwan_ioctl,
+-      .attach            = usb_wwan_startup,
+       .release           = option_release,
++      .port_probe        = usb_wwan_port_probe,
+       .port_remove       = usb_wwan_port_remove,
+       .read_int_callback = option_instat_callback,
+ #ifdef CONFIG_PM
+--- a/drivers/usb/serial/qcserial.c
++++ b/drivers/usb/serial/qcserial.c
+@@ -287,8 +287,8 @@ 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              = usb_wwan_startup,
+       .release             = qc_release,
++      .port_probe          = usb_wwan_port_probe,
+       .port_remove         = usb_wwan_port_remove,
+ #ifdef CONFIG_PM
+       .suspend             = usb_wwan_suspend,
+--- a/drivers/usb/serial/usb-wwan.h
++++ b/drivers/usb/serial/usb-wwan.h
+@@ -8,7 +8,7 @@
+ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
+ extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
+ extern void usb_wwan_close(struct usb_serial_port *port);
+-extern int usb_wwan_startup(struct usb_serial *serial);
++extern int usb_wwan_port_probe(struct usb_serial_port *port);
+ extern int usb_wwan_port_remove(struct usb_serial_port *port);
+ extern int usb_wwan_write_room(struct tty_struct *tty);
+ extern void usb_wwan_set_termios(struct tty_struct *tty,
+--- a/drivers/usb/serial/usb_wwan.c
++++ b/drivers/usb/serial/usb_wwan.c
+@@ -447,10 +447,12 @@ void usb_wwan_close(struct usb_serial_po
+ EXPORT_SYMBOL(usb_wwan_close);
+ /* Helper functions used by usb_wwan_setup_urbs */
+-static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
++static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port,
++                                    int endpoint,
+                                     int dir, void *ctx, char *buf, int len,
+                                     void (*callback) (struct urb *))
+ {
++      struct usb_serial *serial = port->serial;
+       struct urb *urb;
+       if (endpoint == -1)
+@@ -470,100 +472,74 @@ static struct urb *usb_wwan_setup_urb(st
+       return urb;
+ }
+-/* Setup urbs */
+-static void usb_wwan_setup_urbs(struct usb_serial *serial)
++int usb_wwan_port_probe(struct usb_serial_port *port)
+ {
+-      int i, j;
+-      struct usb_serial_port *port;
+-      struct usb_wwan_port_private *portdata;
+-
+-      for (i = 0; i < serial->num_ports; i++) {
+-              port = serial->port[i];
+-              portdata = usb_get_serial_port_data(port);
+-
+-              /* Do indat endpoints first */
+-              for (j = 0; j < N_IN_URB; ++j) {
+-                      portdata->in_urbs[j] = usb_wwan_setup_urb(serial,
+-                                                                port->
+-                                                                bulk_in_endpointAddress,
+-                                                                USB_DIR_IN,
+-                                                                port,
+-                                                                portdata->
+-                                                                in_buffer[j],
+-                                                                IN_BUFLEN,
+-                                                                usb_wwan_indat_callback);
+-              }
+-
+-              /* outdat endpoints */
+-              for (j = 0; j < N_OUT_URB; ++j) {
+-                      portdata->out_urbs[j] = usb_wwan_setup_urb(serial,
+-                                                                 port->
+-                                                                 bulk_out_endpointAddress,
+-                                                                 USB_DIR_OUT,
+-                                                                 port,
+-                                                                 portdata->
+-                                                                 out_buffer
+-                                                                 [j],
+-                                                                 OUT_BUFLEN,
+-                                                                 usb_wwan_outdat_callback);
+-              }
+-      }
+-}
+-
+-int usb_wwan_startup(struct usb_serial *serial)
+-{
+-      int i, j, err;
+-      struct usb_serial_port *port;
+       struct usb_wwan_port_private *portdata;
++      struct urb *urb;
+       u8 *buffer;
++      int err;
++      int i;
+-      /* Now setup per port private data */
+-      for (i = 0; i < serial->num_ports; i++) {
+-              port = serial->port[i];
+-              portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+-              if (!portdata) {
+-                      dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
+-                          __func__, i);
+-                      return 1;
+-              }
+-              init_usb_anchor(&portdata->delayed);
+-
+-              for (j = 0; j < N_IN_URB; j++) {
+-                      buffer = (u8 *) __get_free_page(GFP_KERNEL);
+-                      if (!buffer)
+-                              goto bail_out_error;
+-                      portdata->in_buffer[j] = buffer;
+-              }
+-
+-              for (j = 0; j < N_OUT_URB; j++) {
+-                      buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
+-                      if (!buffer)
+-                              goto bail_out_error2;
+-                      portdata->out_buffer[j] = buffer;
+-              }
++      portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
++      if (!portdata)
++              return -ENOMEM;
++
++      init_usb_anchor(&portdata->delayed);
++
++      for (i = 0; i < N_IN_URB; i++) {
++              buffer = (u8 *)__get_free_page(GFP_KERNEL);
++              if (!buffer)
++                      goto bail_out_error;
++              portdata->in_buffer[i] = buffer;
++
++              urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress,
++                                              USB_DIR_IN, port,
++                                              buffer, IN_BUFLEN,
++                                              usb_wwan_indat_callback);
++              portdata->in_urbs[i] = urb;
++      }
++      for (i = 0; i < N_OUT_URB; i++) {
++              if (port->bulk_out_endpointAddress == -1)
++                      continue;
+-              usb_set_serial_port_data(port, portdata);
++              buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
++              if (!buffer)
++                      goto bail_out_error2;
++              portdata->out_buffer[i] = buffer;
++
++              urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress,
++                                              USB_DIR_OUT, port,
++                                              buffer, OUT_BUFLEN,
++                                              usb_wwan_outdat_callback);
++              portdata->out_urbs[i] = urb;
++      }
+-              if (!port->interrupt_in_urb)
+-                      continue;
++      usb_set_serial_port_data(port, portdata);
++
++      if (port->interrupt_in_urb) {
+               err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+               if (err)
+-                      dbg("%s: submit irq_in urb failed %d", __func__, err);
++                      dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n",
++                              __func__, err);
+       }
+-      usb_wwan_setup_urbs(serial);
++
+       return 0;
+ bail_out_error2:
+-      for (j = 0; j < N_OUT_URB; j++)
+-              kfree(portdata->out_buffer[j]);
++      for (i = 0; i < N_OUT_URB; i++) {
++              usb_free_urb(portdata->out_urbs[i]);
++              kfree(portdata->out_buffer[i]);
++      }
+ bail_out_error:
+-      for (j = 0; j < N_IN_URB; j++)
+-              if (portdata->in_buffer[j])
+-                      free_page((unsigned long)portdata->in_buffer[j]);
++      for (i = 0; i < N_IN_URB; i++) {
++              usb_free_urb(portdata->in_urbs[i]);
++              free_page((unsigned long)portdata->in_buffer[i]);
++      }
+       kfree(portdata);
+-      return 1;
++
++      return -ENOMEM;
+ }
+-EXPORT_SYMBOL(usb_wwan_startup);
++EXPORT_SYMBOL_GPL(usb_wwan_port_probe);
+ int usb_wwan_port_remove(struct usb_serial_port *port)
+ {