Commit cc6b54aa authored by Benjamin Tissoires's avatar Benjamin Tissoires Committed by Jiri Kosina

HID: validate feature and input report details

When dealing with usage_index, be sure to properly use unsigned instead of
int to avoid overflows.

When working on report fields, always validate that their report_counts are
in bounds.
Without this, a HID device could report a malicious feature report that
could trick the driver into a heap overflow:

[  634.885003] usb 1-1: New USB device found, idVendor=0596, idProduct=0500
...
[  676.469629] BUG kmalloc-192 (Tainted: G        W   ): Redzone overwritten

CVE-2013-2897

Cc: stable@vger.kernel.org
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Acked-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 0a9cd0a8
...@@ -94,7 +94,6 @@ EXPORT_SYMBOL_GPL(hid_register_report); ...@@ -94,7 +94,6 @@ EXPORT_SYMBOL_GPL(hid_register_report);
static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
{ {
struct hid_field *field; struct hid_field *field;
int i;
if (report->maxfield == HID_MAX_FIELDS) { if (report->maxfield == HID_MAX_FIELDS) {
hid_err(report->device, "too many fields in report\n"); hid_err(report->device, "too many fields in report\n");
...@@ -113,9 +112,6 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned ...@@ -113,9 +112,6 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
field->value = (s32 *)(field->usage + usages); field->value = (s32 *)(field->usage + usages);
field->report = report; field->report = report;
for (i = 0; i < usages; i++)
field->usage[i].usage_index = i;
return field; return field;
} }
...@@ -226,9 +222,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -226,9 +222,9 @@ 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;
int usages; unsigned usages;
unsigned offset; unsigned offset;
int i; unsigned i;
report = hid_register_report(parser->device, report_type, parser->global.report_id); report = hid_register_report(parser->device, report_type, parser->global.report_id);
if (!report) { if (!report) {
...@@ -255,7 +251,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -255,7 +251,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
if (!parser->local.usage_index) /* Ignore padding fields */ if (!parser->local.usage_index) /* Ignore padding fields */
return 0; return 0;
usages = max_t(int, parser->local.usage_index, parser->global.report_count); usages = max_t(unsigned, parser->local.usage_index,
parser->global.report_count);
field = hid_register_field(report, usages, parser->global.report_count); field = hid_register_field(report, usages, parser->global.report_count);
if (!field) if (!field)
...@@ -266,13 +263,14 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -266,13 +263,14 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
for (i = 0; i < usages; i++) { for (i = 0; i < usages; i++) {
int j = i; unsigned j = i;
/* Duplicate the last usage we parsed if we have excess values */ /* Duplicate the last usage we parsed if we have excess values */
if (i >= parser->local.usage_index) if (i >= parser->local.usage_index)
j = parser->local.usage_index - 1; j = parser->local.usage_index - 1;
field->usage[i].hid = parser->local.usage[j]; field->usage[i].hid = parser->local.usage[j];
field->usage[i].collection_index = field->usage[i].collection_index =
parser->local.collection_index[j]; parser->local.collection_index[j];
field->usage[i].usage_index = i;
} }
field->maxusage = usages; field->maxusage = usages;
...@@ -1354,7 +1352,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, ...@@ -1354,7 +1352,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
goto out; goto out;
} }
if (hid->claimed != HID_CLAIMED_HIDRAW) { if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
for (a = 0; a < report->maxfield; a++) for (a = 0; a < report->maxfield; a++)
hid_input_field(hid, report->field[a], cdata, interrupt); hid_input_field(hid, report->field[a], cdata, interrupt);
hdrv = hid->driver; hdrv = hid->driver;
......
...@@ -485,6 +485,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -485,6 +485,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (field->flags & HID_MAIN_ITEM_CONSTANT) if (field->flags & HID_MAIN_ITEM_CONSTANT)
goto ignore; goto ignore;
/* Ignore if report count is out of bounds. */
if (field->report_count < 1)
goto ignore;
/* only LED usages are supported in output fields */ /* only LED usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT && if (field->report_type == HID_OUTPUT_REPORT &&
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
...@@ -1236,7 +1240,11 @@ static void report_features(struct hid_device *hid) ...@@ -1236,7 +1240,11 @@ static void report_features(struct hid_device *hid)
rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
list_for_each_entry(rep, &rep_enum->report_list, list) list_for_each_entry(rep, &rep_enum->report_list, list)
for (i = 0; i < rep->maxfield; i++) for (i = 0; i < rep->maxfield; i++) {
/* Ignore if report count is out of bounds. */
if (rep->field[i]->report_count < 1)
continue;
for (j = 0; j < rep->field[i]->maxusage; j++) { for (j = 0; j < rep->field[i]->maxusage; j++) {
/* Verify if Battery Strength feature is available */ /* Verify if Battery Strength feature is available */
hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
...@@ -1245,6 +1253,7 @@ static void report_features(struct hid_device *hid) ...@@ -1245,6 +1253,7 @@ static void report_features(struct hid_device *hid)
drv->feature_mapping(hid, rep->field[i], drv->feature_mapping(hid, rep->field[i],
rep->field[i]->usage + j); rep->field[i]->usage + j);
} }
}
} }
static struct hid_input *hidinput_allocate(struct hid_device *hid) static struct hid_input *hidinput_allocate(struct hid_device *hid)
......
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