Skip to content

Commit

Permalink
USB: usb_wwan: fix potential NULL-deref at resume
Browse files Browse the repository at this point in the history
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: 383cedc ("USB: serial: full autosuspend support for the
option driver")

Cc: <stable@vger.kernel.org>  # v2.6.32: 032129c
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Johan Hovold authored and Greg Kroah-Hartman committed May 27, 2014
1 parent 79eed03 commit 9096f1f
Showing 1 changed file with 19 additions and 24 deletions.
43 changes: 19 additions & 24 deletions drivers/usb/serial/usb_wwan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 */
Expand All @@ -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);
Expand Down

0 comments on commit 9096f1f

Please sign in to comment.