diff options
author | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2013-09-11 15:56:57 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-01 12:17:46 -0400 |
commit | ae5c98a48258dbdb742be23eadf9261b70d642c3 (patch) | |
tree | 18c0eed25fb788b1d55e79802c96ba309a8e6762 /drivers/hid | |
parent | 791abfbee86161ad63bddc6af88c0391ef7332bd (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.c | 16 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 11 |
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); | |||
94 | static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) | 94 | static 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 | ||
1177 | static struct hid_input *hidinput_allocate(struct hid_device *hid) | 1186 | static struct hid_input *hidinput_allocate(struct hid_device *hid) |