Skip to content

Commit 6296f4a

Browse files
jmaneyrol-invnJiri Kosina
authored andcommitted
HID: i2c-hid: fix race condition reading reports
Current driver uses a common buffer for reading reports either synchronously in i2c_hid_get_raw_report() and asynchronously in the interrupt handler. There is race condition if an interrupt arrives immediately after the report is received in i2c_hid_get_raw_report(); the common buffer is modified by the interrupt handler with the new report and then i2c_hid_get_raw_report() proceed using wrong data. Fix it by using a separate buffers for synchronous reports. Signed-off-by: Jean-Baptiste Maneyrol <[email protected]> [Antonio Borneo: cleanup, rebase to v3.17, submit mainline] Signed-off-by: Antonio Borneo <[email protected]> Reviewed-by: Benjamin Tissoires <[email protected]> Cc: [email protected] Signed-off-by: Jiri Kosina <[email protected]>
1 parent 08bb7be commit 6296f4a

File tree

1 file changed

+8
-4
lines changed

1 file changed

+8
-4
lines changed

drivers/hid/i2c-hid/i2c-hid.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ struct i2c_hid {
137137
* descriptor. */
138138
unsigned int bufsize; /* i2c buffer size */
139139
char *inbuf; /* Input buffer */
140+
char *rawbuf; /* Raw Input buffer */
140141
char *cmdbuf; /* Command buffer */
141142
char *argsbuf; /* Command arguments buffer */
142143

@@ -504,9 +505,11 @@ static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type,
504505
static void i2c_hid_free_buffers(struct i2c_hid *ihid)
505506
{
506507
kfree(ihid->inbuf);
508+
kfree(ihid->rawbuf);
507509
kfree(ihid->argsbuf);
508510
kfree(ihid->cmdbuf);
509511
ihid->inbuf = NULL;
512+
ihid->rawbuf = NULL;
510513
ihid->cmdbuf = NULL;
511514
ihid->argsbuf = NULL;
512515
ihid->bufsize = 0;
@@ -522,10 +525,11 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
522525
report_size; /* report */
523526

524527
ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
528+
ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
525529
ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
526530
ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
527531

528-
if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) {
532+
if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
529533
i2c_hid_free_buffers(ihid);
530534
return -ENOMEM;
531535
}
@@ -552,12 +556,12 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
552556

553557
ret = i2c_hid_get_report(client,
554558
report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
555-
report_number, ihid->inbuf, ask_count);
559+
report_number, ihid->rawbuf, ask_count);
556560

557561
if (ret < 0)
558562
return ret;
559563

560-
ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8);
564+
ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
561565

562566
if (ret_count <= 2)
563567
return 0;
@@ -566,7 +570,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
566570

567571
/* The query buffer contains the size, dropping it in the reply */
568572
count = min(count, ret_count - 2);
569-
memcpy(buf, ihid->inbuf + 2, count);
573+
memcpy(buf, ihid->rawbuf + 2, count);
570574

571575
return count;
572576
}

0 commit comments

Comments
 (0)