OSDN Git Service

USB: serial: garmin_gps: fix I/O after failed probe and remove
authorJohan Hovold <johan@kernel.org>
Wed, 11 Oct 2017 12:02:57 +0000 (14:02 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 21 Nov 2017 08:21:23 +0000 (09:21 +0100)
commit 19a565d9af6e0d828bd0d521d3bafd5017f4ce52 upstream.

Make sure to stop any submitted interrupt and bulk-out URBs before
returning after failed probe and when the port is being unbound to avoid
later NULL-pointer dereferences in the completion callbacks.

Also fix up the related and broken I/O cancellation on failed open and
on close. (Note that port->write_urb was never submitted.)

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/serial/garmin_gps.c

index 37d0e8c..8ac161c 100644 (file)
@@ -138,6 +138,7 @@ struct garmin_data {
        __u8   privpkt[4*6];
        spinlock_t lock;
        struct list_head pktlist;
+       struct usb_anchor write_urbs;
 };
 
 
@@ -906,7 +907,7 @@ static int garmin_init_session(struct usb_serial_port *port)
                                        sizeof(GARMIN_START_SESSION_REQ), 0);
 
                        if (status < 0)
-                               break;
+                               goto err_kill_urbs;
                }
 
                if (status > 0)
@@ -914,6 +915,12 @@ static int garmin_init_session(struct usb_serial_port *port)
        }
 
        return status;
+
+err_kill_urbs:
+       usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
+       usb_kill_urb(port->interrupt_in_urb);
+
+       return status;
 }
 
 
@@ -931,7 +938,6 @@ static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port)
        spin_unlock_irqrestore(&garmin_data_p->lock, flags);
 
        /* shutdown any bulk reads that might be going on */
-       usb_kill_urb(port->write_urb);
        usb_kill_urb(port->read_urb);
 
        if (garmin_data_p->state == STATE_RESET)
@@ -954,7 +960,7 @@ static void garmin_close(struct usb_serial_port *port)
 
        /* shutdown our urbs */
        usb_kill_urb(port->read_urb);
-       usb_kill_urb(port->write_urb);
+       usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
 
        /* keep reset state so we know that we must start a new session */
        if (garmin_data_p->state != STATE_RESET)
@@ -1038,12 +1044,14 @@ static int garmin_write_bulk(struct usb_serial_port *port,
        }
 
        /* send it down the pipe */
+       usb_anchor_urb(urb, &garmin_data_p->write_urbs);
        status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status) {
                dev_err(&port->dev,
                   "%s - usb_submit_urb(write bulk) failed with status = %d\n",
                                __func__, status);
                count = status;
+               usb_unanchor_urb(urb);
                kfree(buffer);
        }
 
@@ -1402,6 +1410,7 @@ static int garmin_port_probe(struct usb_serial_port *port)
        garmin_data_p->state = 0;
        garmin_data_p->flags = 0;
        garmin_data_p->count = 0;
+       init_usb_anchor(&garmin_data_p->write_urbs);
        usb_set_serial_port_data(port, garmin_data_p);
 
        status = garmin_init_session(port);
@@ -1414,6 +1423,7 @@ static int garmin_port_remove(struct usb_serial_port *port)
 {
        struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
 
+       usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
        usb_kill_urb(port->interrupt_in_urb);
        del_timer_sync(&garmin_data_p->timer);
        kfree(garmin_data_p);