Skip to content

Commit 0ed08fa

Browse files
AlanSternJiri Kosina
authored andcommitted
HID: usbhid: Fix race between usbhid_close() and usbhid_stop()
The syzbot fuzzer discovered a bad race between in the usbhid driver between usbhid_stop() and usbhid_close(). In particular, usbhid_stop() does: usb_free_urb(usbhid->urbin); ... usbhid->urbin = NULL; /* don't mess up next start */ and usbhid_close() does: usb_kill_urb(usbhid->urbin); with no mutual exclusion. If the two routines happen to run concurrently so that usb_kill_urb() is called in between the usb_free_urb() and the NULL assignment, it will access the deallocated urb structure -- a use-after-free bug. This patch adds a mutex to the usbhid private structure and uses it to enforce mutual exclusion of the usbhid_start(), usbhid_stop(), usbhid_open() and usbhid_close() callbacks. Reported-and-tested-by: [email protected] Signed-off-by: Alan Stern <[email protected]> CC: <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent b43f977 commit 0ed08fa

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

drivers/hid/usbhid/hid-core.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -682,16 +682,21 @@ static int usbhid_open(struct hid_device *hid)
682682
struct usbhid_device *usbhid = hid->driver_data;
683683
int res;
684684

685+
mutex_lock(&usbhid->mutex);
686+
685687
set_bit(HID_OPENED, &usbhid->iofl);
686688

687-
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
688-
return 0;
689+
if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
690+
res = 0;
691+
goto Done;
692+
}
689693

690694
res = usb_autopm_get_interface(usbhid->intf);
691695
/* the device must be awake to reliably request remote wakeup */
692696
if (res < 0) {
693697
clear_bit(HID_OPENED, &usbhid->iofl);
694-
return -EIO;
698+
res = -EIO;
699+
goto Done;
695700
}
696701

697702
usbhid->intf->needs_remote_wakeup = 1;
@@ -725,13 +730,18 @@ static int usbhid_open(struct hid_device *hid)
725730
msleep(50);
726731

727732
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
733+
734+
Done:
735+
mutex_unlock(&usbhid->mutex);
728736
return res;
729737
}
730738

731739
static void usbhid_close(struct hid_device *hid)
732740
{
733741
struct usbhid_device *usbhid = hid->driver_data;
734742

743+
mutex_lock(&usbhid->mutex);
744+
735745
/*
736746
* Make sure we don't restart data acquisition due to
737747
* a resumption we no longer care about by avoiding racing
@@ -743,12 +753,13 @@ static void usbhid_close(struct hid_device *hid)
743753
clear_bit(HID_IN_POLLING, &usbhid->iofl);
744754
spin_unlock_irq(&usbhid->lock);
745755

746-
if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
747-
return;
756+
if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) {
757+
hid_cancel_delayed_stuff(usbhid);
758+
usb_kill_urb(usbhid->urbin);
759+
usbhid->intf->needs_remote_wakeup = 0;
760+
}
748761

749-
hid_cancel_delayed_stuff(usbhid);
750-
usb_kill_urb(usbhid->urbin);
751-
usbhid->intf->needs_remote_wakeup = 0;
762+
mutex_unlock(&usbhid->mutex);
752763
}
753764

754765
/*
@@ -1057,6 +1068,8 @@ static int usbhid_start(struct hid_device *hid)
10571068
unsigned int n, insize = 0;
10581069
int ret;
10591070

1071+
mutex_lock(&usbhid->mutex);
1072+
10601073
clear_bit(HID_DISCONNECTED, &usbhid->iofl);
10611074

10621075
usbhid->bufsize = HID_MIN_BUFFER_SIZE;
@@ -1177,6 +1190,8 @@ static int usbhid_start(struct hid_device *hid)
11771190
usbhid_set_leds(hid);
11781191
device_set_wakeup_enable(&dev->dev, 1);
11791192
}
1193+
1194+
mutex_unlock(&usbhid->mutex);
11801195
return 0;
11811196

11821197
fail:
@@ -1187,6 +1202,7 @@ static int usbhid_start(struct hid_device *hid)
11871202
usbhid->urbout = NULL;
11881203
usbhid->urbctrl = NULL;
11891204
hid_free_buffers(dev, hid);
1205+
mutex_unlock(&usbhid->mutex);
11901206
return ret;
11911207
}
11921208

@@ -1202,6 +1218,8 @@ static void usbhid_stop(struct hid_device *hid)
12021218
usbhid->intf->needs_remote_wakeup = 0;
12031219
}
12041220

1221+
mutex_lock(&usbhid->mutex);
1222+
12051223
clear_bit(HID_STARTED, &usbhid->iofl);
12061224
spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */
12071225
set_bit(HID_DISCONNECTED, &usbhid->iofl);
@@ -1222,6 +1240,8 @@ static void usbhid_stop(struct hid_device *hid)
12221240
usbhid->urbout = NULL;
12231241

12241242
hid_free_buffers(hid_to_usb_dev(hid), hid);
1243+
1244+
mutex_unlock(&usbhid->mutex);
12251245
}
12261246

12271247
static int usbhid_power(struct hid_device *hid, int lvl)
@@ -1382,6 +1402,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
13821402
INIT_WORK(&usbhid->reset_work, hid_reset);
13831403
timer_setup(&usbhid->io_retry, hid_retry_timeout, 0);
13841404
spin_lock_init(&usbhid->lock);
1405+
mutex_init(&usbhid->mutex);
13851406

13861407
ret = hid_add_device(hid);
13871408
if (ret) {

drivers/hid/usbhid/usbhid.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct usbhid_device {
8080
dma_addr_t outbuf_dma; /* Output buffer dma */
8181
unsigned long last_out; /* record of last output for timeouts */
8282

83+
struct mutex mutex; /* start/stop/open/close */
8384
spinlock_t lock; /* fifo spinlock */
8485
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
8586
struct timer_list io_retry; /* Retry timer */

0 commit comments

Comments
 (0)