Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69626f23 authored by Oliver Neukum's avatar Oliver Neukum Committed by Jiri Kosina
Browse files

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: default avatarOliver Neukum <oneukum@suse.de>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent abdff0f7
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ static int hid_start_in(struct hid_device *hid)

	spin_lock_irqsave(&usbhid->inlock, flags);
	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)) {
		rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
		if (rc != 0)
@@ -155,7 +156,7 @@ static void hid_io_error(struct hid_device *hid)
	spin_lock_irqsave(&usbhid->inlock, flags);

	/* Stop when disconnected */
	if (usb_get_intfdata(usbhid->intf) == NULL)
	if (test_bit(HID_DISCONNECTED, &usbhid->iofl))
		goto done;

	/* 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)

	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
	usb_set_intfdata(intf, NULL);
	set_bit(HID_DISCONNECTED, &usbhid->iofl);
	spin_unlock_irq(&usbhid->inlock);
	usb_kill_urb(usbhid->urbin);
	usb_kill_urb(usbhid->urbout);
+1 −0
Original line number Diff line number Diff line
@@ -424,6 +424,7 @@ struct hid_control_fifo {
#define HID_RESET_PENDING	4
#define HID_SUSPENDED		5
#define HID_CLEAR_HALT		6
#define HID_DISCONNECTED	7

struct hid_input {
	struct list_head list;