aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2013-09-11 15:56:57 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-01 12:17:46 -0400
commitae5c98a48258dbdb742be23eadf9261b70d642c3 (patch)
tree18c0eed25fb788b1d55e79802c96ba309a8e6762 /drivers/hid
parent791abfbee86161ad63bddc6af88c0391ef7332bd (diff)
HID: validate feature and input report details
commit cc6b54aa54bf40b762cab45a9fc8aa81653146eb upstream. 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 Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Acked-by: Kees Cook <keescook@chromium.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-core.c16
-rw-r--r--drivers/hid/hid-input.c11
2 files changed, 17 insertions, 10 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 85bf1dead80d..ca959cf572a6 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -94,7 +94,6 @@ EXPORT_SYMBOL_GPL(hid_register_report);
94static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) 94static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
95{ 95{
96 struct hid_field *field; 96 struct hid_field *field;
97 int i;
98 97
99 if (report->maxfield == HID_MAX_FIELDS) { 98 if (report->maxfield == HID_MAX_FIELDS) {
100 hid_err(report->device, "too many fields in report\n"); 99 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 field->value = (s32 *)(field->usage + usages); 112 field->value = (s32 *)(field->usage + usages);
114 field->report = report; 113 field->report = report;
115 114
116 for (i = 0; i < usages; i++)
117 field->usage[i].usage_index = i;
118
119 return field; 115 return field;
120} 116}
121 117
@@ -226,9 +222,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
226{ 222{
227 struct hid_report *report; 223 struct hid_report *report;
228 struct hid_field *field; 224 struct hid_field *field;
229 int usages; 225 unsigned usages;
230 unsigned offset; 226 unsigned offset;
231 int i; 227 unsigned i;
232 228
233 report = hid_register_report(parser->device, report_type, parser->global.report_id); 229 report = hid_register_report(parser->device, report_type, parser->global.report_id);
234 if (!report) { 230 if (!report) {
@@ -255,7 +251,8 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
255 if (!parser->local.usage_index) /* Ignore padding fields */ 251 if (!parser->local.usage_index) /* Ignore padding fields */
256 return 0; 252 return 0;
257 253
258 usages = max_t(int, parser->local.usage_index, parser->global.report_count); 254 usages = max_t(unsigned, parser->local.usage_index,
255 parser->global.report_count);
259 256
260 field = hid_register_field(report, usages, parser->global.report_count); 257 field = hid_register_field(report, usages, parser->global.report_count);
261 if (!field) 258 if (!field)
@@ -266,13 +263,14 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
266 field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); 263 field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
267 264
268 for (i = 0; i < usages; i++) { 265 for (i = 0; i < usages; i++) {
269 int j = i; 266 unsigned j = i;
270 /* Duplicate the last usage we parsed if we have excess values */ 267 /* Duplicate the last usage we parsed if we have excess values */
271 if (i >= parser->local.usage_index) 268 if (i >= parser->local.usage_index)
272 j = parser->local.usage_index - 1; 269 j = parser->local.usage_index - 1;
273 field->usage[i].hid = parser->local.usage[j]; 270 field->usage[i].hid = parser->local.usage[j];
274 field->usage[i].collection_index = 271 field->usage[i].collection_index =
275 parser->local.collection_index[j]; 272 parser->local.collection_index[j];
273 field->usage[i].usage_index = i;
276 } 274 }
277 275
278 field->maxusage = usages; 276 field->maxusage = usages;
@@ -1295,7 +1293,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
1295 goto out; 1293 goto out;
1296 } 1294 }
1297 1295
1298 if (hid->claimed != HID_CLAIMED_HIDRAW) { 1296 if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
1299 for (a = 0; a < report->maxfield; a++) 1297 for (a = 0; a < report->maxfield; a++)
1300 hid_input_field(hid, report->field[a], cdata, interrupt); 1298 hid_input_field(hid, report->field[a], cdata, interrupt);
1301 hdrv = hid->driver; 1299 hdrv = hid->driver;
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ac5e93528dfa..012880a2228c 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -485,6 +485,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
485 if (field->flags & HID_MAIN_ITEM_CONSTANT) 485 if (field->flags & HID_MAIN_ITEM_CONSTANT)
486 goto ignore; 486 goto ignore;
487 487
488 /* Ignore if report count is out of bounds. */
489 if (field->report_count < 1)
490 goto ignore;
491
488 /* only LED usages are supported in output fields */ 492 /* only LED usages are supported in output fields */
489 if (field->report_type == HID_OUTPUT_REPORT && 493 if (field->report_type == HID_OUTPUT_REPORT &&
490 (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { 494 (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
@@ -1163,7 +1167,11 @@ static void report_features(struct hid_device *hid)
1163 1167
1164 rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; 1168 rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
1165 list_for_each_entry(rep, &rep_enum->report_list, list) 1169 list_for_each_entry(rep, &rep_enum->report_list, list)
1166 for (i = 0; i < rep->maxfield; i++) 1170 for (i = 0; i < rep->maxfield; i++) {
1171 /* Ignore if report count is out of bounds. */
1172 if (rep->field[i]->report_count < 1)
1173 continue;
1174
1167 for (j = 0; j < rep->field[i]->maxusage; j++) { 1175 for (j = 0; j < rep->field[i]->maxusage; j++) {
1168 /* Verify if Battery Strength feature is available */ 1176 /* Verify if Battery Strength feature is available */
1169 hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); 1177 hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
@@ -1172,6 +1180,7 @@ static void report_features(struct hid_device *hid)
1172 drv->feature_mapping(hid, rep->field[i], 1180 drv->feature_mapping(hid, rep->field[i],
1173 rep->field[i]->usage + j); 1181 rep->field[i]->usage + j);
1174 } 1182 }
1183 }
1175} 1184}
1176 1185
1177static struct hid_input *hidinput_allocate(struct hid_device *hid) 1186static struct hid_input *hidinput_allocate(struct hid_device *hid)