USB: mos7840: fix race in register handling
authorJohan Hovold <jhovold@gmail.com>
Fri, 26 Jul 2013 09:55:17 +0000 (11:55 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 14 Apr 2014 13:44:26 +0000 (06:44 -0700)
commit d8a083cc746664916d9d36ed9e4d08a29525f245 upstream.

Fix race in mos7840_get_reg which unconditionally manipulated the
control urb (which may already be in use) by adding a control-urb busy
flag.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Cc: Yang Yingliang <yangyingliang@huawei.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/serial/mos7840.c

index d48b79f..947b866 100644 (file)
 #define URB_TRANSFER_BUFFER_SIZE        32     /* URB Size  */
 
 
+enum mos7840_flag {
+       MOS7840_FLAG_CTRL_BUSY,
+};
+
 static const struct usb_device_id moschip_port_id_table[] = {
        {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
        {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
@@ -258,6 +262,8 @@ struct moschip_port {
        struct urb *write_urb_pool[NUM_URBS];
        char busy[NUM_URBS];
        bool read_urb_busy;
+
+       unsigned long flags;
 };
 
 
@@ -519,11 +525,11 @@ static void mos7840_control_callback(struct urb *urb)
                /* this urb is terminated, clean up */
                dbg("%s - urb shutting down with status: %d", __func__,
                    status);
-               return;
+               goto out;
        default:
                dbg("%s - nonzero urb status received: %d", __func__,
                    status);
-               return;
+               goto out;
        }
 
        dbg("%s urb buffer size is %d", __func__, urb->actual_length);
@@ -536,6 +542,8 @@ static void mos7840_control_callback(struct urb *urb)
                mos7840_handle_new_msr(mos7840_port, regval);
        else if (mos7840_port->MsrLsr == 1)
                mos7840_handle_new_lsr(mos7840_port, regval);
+out:
+       clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mos7840_port->flags);
 }
 
 static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
@@ -546,6 +554,9 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
        unsigned char *buffer = mcs->ctrl_buf;
        int ret;
 
+       if (test_and_set_bit_lock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags))
+               return -EBUSY;
+
        dr->bRequestType = MCS_RD_RTYPE;
        dr->bRequest = MCS_RDREQ;
        dr->wValue = cpu_to_le16(Wval); /* 0 */
@@ -557,6 +568,9 @@ static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
                             mos7840_control_callback, mcs);
        mcs->control_urb->transfer_buffer_length = 2;
        ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+       if (ret)
+               clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags);
+
        return ret;
 }