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
--- /dev/null
+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[] = {
--- /dev/null
+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);
--- /dev/null
+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;
+ }
--- /dev/null
+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);
--- /dev/null
+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,
--- /dev/null
+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:
--- /dev/null
+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)