| From 9096f1fbba916c2e052651e9de82fcfb98d4bea7 Mon Sep 17 00:00:00 2001 |
| From: Johan Hovold <jhovold@gmail.com> |
| Date: Mon, 26 May 2014 19:23:17 +0200 |
| Subject: USB: usb_wwan: fix potential NULL-deref at resume |
| |
| From: Johan Hovold <jhovold@gmail.com> |
| |
| commit 9096f1fbba916c2e052651e9de82fcfb98d4bea7 upstream. |
| |
| The interrupt urb was submitted unconditionally at resume, something |
| which could lead to a NULL-pointer dereference in the urb completion |
| handler as resume may be called after the port and port data is gone. |
| |
| Fix this by making sure the interrupt urb is only submitted and active |
| when the port is open. |
| |
| Fixes: 383cedc3bb43 ("USB: serial: full autosuspend support for the |
| option driver") |
| |
| Signed-off-by: Johan Hovold <jhovold@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/serial/usb_wwan.c | 43 ++++++++++++++++++------------------------ |
| 1 file changed, 19 insertions(+), 24 deletions(-) |
| |
| --- a/drivers/usb/serial/usb_wwan.c |
| +++ b/drivers/usb/serial/usb_wwan.c |
| @@ -388,6 +388,14 @@ int usb_wwan_open(struct tty_struct *tty |
| portdata = usb_get_serial_port_data(port); |
| intfdata = serial->private; |
| |
| + if (port->interrupt_in_urb) { |
| + err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); |
| + if (err) { |
| + dev_dbg(&port->dev, "%s: submit int urb failed: %d\n", |
| + __func__, err); |
| + } |
| + } |
| + |
| /* Start reading from the IN endpoint */ |
| for (i = 0; i < N_IN_URB; i++) { |
| urb = portdata->in_urbs[i]; |
| @@ -454,6 +462,7 @@ void usb_wwan_close(struct usb_serial_po |
| usb_kill_urb(portdata->in_urbs[i]); |
| for (i = 0; i < N_OUT_URB; i++) |
| usb_kill_urb(portdata->out_urbs[i]); |
| + usb_kill_urb(port->interrupt_in_urb); |
| |
| /* balancing - important as an error cannot be handled*/ |
| usb_autopm_get_interface_no_resume(serial->interface); |
| @@ -487,7 +496,6 @@ int usb_wwan_port_probe(struct usb_seria |
| struct usb_wwan_port_private *portdata; |
| struct urb *urb; |
| u8 *buffer; |
| - int err; |
| int i; |
| |
| if (!port->bulk_in_size || !port->bulk_out_size) |
| @@ -527,13 +535,6 @@ int usb_wwan_port_probe(struct usb_seria |
| |
| usb_set_serial_port_data(port, portdata); |
| |
| - if (port->interrupt_in_urb) { |
| - err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); |
| - if (err) |
| - dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n", |
| - __func__, err); |
| - } |
| - |
| return 0; |
| |
| bail_out_error2: |
| @@ -651,22 +652,6 @@ int usb_wwan_resume(struct usb_serial *s |
| struct urb *urb; |
| int err = 0; |
| |
| - /* get the interrupt URBs resubmitted unconditionally */ |
| - for (i = 0; i < serial->num_ports; i++) { |
| - port = serial->port[i]; |
| - if (!port->interrupt_in_urb) { |
| - dev_dbg(&port->dev, "%s: No interrupt URB for port\n", __func__); |
| - continue; |
| - } |
| - err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); |
| - dev_dbg(&port->dev, "Submitted interrupt URB for port (result %d)\n", err); |
| - if (err < 0) { |
| - dev_err(&port->dev, "%s: Error %d for interrupt URB\n", |
| - __func__, err); |
| - goto err_out; |
| - } |
| - } |
| - |
| spin_lock_irq(&intfdata->susp_lock); |
| for (i = 0; i < serial->num_ports; i++) { |
| /* walk all ports */ |
| @@ -677,6 +662,16 @@ int usb_wwan_resume(struct usb_serial *s |
| if (!portdata || !portdata->opened) |
| continue; |
| |
| + if (port->interrupt_in_urb) { |
| + err = usb_submit_urb(port->interrupt_in_urb, |
| + GFP_ATOMIC); |
| + if (err) { |
| + dev_err(&port->dev, |
| + "%s: submit int urb failed: %d\n", |
| + __func__, err); |
| + } |
| + } |
| + |
| for (j = 0; j < N_IN_URB; j++) { |
| urb = portdata->in_urbs[j]; |
| err = usb_submit_urb(urb, GFP_ATOMIC); |