diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-17 21:54:05 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-17 21:54:05 -0400 |
| commit | 84fca9f38c5d646e95cdeef70e41cf15db549b95 (patch) | |
| tree | c904715c9b70a2f0716c84931e2d58b1a226a1e0 | |
| parent | 03e1261778cca782d41a3d8e3945ca88cf93e01e (diff) | |
| parent | 0ccdd9e7476680c16113131264ad6597bd10299d (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina:
"Fixes for CVE-2013-2897, CVE-2013-2895, CVE-2013-2897, CVE-2013-2894,
CVE-2013-2893, CVE-2013-2891, CVE-2013-2890, CVE-2013-2889.
All the bugs are triggerable only by specially crafted evil-on-purpose
HW devices. Fixes by Kees Cook and Benjamin Tissoires"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
HID: lenovo-tpkbd: fix leak if tpkbd_probe_tp fails
HID: multitouch: validate indexes details
HID: logitech-dj: validate output report details
HID: validate feature and input report details
HID: lenovo-tpkbd: validate output report details
HID: LG: validate HID output report details
HID: steelseries: validate output report details
HID: sony: validate HID output report details
HID: zeroplus: validate output report details
HID: provide a helper for validating hid reports
| -rw-r--r-- | drivers/hid/hid-core.c | 74 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 11 | ||||
| -rw-r--r-- | drivers/hid/hid-lenovo-tpkbd.c | 25 | ||||
| -rw-r--r-- | drivers/hid/hid-lg2ff.c | 19 | ||||
| -rw-r--r-- | drivers/hid/hid-lg3ff.c | 29 | ||||
| -rw-r--r-- | drivers/hid/hid-lg4ff.c | 20 | ||||
| -rw-r--r-- | drivers/hid/hid-lgff.c | 17 | ||||
| -rw-r--r-- | drivers/hid/hid-logitech-dj.c | 10 | ||||
| -rw-r--r-- | drivers/hid/hid-multitouch.c | 26 | ||||
| -rw-r--r-- | drivers/hid/hid-sony.c | 4 | ||||
| -rw-r--r-- | drivers/hid/hid-steelseries.c | 5 | ||||
| -rw-r--r-- | drivers/hid/hid-zpff.c | 18 | ||||
| -rw-r--r-- | include/linux/hid.h | 4 |
13 files changed, 146 insertions, 116 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ae88a97f976e..b8470b1a10fe 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; |
| @@ -801,6 +799,64 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) | |||
| 801 | } | 799 | } |
| 802 | EXPORT_SYMBOL_GPL(hid_parse_report); | 800 | EXPORT_SYMBOL_GPL(hid_parse_report); |
| 803 | 801 | ||
| 802 | static const char * const hid_report_names[] = { | ||
| 803 | "HID_INPUT_REPORT", | ||
| 804 | "HID_OUTPUT_REPORT", | ||
| 805 | "HID_FEATURE_REPORT", | ||
| 806 | }; | ||
| 807 | /** | ||
| 808 | * hid_validate_values - validate existing device report's value indexes | ||
| 809 | * | ||
| 810 | * @device: hid device | ||
| 811 | * @type: which report type to examine | ||
| 812 | * @id: which report ID to examine (0 for first) | ||
| 813 | * @field_index: which report field to examine | ||
| 814 | * @report_counts: expected number of values | ||
| 815 | * | ||
| 816 | * Validate the number of values in a given field of a given report, after | ||
| 817 | * parsing. | ||
| 818 | */ | ||
| 819 | struct hid_report *hid_validate_values(struct hid_device *hid, | ||
| 820 | unsigned int type, unsigned int id, | ||
| 821 | unsigned int field_index, | ||
| 822 | unsigned int report_counts) | ||
| 823 | { | ||
| 824 | struct hid_report *report; | ||
| 825 | |||
| 826 | if (type > HID_FEATURE_REPORT) { | ||
| 827 | hid_err(hid, "invalid HID report type %u\n", type); | ||
| 828 | return NULL; | ||
| 829 | } | ||
| 830 | |||
| 831 | if (id >= HID_MAX_IDS) { | ||
| 832 | hid_err(hid, "invalid HID report id %u\n", id); | ||
| 833 | return NULL; | ||
| 834 | } | ||
| 835 | |||
| 836 | /* | ||
| 837 | * Explicitly not using hid_get_report() here since it depends on | ||
| 838 | * ->numbered being checked, which may not always be the case when | ||
| 839 | * drivers go to access report values. | ||
| 840 | */ | ||
| 841 | report = hid->report_enum[type].report_id_hash[id]; | ||
| 842 | if (!report) { | ||
| 843 | hid_err(hid, "missing %s %u\n", hid_report_names[type], id); | ||
| 844 | return NULL; | ||
| 845 | } | ||
| 846 | if (report->maxfield <= field_index) { | ||
| 847 | hid_err(hid, "not enough fields in %s %u\n", | ||
| 848 | hid_report_names[type], id); | ||
| 849 | return NULL; | ||
| 850 | } | ||
| 851 | if (report->field[field_index]->report_count < report_counts) { | ||
| 852 | hid_err(hid, "not enough values in %s %u field %u\n", | ||
| 853 | hid_report_names[type], id, field_index); | ||
| 854 | return NULL; | ||
| 855 | } | ||
| 856 | return report; | ||
| 857 | } | ||
| 858 | EXPORT_SYMBOL_GPL(hid_validate_values); | ||
| 859 | |||
| 804 | /** | 860 | /** |
| 805 | * hid_open_report - open a driver-specific device report | 861 | * hid_open_report - open a driver-specific device report |
| 806 | * | 862 | * |
| @@ -1296,7 +1352,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, | |||
| 1296 | goto out; | 1352 | goto out; |
| 1297 | } | 1353 | } |
| 1298 | 1354 | ||
| 1299 | if (hid->claimed != HID_CLAIMED_HIDRAW) { | 1355 | if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { |
| 1300 | for (a = 0; a < report->maxfield; a++) | 1356 | for (a = 0; a < report->maxfield; a++) |
| 1301 | hid_input_field(hid, report->field[a], cdata, interrupt); | 1357 | hid_input_field(hid, report->field[a], cdata, interrupt); |
| 1302 | hdrv = hid->driver; | 1358 | hdrv = hid->driver; |
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index b420f4a0fd28..8741d953dcc8 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) { |
| @@ -1236,7 +1240,11 @@ static void report_features(struct hid_device *hid) | |||
| 1236 | 1240 | ||
| 1237 | rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; | 1241 | rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; |
| 1238 | list_for_each_entry(rep, &rep_enum->report_list, list) | 1242 | list_for_each_entry(rep, &rep_enum->report_list, list) |
| 1239 | for (i = 0; i < rep->maxfield; i++) | 1243 | for (i = 0; i < rep->maxfield; i++) { |
| 1244 | /* Ignore if report count is out of bounds. */ | ||
| 1245 | if (rep->field[i]->report_count < 1) | ||
| 1246 | continue; | ||
| 1247 | |||
| 1240 | for (j = 0; j < rep->field[i]->maxusage; j++) { | 1248 | for (j = 0; j < rep->field[i]->maxusage; j++) { |
| 1241 | /* Verify if Battery Strength feature is available */ | 1249 | /* Verify if Battery Strength feature is available */ |
| 1242 | hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); | 1250 | hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); |
| @@ -1245,6 +1253,7 @@ static void report_features(struct hid_device *hid) | |||
| 1245 | drv->feature_mapping(hid, rep->field[i], | 1253 | drv->feature_mapping(hid, rep->field[i], |
| 1246 | rep-& | ||
