Skip to content

Commit 4ea5763

Browse files
thejhJiri Kosina
authored andcommitted
HID: uhid: Fix worker destroying device without any protection
uhid has to run hid_add_device() from workqueue context while allowing parallel use of the userspace API (which is protected with ->devlock). But hid_add_device() can fail. Currently, that is handled by immediately destroying the associated HID device, without using ->devlock - but if there are concurrent requests from userspace, that's wrong and leads to NULL dereferences and/or memory corruption (via use-after-free). Fix it by leaving the HID device as-is in the worker. We can clean it up later, either in the UHID_DESTROY command handler or in the ->release() handler. Cc: stable@vger.kernel.org Fixes: 67f8ecc ("HID: uhid: fix timeout when probe races with IO") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent e24aeff commit 4ea5763

File tree

1 file changed

+25
-4
lines changed

1 file changed

+25
-4
lines changed

drivers/hid/uhid.c

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,22 @@
2828

2929
struct uhid_device {
3030
struct mutex devlock;
31+
32+
/* This flag tracks whether the HID device is usable for commands from
33+
* userspace. The flag is already set before hid_add_device(), which
34+
* runs in workqueue context, to allow hid_add_device() to communicate
35+
* with userspace.
36+
* However, if hid_add_device() fails, the flag is cleared without
37+
* holding devlock.
38+
* We guarantee that if @running changes from true to false while you're
39+
* holding @devlock, it's still fine to access @hid.
40+
*/
3141
bool running;
3242

3343
__u8 *rd_data;
3444
uint rd_size;
3545

46+
/* When this is NULL, userspace may use UHID_CREATE/UHID_CREATE2. */
3647
struct hid_device *hid;
3748
struct uhid_event input_buf;
3849

@@ -63,9 +74,18 @@ static void uhid_device_add_worker(struct work_struct *work)
6374
if (ret) {
6475
hid_err(uhid->hid, "Cannot register HID device: error %d\n", ret);
6576

66-
hid_destroy_device(uhid->hid);
67-
uhid->hid = NULL;
77+
/* We used to call hid_destroy_device() here, but that's really
78+
* messy to get right because we have to coordinate with
79+
* concurrent writes from userspace that might be in the middle
80+
* of using uhid->hid.
81+
* Just leave uhid->hid as-is for now, and clean it up when
82+
* userspace tries to close or reinitialize the uhid instance.
83+
*
84+
* However, we do have to clear the ->running flag and do a
85+
* wakeup to make sure userspace knows that the device is gone.
86+
*/
6887
uhid->running = false;
88+
wake_up_interruptible(&uhid->report_wait);
6989
}
7090
}
7191

@@ -474,7 +494,7 @@ static int uhid_dev_create2(struct uhid_device *uhid,
474494
void *rd_data;
475495
int ret;
476496

477-
if (uhid->running)
497+
if (uhid->hid)
478498
return -EALREADY;
479499

480500
rd_size = ev->u.create2.rd_size;
@@ -556,7 +576,7 @@ static int uhid_dev_create(struct uhid_device *uhid,
556576

557577
static int uhid_dev_destroy(struct uhid_device *uhid)
558578
{
559-
if (!uhid->running)
579+
if (!uhid->hid)
560580
return -EINVAL;
561581

562582
uhid->running = false;
@@ -565,6 +585,7 @@ static int uhid_dev_destroy(struct uhid_device *uhid)
565585
cancel_work_sync(&uhid->worker);
566586

567587
hid_destroy_device(uhid->hid);
588+
uhid->hid = NULL;
568589
kfree(uhid->rd_data);
569590

570591
return 0;

0 commit comments

Comments
 (0)