ANDROID: HID; Over-ride default maximum buffer size when using UHID

Presently, when a report is processed, its proposed size, provided by
the user of the API (as Report Size * Report Count) is compared against
the subsystem default HID_MAX_BUFFER_SIZE (16k).  However, some
low-level HID drivers allocate a reduced amount of memory to their
buffers (e.g. UHID only allocates UHID_DATA_MAX (4k) buffers), rending
this check inadequate in some cases.

In these circumstances, if the received report ends up being smaller
than the proposed report size, the remainder of the buffer is zeroed.
That is, the space between sizeof(csize) (size of the current report)
and the rsize (size proposed i.e. Report Size * Report Count), which can
be handled up to HID_MAX_BUFFER_SIZE (16k).  Meaning that memset()
shoots straight past the end of the buffer boundary and starts zeroing
out in-use values, often resulting in calamity.

This is an Android specific patch which essentially achieves the same
goal as the recently reverted upstream commits b1a37ed00d790 "(HID:
core: Provide new max_buffer_size attribute to over-ride the default")
and 1c5d4221240a2 ("HID: uhid: Over-ride the default maximum data buffer
value with our own") only it does so in an ABI friendly (albeit more
hacky) way.

Bug: 260007429
Signed-off-by: Lee Jones <joneslee@google.com>
Change-Id: I1f56673bb67b63ab14b58634bfe74a04b0758e3d
(cherry picked from commit 71761b36c37ae15a09fdd4d4adcc98bb939c426c)
This commit is contained in:
Lee Jones 2023-06-13 14:45:31 +01:00
parent 8047bf5f22
commit e699d543bb

View File

@ -32,6 +32,7 @@
#include <linux/hiddev.h> #include <linux/hiddev.h>
#include <linux/hid-debug.h> #include <linux/hid-debug.h>
#include <linux/hidraw.h> #include <linux/hidraw.h>
#include <linux/uhid.h>
#include "hid-ids.h" #include "hid-ids.h"
@ -258,6 +259,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
{ {
struct hid_report *report; struct hid_report *report;
struct hid_field *field; struct hid_field *field;
unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE;
unsigned int usages; unsigned int usages;
unsigned int offset; unsigned int offset;
unsigned int i; unsigned int i;
@ -288,8 +290,11 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
offset = report->size; offset = report->size;
report->size += parser->global.report_size * parser->global.report_count; report->size += parser->global.report_size * parser->global.report_count;
if (parser->device->ll_driver == &uhid_hid_driver)
max_buffer_size = UHID_DATA_MAX;
/* Total size check: Allow for possible report index byte */ /* Total size check: Allow for possible report index byte */
if (report->size > (HID_MAX_BUFFER_SIZE - 1) << 3) { if (report->size > (max_buffer_size - 1) << 3) {
hid_err(parser->device, "report is too long\n"); hid_err(parser->device, "report is too long\n");
return -1; return -1;
} }
@ -1745,6 +1750,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_report *report; struct hid_report *report;
struct hid_driver *hdrv; struct hid_driver *hdrv;
int max_buffer_size = HID_MAX_BUFFER_SIZE;
unsigned int a; unsigned int a;
u32 rsize, csize = size; u32 rsize, csize = size;
u8 *cdata = data; u8 *cdata = data;
@ -1761,10 +1767,13 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
rsize = hid_compute_report_size(report); rsize = hid_compute_report_size(report);
if (report_enum->numbered && rsize >= HID_MAX_BUFFER_SIZE) if (hid->ll_driver == &uhid_hid_driver)
rsize = HID_MAX_BUFFER_SIZE - 1; max_buffer_size = UHID_DATA_MAX;
else if (rsize > HID_MAX_BUFFER_SIZE)
rsize = HID_MAX_BUFFER_SIZE; if (report_enum->numbered && rsize >= max_buffer_size)
rsize = max_buffer_size - 1;
else if (rsize > max_buffer_size)
rsize = max_buffer_size;
if (csize < rsize) { if (csize < rsize) {
dbg_hid("report %d is too short, (%d < %d)\n", report->id, dbg_hid("report %d is too short, (%d < %d)\n", report->id,