From: Greg Kroah-Hartman Date: Fri, 26 Oct 2012 22:47:38 +0000 (-0700) Subject: 3.6-stable patches X-Git-Tag: v3.0.49~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ec7053d6bc7ced8609e3dc5b660507d2e28a659d;p=thirdparty%2Fkernel%2Fstable-queue.git 3.6-stable patches 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 --- diff --git a/queue-3.6/series b/queue-3.6/series index 8b87fa3b1a6..b2f3018ab35 100644 --- a/queue-3.6/series +++ b/queue-3.6/series @@ -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 index 00000000000..54f4672f2d3 --- /dev/null +++ b/queue-3.6/usb-keyspan-fix-null-pointer-dereferences-and-memory-leaks.patch @@ -0,0 +1,312 @@ +From f79b2d0fe81eecb412dc48e87a119afc690da8e9 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:15 +0200 +Subject: USB: keyspan: fix NULL-pointer dereferences and memory leaks + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..ae749439dcb --- /dev/null +++ b/queue-3.6/usb-omninet-fix-port-data-memory-leak.patch @@ -0,0 +1,104 @@ +From feffa7ca6008ab859dd7ab7448a5a899bf0aa98f Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:06 +0200 +Subject: USB: omninet: fix port-data memory leak + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..344619f085e --- /dev/null +++ b/queue-3.6/usb-opticon-fix-dma-from-stack.patch @@ -0,0 +1,44 @@ +From ea0dbebffe118724cd4df7d9b071ea8ee48d48f0 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:11 +0200 +Subject: USB: opticon: fix DMA from stack + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..dab57954e68 --- /dev/null +++ b/queue-3.6/usb-opticon-fix-memory-leak-in-error-path.patch @@ -0,0 +1,38 @@ +From acbf0e5263de563e25f7c104868e4490b9e72b13 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:12 +0200 +Subject: USB: opticon: fix memory leak in error path + +From: Johan Hovold + +commit acbf0e5263de563e25f7c104868e4490b9e72b13 upstream. + +Fix memory leak in write error path. + +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..b233b471f14 --- /dev/null +++ b/queue-3.6/usb-qcserial-fix-interface-data-memory-leak-in-error-path.patch @@ -0,0 +1,87 @@ +From 961be09e1ead58509ed4bed0d5819a15d8613d8d Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 15:42:41 +0200 +Subject: USB: qcserial: fix interface-data memory leak in error path + +From: Johan Hovold + +commit 961be09e1ead58509ed4bed0d5819a15d8613d8d 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/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 index 00000000000..d014d4e8a64 --- /dev/null +++ b/queue-3.6/usb-whiteheat-fix-memory-leak-in-error-path.patch @@ -0,0 +1,29 @@ +From c129197c99550d356cf5f69b046994dd53cd1b9d Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:01 +0200 +Subject: USB: whiteheat: fix memory leak in error path + +From: Johan Hovold + +commit c129197c99550d356cf5f69b046994dd53cd1b9d upstream. + +Make sure command buffer is deallocated in case of errors during attach. + +Signed-off-by: Johan Hovold +Cc: +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..4f1e9d5d735 --- /dev/null +++ b/queue-3.6/usb-whiteheat-fix-port-data-memory-leak.patch @@ -0,0 +1,146 @@ +From c467206ed6bcce26c83d0435612cc4fee2527305 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 25 Oct 2012 10:29:02 +0200 +Subject: USB: whiteheat: fix port-data memory leak + +From: Johan Hovold + +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 +Cc: +Signed-off-by: Greg Kroah-Hartman + +--- + 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)