提交 69626f23 编写于 作者: O Oliver Neukum 提交者: Jiri Kosina

HID: fix race between open() and disconnect() in usbhid

There is a window:

task A					task B
spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
usb_set_intfdata(intf, NULL);
spin_unlock_irq(&usbhid->inlock);
usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbout);
usb_kill_urb(usbhid->urbctrl);

del_timer_sync(&usbhid->io_retry);
cancel_work_sync(&usbhid->reset_work);

						if (!hid->open++) {
							res = usb_autopm_get_interface(usbhid->intf);
							if (res < 0) {
								hid->open--;
								return -EIO;
							}
						}
						if (hid_start_in(hid))

if (hid->claimed & HID_CLAIMED_INPUT)
	hidinput_disconnect(hid);

in which an open() to an already disconnected device will submit an URB
to an undead device. In case disconnect() was called by an ioctl, this'll
oops. Fix by introducing a new flag and checking it in hid_start_in().
Signed-off-by: NOliver Neukum <oneukum@suse.de>
Signed-off-by: NJiri Kosina <jkosina@suse.cz>
上级 abdff0f7
...@@ -82,6 +82,7 @@ static int hid_start_in(struct hid_device *hid) ...@@ -82,6 +82,7 @@ static int hid_start_in(struct hid_device *hid)
spin_lock_irqsave(&usbhid->inlock, flags); spin_lock_irqsave(&usbhid->inlock, flags);
if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) && if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) if (rc != 0)
...@@ -155,7 +156,7 @@ static void hid_io_error(struct hid_device *hid) ...@@ -155,7 +156,7 @@ static void hid_io_error(struct hid_device *hid)
spin_lock_irqsave(&usbhid->inlock, flags); spin_lock_irqsave(&usbhid->inlock, flags);
/* Stop when disconnected */ /* Stop when disconnected */
if (usb_get_intfdata(usbhid->intf) == NULL) if (test_bit(HID_DISCONNECTED, &usbhid->iofl))
goto done; goto done;
/* If it has been a while since the last error, we'll assume /* If it has been a while since the last error, we'll assume
...@@ -941,6 +942,7 @@ static void hid_disconnect(struct usb_interface *intf) ...@@ -941,6 +942,7 @@ static void hid_disconnect(struct usb_interface *intf)
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
set_bit(HID_DISCONNECTED, &usbhid->iofl);
spin_unlock_irq(&usbhid->inlock); spin_unlock_irq(&usbhid->inlock);
usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbout); usb_kill_urb(usbhid->urbout);
......
...@@ -424,6 +424,7 @@ struct hid_control_fifo { ...@@ -424,6 +424,7 @@ struct hid_control_fifo {
#define HID_RESET_PENDING 4 #define HID_RESET_PENDING 4
#define HID_SUSPENDED 5 #define HID_SUSPENDED 5
#define HID_CLEAR_HALT 6 #define HID_CLEAR_HALT 6
#define HID_DISCONNECTED 7
struct hid_input { struct hid_input {
struct list_head list; struct list_head list;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册