Commit 6296f4a8 authored by Jean-Baptiste Maneyrol's avatar Jean-Baptiste Maneyrol Committed by Jiri Kosina

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: default avatarJean-Baptiste Maneyrol <jmaneyrol@invensense.com>
[Antonio Borneo: cleanup, rebase to v3.17, submit mainline]
Signed-off-by: default avatarAntonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 08bb7bea
...@@ -137,6 +137,7 @@ struct i2c_hid { ...@@ -137,6 +137,7 @@ struct i2c_hid {
* descriptor. */ * descriptor. */
unsigned int bufsize; /* i2c buffer size */ unsigned int bufsize; /* i2c buffer size */
char *inbuf; /* Input buffer */ char *inbuf; /* Input buffer */
char *rawbuf; /* Raw Input buffer */
char *cmdbuf; /* Command buffer */ char *cmdbuf; /* Command buffer */
char *argsbuf; /* Command arguments buffer */ char *argsbuf; /* Command arguments buffer */
...@@ -504,9 +505,11 @@ static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type, ...@@ -504,9 +505,11 @@ static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type,
static void i2c_hid_free_buffers(struct i2c_hid *ihid) static void i2c_hid_free_buffers(struct i2c_hid *ihid)
{ {
kfree(ihid->inbuf); kfree(ihid->inbuf);
kfree(ihid->rawbuf);
kfree(ihid->argsbuf); kfree(ihid->argsbuf);
kfree(ihid->cmdbuf); kfree(ihid->cmdbuf);
ihid->inbuf = NULL; ihid->inbuf = NULL;
ihid->rawbuf = NULL;
ihid->cmdbuf = NULL; ihid->cmdbuf = NULL;
ihid->argsbuf = NULL; ihid->argsbuf = NULL;
ihid->bufsize = 0; ihid->bufsize = 0;
...@@ -522,10 +525,11 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size) ...@@ -522,10 +525,11 @@ static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
report_size; /* report */ report_size; /* report */
ihid->inbuf = kzalloc(report_size, GFP_KERNEL); ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
ihid->argsbuf = kzalloc(args_len, GFP_KERNEL); ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL); ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
if (!ihid->inbuf || !ihid->argsbuf || !ihid->cmdbuf) { if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
i2c_hid_free_buffers(ihid); i2c_hid_free_buffers(ihid);
return -ENOMEM; return -ENOMEM;
} }
...@@ -552,12 +556,12 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, ...@@ -552,12 +556,12 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
ret = i2c_hid_get_report(client, ret = i2c_hid_get_report(client,
report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
report_number, ihid->inbuf, ask_count); report_number, ihid->rawbuf, ask_count);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret_count = ihid->inbuf[0] | (ihid->inbuf[1] << 8); ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
if (ret_count <= 2) if (ret_count <= 2)
return 0; return 0;
...@@ -566,7 +570,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, ...@@ -566,7 +570,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
/* The query buffer contains the size, dropping it in the reply */ /* The query buffer contains the size, dropping it in the reply */
count = min(count, ret_count - 2); count = min(count, ret_count - 2);
memcpy(buf, ihid->inbuf + 2, count); memcpy(buf, ihid->rawbuf + 2, count);
return count; return count;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment