summaryrefslogtreecommitdiff
path: root/drivers/usb/serial
diff options
context:
space:
mode:
authorJohan Hovold <jhovold@gmail.com>2014-05-26 21:23:17 +0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-28 02:04:06 +0400
commit9096f1fbba916c2e052651e9de82fcfb98d4bea7 (patch)
treedb0db9168fd162bc424e8e37156783681e36580f /drivers/usb/serial
parent79eed03e77d481b55d85d1cfe5a1636a0d3897fd (diff)
downloadlinux-9096f1fbba916c2e052651e9de82fcfb98d4bea7.tar.xz
USB: usb_wwan: fix potential NULL-deref at resume
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") Cc: <stable@vger.kernel.org> # v2.6.32: 032129cb03df Signed-off-by: Johan Hovold <jhovold@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/serial')
-rw-r--r--drivers/usb/serial/usb_wwan.c43
1 files changed, 19 insertions, 24 deletions
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 2ab478b55759..c5b9deb2e04e 100644
--- 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, struct usb_serial_port *port)
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_port *port)
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_serial_port *port)
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_serial_port *port)
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 *serial)
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 *serial)
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);