diff options
author | Johan Hovold <jhovold@gmail.com> | 2012-10-25 04:29:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-10-25 12:37:13 -0400 |
commit | b8f0e82044c9ba40e92340c8a6d47d6bd6d819bc (patch) | |
tree | 3b184622994a7ebc76b743dd7df16534a5c826d8 /drivers/usb | |
parent | f79b2d0fe81eecb412dc48e87a119afc690da8e9 (diff) |
USB: usb-wwan: fix multiple memory leaks in error paths
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/serial/ipw.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/option.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/qcserial.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/usb-wwan.h | 2 | ||||
-rw-r--r-- | drivers/usb/serial/usb_wwan.c | 124 |
5 files changed, 54 insertions, 78 deletions
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 20a132ec39e2..add45b7d8aa7 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c | |||
@@ -304,8 +304,8 @@ static struct usb_serial_driver ipw_device = { | |||
304 | .open = ipw_open, | 304 | .open = ipw_open, |
305 | .close = ipw_close, | 305 | .close = ipw_close, |
306 | .probe = ipw_probe, | 306 | .probe = ipw_probe, |
307 | .attach = usb_wwan_startup, | ||
308 | .release = ipw_release, | 307 | .release = ipw_release, |
308 | .port_probe = usb_wwan_port_probe, | ||
309 | .port_remove = usb_wwan_port_remove, | 309 | .port_remove = usb_wwan_port_remove, |
310 | .dtr_rts = ipw_dtr_rts, | 310 | .dtr_rts = ipw_dtr_rts, |
311 | .write = usb_wwan_write, | 311 | .write = usb_wwan_write, |
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 54d4148d01d1..eb4bdd4a0106 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c | |||
@@ -1288,8 +1288,8 @@ static struct usb_serial_driver option_1port_device = { | |||
1288 | .tiocmget = usb_wwan_tiocmget, | 1288 | .tiocmget = usb_wwan_tiocmget, |
1289 | .tiocmset = usb_wwan_tiocmset, | 1289 | .tiocmset = usb_wwan_tiocmset, |
1290 | .ioctl = usb_wwan_ioctl, | 1290 | .ioctl = usb_wwan_ioctl, |
1291 | .attach = usb_wwan_startup, | ||
1292 | .release = option_release, | 1291 | .release = option_release, |
1292 | .port_probe = usb_wwan_port_probe, | ||
1293 | .port_remove = usb_wwan_port_remove, | 1293 | .port_remove = usb_wwan_port_remove, |
1294 | .read_int_callback = option_instat_callback, | 1294 | .read_int_callback = option_instat_callback, |
1295 | #ifdef CONFIG_PM | 1295 | #ifdef CONFIG_PM |
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index c3ddb65c05f2..8dd2280dff63 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c | |||
@@ -285,8 +285,8 @@ static struct usb_serial_driver qcdevice = { | |||
285 | .write = usb_wwan_write, | 285 | .write = usb_wwan_write, |
286 | .write_room = usb_wwan_write_room, | 286 | .write_room = usb_wwan_write_room, |
287 | .chars_in_buffer = usb_wwan_chars_in_buffer, | 287 | .chars_in_buffer = usb_wwan_chars_in_buffer, |
288 | .attach = usb_wwan_startup, | ||
289 | .release = qc_release, | 288 | .release = qc_release, |
289 | .port_probe = usb_wwan_port_probe, | ||
290 | .port_remove = usb_wwan_port_remove, | 290 | .port_remove = usb_wwan_port_remove, |
291 | #ifdef CONFIG_PM | 291 | #ifdef CONFIG_PM |
292 | .suspend = usb_wwan_suspend, | 292 | .suspend = usb_wwan_suspend, |
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 1f034d2397c6..684739b8efd0 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h | |||
@@ -8,7 +8,7 @@ | |||
8 | extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on); | 8 | extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on); |
9 | extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); | 9 | extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); |
10 | extern void usb_wwan_close(struct usb_serial_port *port); | 10 | extern void usb_wwan_close(struct usb_serial_port *port); |
11 | extern int usb_wwan_startup(struct usb_serial *serial); | 11 | extern int usb_wwan_port_probe(struct usb_serial_port *port); |
12 | extern int usb_wwan_port_remove(struct usb_serial_port *port); | 12 | extern int usb_wwan_port_remove(struct usb_serial_port *port); |
13 | extern int usb_wwan_write_room(struct tty_struct *tty); | 13 | extern int usb_wwan_write_room(struct tty_struct *tty); |
14 | extern void usb_wwan_set_termios(struct tty_struct *tty, | 14 | extern void usb_wwan_set_termios(struct tty_struct *tty, |
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index e42aa398ed37..61a73ad1a187 100644 --- 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_port *port) | |||
447 | EXPORT_SYMBOL(usb_wwan_close); | 447 | EXPORT_SYMBOL(usb_wwan_close); |
448 | 448 | ||
449 | /* Helper functions used by usb_wwan_setup_urbs */ | 449 | /* Helper functions used by usb_wwan_setup_urbs */ |
450 | static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, | 450 | static struct urb *usb_wwan_setup_urb(struct usb_serial_port *port, |
451 | int endpoint, | ||
451 | int dir, void *ctx, char *buf, int len, | 452 | int dir, void *ctx, char *buf, int len, |
452 | void (*callback) (struct urb *)) | 453 | void (*callback) (struct urb *)) |
453 | { | 454 | { |
455 | struct usb_serial *serial = port->serial; | ||
454 | struct urb *urb; | 456 | struct urb *urb; |
455 | 457 | ||
456 | if (endpoint == -1) | 458 | if (endpoint == -1) |
@@ -472,101 +474,75 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint, | |||
472 | return urb; | 474 | return urb; |
473 | } | 475 | } |
474 | 476 | ||
475 | /* Setup urbs */ | 477 | int usb_wwan_port_probe(struct usb_serial_port *port) |
476 | static void usb_wwan_setup_urbs(struct usb_serial *serial) | ||
477 | { | 478 | { |
478 | int i, j; | ||
479 | struct usb_serial_port *port; | ||
480 | struct usb_wwan_port_private *portdata; | 479 | struct usb_wwan_port_private *portdata; |
480 | struct urb *urb; | ||
481 | u8 *buffer; | ||
482 | int err; | ||
483 | int i; | ||
481 | 484 | ||
482 | for (i = 0; i < serial->num_ports; i++) { | 485 | portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); |
483 | port = serial->port[i]; | 486 | if (!portdata) |
484 | portdata = usb_get_serial_port_data(port); | 487 | return -ENOMEM; |
485 | 488 | ||
486 | /* Do indat endpoints first */ | 489 | init_usb_anchor(&portdata->delayed); |
487 | for (j = 0; j < N_IN_URB; ++j) { | ||
488 | portdata->in_urbs[j] = usb_wwan_setup_urb(serial, | ||
489 | port-> | ||
490 | bulk_in_endpointAddress, | ||
491 | USB_DIR_IN, | ||
492 | port, | ||
493 | portdata-> | ||
494 | in_buffer[j], | ||
495 | IN_BUFLEN, | ||
496 | usb_wwan_indat_callback); | ||
497 | } | ||
498 | 490 | ||
499 | /* outdat endpoints */ | 491 | for (i = 0; i < N_IN_URB; i++) { |
500 | for (j = 0; j < N_OUT_URB; ++j) { | 492 | buffer = (u8 *)__get_free_page(GFP_KERNEL); |
501 | portdata->out_urbs[j] = usb_wwan_setup_urb(serial, | 493 | if (!buffer) |
502 | port-> | 494 | goto bail_out_error; |
503 | bulk_out_endpointAddress, | 495 | portdata->in_buffer[i] = buffer; |
504 | USB_DIR_OUT, | 496 | |
505 | port, | 497 | urb = usb_wwan_setup_urb(port, port->bulk_in_endpointAddress, |
506 | portdata-> | 498 | USB_DIR_IN, port, |
507 | out_buffer | 499 | buffer, IN_BUFLEN, |
508 | [j], | 500 | usb_wwan_indat_callback); |
509 | OUT_BUFLEN, | 501 | portdata->in_urbs[i] = urb; |
510 | usb_wwan_outdat_callback); | ||
511 | } | ||
512 | } | 502 | } |
513 | } | ||
514 | |||
515 | int usb_wwan_startup(struct usb_serial *serial) | ||
516 | { | ||
517 | int i, j, err; | ||
518 | struct usb_serial_port *port; | ||
519 | struct usb_wwan_port_private *portdata; | ||
520 | u8 *buffer; | ||
521 | 503 | ||
522 | /* Now setup per port private data */ | 504 | for (i = 0; i < N_OUT_URB; i++) { |
523 | for (i = 0; i < serial->num_ports; i++) { | 505 | if (port->bulk_out_endpointAddress == -1) |
524 | port = serial->port[i]; | 506 | continue; |
525 | portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); | ||
526 | if (!portdata) { | ||
527 | dev_dbg(&port->dev, "%s: kmalloc for usb_wwan_port_private (%d) failed!.\n", | ||
528 | __func__, i); | ||
529 | return 1; | ||
530 | } | ||
531 | init_usb_anchor(&portdata->delayed); | ||
532 | 507 | ||
533 | for (j = 0; j < N_IN_URB; j++) { | 508 | buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); |
534 | buffer = (u8 *) __get_free_page(GFP_KERNEL); | 509 | if (!buffer) |
535 | if (!buffer) | 510 | goto bail_out_error2; |
536 | goto bail_out_error; | 511 | portdata->out_buffer[i] = buffer; |
537 | portdata->in_buffer[j] = buffer; | ||
538 | } | ||
539 | 512 | ||
540 | for (j = 0; j < N_OUT_URB; j++) { | 513 | urb = usb_wwan_setup_urb(port, port->bulk_out_endpointAddress, |
541 | buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL); | 514 | USB_DIR_OUT, port, |
542 | if (!buffer) | 515 | buffer, OUT_BUFLEN, |
543 | goto bail_out_error2; | 516 | usb_wwan_outdat_callback); |
544 | portdata->out_buffer[j] = buffer; | 517 | portdata->out_urbs[i] = urb; |
545 | } | 518 | } |
546 | 519 | ||
547 | usb_set_serial_port_data(port, portdata); | 520 | usb_set_serial_port_data(port, portdata); |
548 | 521 | ||
549 | if (!port->interrupt_in_urb) | 522 | if (port->interrupt_in_urb) { |
550 | continue; | ||
551 | err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); | 523 | err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); |
552 | if (err) | 524 | if (err) |
553 | dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n", | 525 | dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n", |
554 | __func__, err); | 526 | __func__, err); |
555 | } | 527 | } |
556 | usb_wwan_setup_urbs(serial); | 528 | |
557 | return 0; | 529 | return 0; |
558 | 530 | ||
559 | bail_out_error2: | 531 | bail_out_error2: |
560 | for (j = 0; j < N_OUT_URB; j++) | 532 | for (i = 0; i < N_OUT_URB; i++) { |
561 | kfree(portdata->out_buffer[j]); | 533 | usb_free_urb(portdata->out_urbs[i]); |
534 | kfree(portdata->out_buffer[i]); | ||
535 | } | ||
562 | bail_out_error: | 536 | bail_out_error: |
563 | for (j = 0; j < N_IN_URB; j++) | 537 | for (i = 0; i < N_IN_URB; i++) { |
564 | if (portdata->in_buffer[j]) | 538 | usb_free_urb(portdata->in_urbs[i]); |
565 | free_page((unsigned long)portdata->in_buffer[j]); | 539 | free_page((unsigned long)portdata->in_buffer[i]); |
540 | } | ||
566 | kfree(portdata); | 541 | kfree(portdata); |
567 | return 1; | 542 | |
543 | return -ENOMEM; | ||
568 | } | 544 | } |
569 | EXPORT_SYMBOL(usb_wwan_startup); | 545 | EXPORT_SYMBOL_GPL(usb_wwan_port_probe); |
570 | 546 | ||
571 | int usb_wwan_port_remove(struct usb_serial_port *port) | 547 | int usb_wwan_port_remove(struct usb_serial_port *port) |
572 | { | 548 | { |