HID: sensor-hub: Fix out-of-bounds write in sensor_hub_get_feature

sensor_hub_get_feature() clamps its return value to the caller's buffer
size, but the copy loop still copies field->report_size / 8 bytes for
each report value. A malicious HID descriptor can advertise a large
feature field size while an IIO caller supplies a small stack buffer,
such as a single s32, causing an out-of-bounds write.

HID core stores parsed report values in __s32 slots and clamps extracted
values to 32 bits. Reject feature fields that require more than one slot
per value, guard the total byte count calculation, and clamp each
per-value copy to the remaining caller buffer.

Fixes: 5459ada2b3 ("HID: sensor-hub: Fix packing of result buffer for feature report")
Cc: stable@kernel.org
Assisted-by: OpenAI:GPT-5.5-Cyber
Signed-off-by: Xingrui Li <baka9@bakabaka9.tech>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
Xingrui Li 2026-08-05 18:57:53 +00:00 committed by Jiri Kosina
parent ce08c5555c
commit c92693f3ed

View File

@ -239,12 +239,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
u32 field_index, int buffer_size, void *buffer)
{
struct hid_report *report;
struct hid_field *field;
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
int report_size;
size_t field_size;
size_t report_size;
size_t copied = 0;
size_t to_copy;
int ret = 0;
u8 *val_ptr;
int buffer_index = 0;
int i;
unsigned int i;
if (!buffer || buffer_size <= 0)
return -EINVAL;
memset(buffer, 0, buffer_size);
@ -258,27 +263,30 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
hid_hw_wait(hsdev->hdev);
field = report->field[field_index];
/* calculate number of bytes required to read this field */
report_size = DIV_ROUND_UP(report->field[field_index]->report_size,
8) *
report->field[field_index]->report_count;
if (!report_size) {
field_size = DIV_ROUND_UP(field->report_size, 8);
/* HID core stores each parsed report value in a __s32 slot. */
if (!field_size || field_size > sizeof(field->value[0])) {
ret = -EINVAL;
goto done_proc;
}
ret = min(report_size, buffer_size);
val_ptr = (u8 *)report->field[field_index]->value;
for (i = 0; i < report->field[field_index]->report_count; ++i) {
if (buffer_index >= ret)
break;
memcpy(&((u8 *)buffer)[buffer_index], val_ptr,
report->field[field_index]->report_size / 8);
val_ptr += sizeof(__s32);
buffer_index += (report->field[field_index]->report_size / 8);
if (field->report_count > SIZE_MAX / field_size) {
ret = -EINVAL;
goto done_proc;
}
report_size = field_size * field->report_count;
report_size = min_t(size_t, report_size, buffer_size);
for (i = 0; i < field->report_count && copied < report_size; ++i) {
to_copy = min(field_size, report_size - copied);
memcpy(&((u8 *)buffer)[copied], &field->value[i], to_copy);
copied += to_copy;
}
ret = copied;
done_proc:
mutex_unlock(&data->mutex);