USB: mos7840: fix race in register handling

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.

Cc: stable@vger.kernel.org
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Johan Hovold 2013-07-26 11:55:17 +02:00 committed by Greg Kroah-Hartman
parent a8825734e9
commit d8a083cc74

View file

@ -183,6 +183,10 @@
#define LED_ON_MS 500
#define LED_OFF_MS 500
enum mos7840_flag {
MOS7840_FLAG_CTRL_BUSY,
};
static int device_type;
static const struct usb_device_id id_table[] = {
@ -241,6 +245,8 @@ struct moschip_port {
bool led_flag;
struct timer_list led_timer1; /* Timer for LED on */
struct timer_list led_timer2; /* Timer for LED off */
unsigned long flags;
};
/*
@ -460,10 +466,10 @@ static void mos7840_control_callback(struct urb *urb)
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
goto out;
default:
dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
return;
goto out;
}
dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
@ -476,6 +482,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,
@ -486,6 +494,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 */
@ -497,6 +508,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;
}