Linux HID arbitrary heap overflow 2013-09-01 / 2013-09-02 Credit: Kees Cook Risk: High Local: No Remote: Yes CVE: CVE-2013-2888 CWE: CWE-119

CVSS Base Score: 6.2/10 Impact Subscore: 10/10 Exploitability Subscore: 1.9/10 Exploit range: Local Attack complexity: High Authentication: No required Confidentiality impact: Complete Integrity impact: Complete Availability impact: Complete I've found several issues in the Linux HID code. They are making their way into the Linux kernel via the linux-input tree now: http://marc.info/?l=linux-input&m=137772180514608&w=10001-HID-validate-HID-report-id-size.patch CVE-2013-2888 Requires CONFIG_HID Memory write via arbitrary heap array index. This is the most serious, IMO, as it allows (on 32-bit) access to the entire memory range (the index is unsigned 32 bit). This is mitigated slightly by the fact that the starting address is at an "unknown" location on the heap, and that the value written is an "arbitrary" kernel pointer. Still, this could almost certainly be turned into full kernel execution given enough study. The "Report ID" field of a HID report is used to build indexes of reports. The kernel's index of these is limited to 256 entries, so any malicious device that sets a Report ID greater than 255 will trigger memory corruption on the host: [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878 [ 1347.156261] IP: [<ffffffff813e4da0>] hid_register_report+0x2a/0x8b CVE-2013-2888 Signed-off-by: Kees Cook <keescook@chromium.org> Cc: stable@kernel.org --- drivers/hid/hid-core.c | 10 +++++++--- include/linux/hid.h | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 36668d1..5ea7d51 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type, struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; + if (id >= HID_MAX_IDS) + return NULL; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; @@ -404,8 +406,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); - if (parser->global.report_id == 0) { - hid_err(parser->device, "report_id 0 is invalid

"); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid

", + parser->global.report_id); return -1; } return 0; @@ -575,7 +579,7 @@ static void hid_close_report(struct hid_device *device) for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; - for (j = 0; j < 256; j++) { + for (j = 0; j < HID_MAX_IDS; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991..ff545cc 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -393,10 +393,12 @@ struct hid_report { struct hid_device *device; /* associated device */ }; +#define HID_MAX_IDS 256 + struct hid_report_enum { unsigned numbered; struct list_head report_list; - struct hid_report *report_id_hash[256]; + struct hid_report *report_id_hash[HID_MAX_IDS]; }; #define HID_REPORT_TYPES 3 Many drivers need to validate the characteristics of their HID report during initialization to avoid misusing the reports. This adds a common helper to perform validation of the report, its field count, and the value count within the fields. Signed-off-by: Kees Cook <keescook@chromium.org> Cc: stable@kernel.org --- drivers/hid/hid-core.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 4 ++++ 2 files changed, 54 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5ea7d51..55798b2 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -759,6 +759,56 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) } EXPORT_SYMBOL_GPL(hid_parse_report); +static const char * const hid_report_names[] = { + "HID_INPUT_REPORT", + "HID_OUTPUT_REPORT", + "HID_FEATURE_REPORT", +}; +/** + * hid_validate_report - validate existing device report + * + * @device: hid device + * @type: which report type to examine + * @id: which report ID to examine (0 for first) + * @fields: expected number of fields + * @report_counts: expected number of values per field + * + * Validate the report details after parsing. + */ +struct hid_report *hid_validate_report(struct hid_device *hid, + unsigned int type, unsigned int id, + unsigned int fields, + unsigned int report_counts) +{ + struct hid_report *report; + unsigned int i; + + if (type > HID_FEATURE_REPORT) { + hid_err(hid, "invalid HID report %u

", type); + return NULL; + } + + report = hid->report_enum[type].report_id_hash[id]; + if (!report) { + hid_err(hid, "missing %s %u

", hid_report_names[type], id); + return NULL; + } + if (report->maxfield < fields) { + hid_err(hid, "not enough fields in %s %u

", + hid_report_names[type], id); + return NULL; + } + for (i = 0; i < fields; i++) { + if (report->field[i]->report_count < report_counts) { + hid_err(hid, "not enough values in %s %u fields

", + hid_report_names[type], id); + return NULL; + } + } + return report; +} +EXPORT_SYMBOL_GPL(hid_validate_report); + /** * hid_open_report - open a driver-specific device report * diff --git a/include/linux/hid.h b/include/linux/hid.h index ff545cc..76e41d8 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -749,6 +749,10 @@ void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); +struct hid_report *hid_validate_report(struct hid_device *hid, + unsigned int type, unsigned int id, + unsigned int fields, + unsigned int report_counts); int hid_open_report(struct hid_device *device); int hid_check_keys_pressed(struct hid_device *hid); int hid_connect(struct hid_device *hid, unsigned int connect_mask); References: http://marc.info/?l=linux-input&m=137772180514608&w=10001-HID-validate-HID-report-id-size.patch http://marc.info/?l=linux-input&m=137772181214612&w=10002-HID-provide-a-helper-for-validating-hid-reports.patch http://cxsecurity.com/issue/WLB-2013090003 http://cxsecurity.com/issue/WLB-2013090005 http://cxsecurity.com/issue/WLB-2013090006 http://cxsecurity.com/issue/WLB-2013090007 http://cxsecurity.com/issue/WLB-2013090008 http://cxsecurity.com/issue/WLB-2013090009 http://cxsecurity.com/issue/WLB-2013090010 http://cxsecurity.com/issue/WLB-2013090011 http://cxsecurity.com/issue/WLB-2013090012 http://cxsecurity.com/issue/WLB-2013090013 http://cxsecurity.com/issue/WLB-2013090015



See this note in RAW Version Tweet Vote for this issue: 0 0 50% 50%





Thanks for you vote!



Thanks for you comment!

Your message is in quarantine 48 hours. Comment it here. Nick (*) Email (*) Video Text (*)



(*) - required fields. Cancel Submit {{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1 {{ x.comment }} {{ x.ux * 1000 | date:'yyyy-MM-dd' }}CET+1 Show all comments Copyright 2020, cxsecurity.com