Skip to content

Commit 9b57f45

Browse files
committed
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID fixes from Jiri Kosina: - fix for race condition that could lead to NULL pointer dereferences or UAF during uhid device destruction (Jann Horn) - contact count handling regression fixes for Wacom devices (Jason Gerecke) - fix for handling unnumbered HID reports handling in Google Vivaldi driver (Dmitry Torokhov) * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: HID: wacom: Avoid using stale array indicies to read contact count HID: wacom: Ignore the confidence flag when a touch is removed HID: wacom: Reset expected and received contact counts at the same time HID: uhid: Use READ_ONCE()/WRITE_ONCE() for ->running HID: uhid: Fix worker destroying device without any protection HID: vivaldi: Minor cleanups HID: vivaldi: fix handling devices not using numbered reports HID: Ignore battery for Elan touchscreen on HP Envy X360 15t-dr100
2 parents 3c7c250 + 20f3cf5 commit 9b57f45

File tree

5 files changed

+105
-27
lines changed

5 files changed

+105
-27
lines changed

drivers/hid/hid-ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@
400400
#define USB_DEVICE_ID_HP_X2 0x074d
401401
#define USB_DEVICE_ID_HP_X2_10_COVER 0x0755
402402
#define I2C_DEVICE_ID_HP_ENVY_X360_15 0x2d05
403+
#define I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100 0x29CF
403404
#define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817
404405
#define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544
405406
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706

drivers/hid/hid-input.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
330330
HID_BATTERY_QUIRK_IGNORE },
331331
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15),
332332
HID_BATTERY_QUIRK_IGNORE },
333+
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100),
334+
HID_BATTERY_QUIRK_IGNORE },
333335
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15),
334336
HID_BATTERY_QUIRK_IGNORE },
335337
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN),

drivers/hid/hid-vivaldi.c

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
* Author: Sean O'Brien <seobrien@chromium.org>
77
*/
88

9+
#include <linux/device.h>
910
#include <linux/hid.h>
11+
#include <linux/kernel.h>
1012
#include <linux/module.h>
13+
#include <linux/sysfs.h>
1114

1215
#define MIN_FN_ROW_KEY 1
1316
#define MAX_FN_ROW_KEY 24
1417
#define HID_VD_FN_ROW_PHYSMAP 0x00000001
1518
#define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
1619

17-
static struct hid_driver hid_vivaldi;
18-
1920
struct vivaldi_data {
2021
u32 function_row_physmap[MAX_FN_ROW_KEY - MIN_FN_ROW_KEY + 1];
2122
int max_function_row_key;
@@ -40,7 +41,7 @@ static ssize_t function_row_physmap_show(struct device *dev,
4041
return size;
4142
}
4243

43-
DEVICE_ATTR_RO(function_row_physmap);
44+
static DEVICE_ATTR_RO(function_row_physmap);
4445
static struct attribute *sysfs_attrs[] = {
4546
&dev_attr_function_row_physmap.attr,
4647
NULL
@@ -74,10 +75,11 @@ static void vivaldi_feature_mapping(struct hid_device *hdev,
7475
struct hid_usage *usage)
7576
{
7677
struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
78+
struct hid_report *report = field->report;
7779
int fn_key;
7880
int ret;
7981
u32 report_len;
80-
u8 *buf;
82+
u8 *report_data, *buf;
8183

8284
if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
8385
(usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
@@ -89,12 +91,24 @@ static void vivaldi_feature_mapping(struct hid_device *hdev,
8991
if (fn_key > drvdata->max_function_row_key)
9092
drvdata->max_function_row_key = fn_key;
9193

92-
buf = hid_alloc_report_buf(field->report, GFP_KERNEL);
93-
if (!buf)
94+
report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
95+
if (!report_data)
9496
return;
9597

96-
report_len = hid_report_len(field->report);
97-
ret = hid_hw_raw_request(hdev, field->report->id, buf,
98+
report_len = hid_report_len(report);
99+
if (!report->id) {
100+
/*
101+
* hid_hw_raw_request() will stuff report ID (which will be 0)
102+
* into the first byte of the buffer even for unnumbered
103+
* reports, so we need to account for this to avoid getting
104+
* -EOVERFLOW in return.
105+
* Note that hid_alloc_report_buf() adds 7 bytes to the size
106+
* so we can safely say that we have space for an extra byte.
107+
*/
108+
report_len++;
109+
}
110+
111+
ret = hid_hw_raw_request(hdev, report->id, report_data,
98112
report_len, HID_FEATURE_REPORT,
99113
HID_REQ_GET_REPORT);
100114
if (ret < 0) {
@@ -103,7 +117,16 @@ static void vivaldi_feature_mapping(struct hid_device *hdev,
103117
goto out;
104118
}
105119

106-
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
120+
if (!report->id) {
121+
/*
122+
* Undo the damage from hid_hw_raw_request() for unnumbered
123+
* reports.
124+
*/
125+
report_data++;
126+
report_len--;
127+
}
128+
129+
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
107130
report_len, 0);
108131
if (ret) {
109132
dev_warn(&hdev->dev, "failed to report feature %d\n",

drivers/hid/uhid.c

Lines changed: 35 additions & 14 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;
68-
uhid->running = false;
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+
*/
87+
WRITE_ONCE(uhid->running, false);
88+
wake_up_interruptible(&uhid->report_wait);
6989
}
7090
}
7191

@@ -174,9 +194,9 @@ static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
174194
spin_unlock_irqrestore(&uhid->qlock, flags);
175195

176196
ret = wait_event_interruptible_timeout(uhid->report_wait,
177-
!uhid->report_running || !uhid->running,
197+
!uhid->report_running || !READ_ONCE(uhid->running),
178198
5 * HZ);
179-
if (!ret || !uhid->running || uhid->report_running)
199+
if (!ret || !READ_ONCE(uhid->running) || uhid->report_running)
180200
ret = -EIO;
181201
else if (ret < 0)
182202
ret = -ERESTARTSYS;
@@ -217,7 +237,7 @@ static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
217237
struct uhid_event *ev;
218238
int ret;
219239

220-
if (!uhid->running)
240+
if (!READ_ONCE(uhid->running))
221241
return -EIO;
222242

223243
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
@@ -259,7 +279,7 @@ static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
259279
struct uhid_event *ev;
260280
int ret;
261281

262-
if (!uhid->running || count > UHID_DATA_MAX)
282+
if (!READ_ONCE(uhid->running) || count > UHID_DATA_MAX)
263283
return -EIO;
264284

265285
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
@@ -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,23 +576,24 @@ 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

562-
uhid->running = false;
582+
WRITE_ONCE(uhid->running, false);
563583
wake_up_interruptible(&uhid->report_wait);
564584

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;
571592
}
572593

573594
static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
574595
{
575-
if (!uhid->running)
596+
if (!READ_ONCE(uhid->running))
576597
return -EINVAL;
577598

578599
hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data,
@@ -583,7 +604,7 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)
583604

584605
static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
585606
{
586-
if (!uhid->running)
607+
if (!READ_ONCE(uhid->running))
587608
return -EINVAL;
588609

589610
hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data,
@@ -595,7 +616,7 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
595616
static int uhid_dev_get_report_reply(struct uhid_device *uhid,
596617
struct uhid_event *ev)
597618
{
598-
if (!uhid->running)
619+
if (!READ_ONCE(uhid->running))
599620
return -EINVAL;
600621

601622
uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
@@ -605,7 +626,7 @@ static int uhid_dev_get_report_reply(struct uhid_device *uhid,
605626
static int uhid_dev_set_report_reply(struct uhid_device *uhid,
606627
struct uhid_event *ev)
607628
{
608-
if (!uhid->running)
629+
if (!READ_ONCE(uhid->running))
609630
return -EINVAL;
610631

611632
uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);

drivers/hid/wacom_wac.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,24 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
25882588
}
25892589
}
25902590

2591+
static bool wacom_wac_slot_is_active(struct input_dev *dev, int key)
2592+
{
2593+
struct input_mt *mt = dev->mt;
2594+
struct input_mt_slot *s;
2595+
2596+
if (!mt)
2597+
return false;
2598+
2599+
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
2600+
if (s->key == key &&
2601+
input_mt_get_value(s, ABS_MT_TRACKING_ID) >= 0) {
2602+
return true;
2603+
}
2604+
}
2605+
2606+
return false;
2607+
}
2608+
25912609
static void wacom_wac_finger_event(struct hid_device *hdev,
25922610
struct hid_field *field, struct hid_usage *usage, __s32 value)
25932611
{
@@ -2638,9 +2656,14 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
26382656
}
26392657

26402658
if (usage->usage_index + 1 == field->report_count) {
2641-
if (equivalent_usage == wacom_wac->hid_data.last_slot_field &&
2642-
wacom_wac->hid_data.confidence)
2643-
wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
2659+
if (equivalent_usage == wacom_wac->hid_data.last_slot_field) {
2660+
bool touch_removed = wacom_wac_slot_is_active(wacom_wac->touch_input,
2661+
wacom_wac->hid_data.id) && !wacom_wac->hid_data.tipswitch;
2662+
2663+
if (wacom_wac->hid_data.confidence || touch_removed) {
2664+
wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
2665+
}
2666+
}
26442667
}
26452668
}
26462669

@@ -2659,6 +2682,10 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
26592682

26602683
hid_data->confidence = true;
26612684

2685+
hid_data->cc_report = 0;
2686+
hid_data->cc_index = -1;
2687+
hid_data->cc_value_index = -1;
2688+
26622689
for (i = 0; i < report->maxfield; i++) {
26632690
struct hid_field *field = report->field[i];
26642691
int j;
@@ -2692,11 +2719,14 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
26922719
hid_data->cc_index >= 0) {
26932720
struct hid_field *field = report->field[hid_data->cc_index];
26942721
int value = field->value[hid_data->cc_value_index];
2695-
if (value)
2722+
if (value) {
26962723
hid_data->num_expected = value;
2724+
hid_data->num_received = 0;
2725+
}
26972726
}
26982727
else {
26992728
hid_data->num_expected = wacom_wac->features.touch_max;
2729+
hid_data->num_received = 0;
27002730
}
27012731
}
27022732

@@ -2724,6 +2754,7 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
27242754

27252755
input_sync(input);
27262756
wacom_wac->hid_data.num_received = 0;
2757+
wacom_wac->hid_data.num_expected = 0;
27272758

27282759
/* keep touch state for pen event */
27292760
wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac);

0 commit comments

Comments
 (0)