From: Greg Kroah-Hartman Date: Fri, 26 Oct 2012 22:32:41 +0000 (-0700) Subject: 3.6-stable patches X-Git-Tag: v3.0.49~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=602836db6563a1328464c353abfa763c5ebf2fc2;p=thirdparty%2Fkernel%2Fstable-queue.git 3.6-stable patches 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 --- diff --git a/queue-3.6/series b/queue-3.6/series index 2dff7a94b56..8b87fa3b1a6 100644 --- a/queue-3.6/series +++ b/queue-3.6/series @@ -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 index 00000000000..aeebaa13b67 --- /dev/null +++ b/queue-3.6/usb-ch341-fix-port-data-memory-leak.patch @@ -0,0 +1,90 @@ +From 456c5be56ed070a4d883c60b587bcc1c97a8cf3e Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:03 +0200 +Subject: USB: ch341: fix port-data memory leak + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..e401f8f5124 --- /dev/null +++ b/queue-3.6/usb-digi_acceleport-fix-port-data-memory-leak.patch @@ -0,0 +1,196 @@ +From fb44ff854e148bc5c5982dad32da98b7a0989d2d Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:04 +0200 +Subject: USB: digi_acceleport: fix port-data memory leak + +From: Johan Hovold + +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 +Cc: Al Borchers +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..6ad3be7b7e2 --- /dev/null +++ b/queue-3.6/usb-ipw-fix-interface-data-memory-leak-in-error-path.patch @@ -0,0 +1,40 @@ +From a997448c89905b80aa4022f734f03685e733d711 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 15:42:39 +0200 +Subject: USB: ipw: fix interface-data memory leak in error path + +From: Johan Hovold + +commit a997448c89905b80aa4022f734f03685e733d711 upstream. + +Move interface data allocation to attach so that it is deallocated +should usb-serial probe fail. + +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..e38f9879ea4 --- /dev/null +++ b/queue-3.6/usb-mct_u232-fix-broken-close.patch @@ -0,0 +1,50 @@ +From 5260e458f5eff269a43e4f1e9c47186c57b88ddb Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:14 +0200 +Subject: USB: mct_u232: fix broken close + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..d3acce0f758 --- /dev/null +++ b/queue-3.6/usb-mct_u232-fix-port-data-memory-leak.patch @@ -0,0 +1,108 @@ +From a8f2ae7a3aa59079d7e7e1ddf5007f03532f458c Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:13 +0200 +Subject: USB: mct_u232: fix port-data memory leak + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..9b947776ca9 --- /dev/null +++ b/queue-3.6/usb-option-fix-interface-data-memory-leak-in-error-path.patch @@ -0,0 +1,80 @@ +From c2dd4a8eac7821fed2c2d19e4607d0986b53b0fe Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 15:42:40 +0200 +Subject: USB: option: fix interface-data memory leak in error path + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..6b67f3e48b1 --- /dev/null +++ b/queue-3.6/usb-usb-wwan-fix-multiple-memory-leaks-in-error-paths.patch @@ -0,0 +1,252 @@ +From b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:16 +0200 +Subject: USB: usb-wwan: fix multiple memory leaks in error paths + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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) + {